Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pybind11 example code #91

Open
district10 opened this issue Oct 15, 2020 · 0 comments
Open

Pybind11 example code #91

district10 opened this issue Oct 15, 2020 · 0 comments

Comments

@district10
Copy link

I struggled binding better enum to python via pybind11.

Here's a solution:

C++ Macro Helpers

#define BIND_BETTER_ENUM_VALUE(MyEnum, Value)                              \
    .def_property_readonly_static(#Value,                                  \
                                  [](py::object) { return +MyEnum::Value; })
#define BIND_BETTER_ENUM(MyEnum)                                           \
py::class_<MyEnum>(m, #MyEnum)                                             \
    .def(py::self == py::self)                                             \
    .def(py::self != py::self)                                             \
    .def("__hash__",                                                       \
         [](const MyEnum &self) { return self._to_integral(); })           \
    .def_static("to_string",                                               \
                [](const MyEnum &self) { return (+self)._to_string(); })   \
    .def("__str__",                                                        \
         [](const MyEnum &self) { return (+self)._to_string(); })          \
    .def_property_readonly(                                                \
        "name", [](const MyEnum &self) { return +self._to_string(); })     \
    .def_static("from_string",                                             \
                [](const std::string &s) {                                 \
                    return MyEnum::_from_string(s.c_str());                \
                })                                                         \
    .def_static("from_string_nocase",                                      \
                [](const std::string &s) {                                 \
                    return MyEnum::_from_string_nocase(s.c_str());         \
                })                                                         \
    .def_static("from_string_nothrow",                                     \
                [](const std::string &s) -> tl::optional<MyEnum> {         \
                    auto e = MyEnum::_from_string_nothrow(s.c_str());      \
                    return e ? tl::optional<MyEnum>(*e)                    \
                             : tl::optional<MyEnum>{};                     \
                })                                                         \
    .def_static(                                                           \
        "from_string_nocase_nothrow",                                      \
        [](const std::string &s) -> tl::optional<MyEnum> {                 \
            auto e = MyEnum::_from_string_nocase_nothrow(s.c_str());       \
            return e ? tl::optional<MyEnum>(*e) : tl::optional<MyEnum>{};  \
        })                                                                 \
    .def("to_integral",                                                    \
         [](const MyEnum &self) { return self._to_integral(); })           \
    .def_static("from_integral",                                           \
                [](int i) { return MyEnum::_from_integral(i); })           \
    .def_static("from_integral_nothrow",                                   \
                [](int i) {                                                \
                    auto e = MyEnum::_from_integral_nothrow(i);            \
                    return e ? tl::optional<MyEnum>(*e)                    \
                             : tl::optional<MyEnum>{};                     \
                })                                                         \
    .def_static("from_integral_unchecked",                                 \
                [](int i) { return MyEnum::_from_integral_unchecked(i); }) \
    .def_static("is_valid", [](int i) { return MyEnum::_is_valid(i); })    \
    .def_static(                                                           \
        "is_valid",                                                        \
        [](const std::string &s) { return MyEnum::_is_valid(s.c_str()); }) \
    .def_static("is_valid_no_case",                                        \
                [](const std::string &s) {                                 \
                    return MyEnum::_is_valid_nocase(s.c_str());            \
                })                                                         \
    .def_static("size", []() { return MyEnum::_size(); })                  \
    .def_static("keys",                                                    \
                []() {                                                     \
                    std::vector<std::string> ret;                          \
                    ret.reserve(MyEnum::_size());                          \
                    for (auto &name : MyEnum::_names()) {                  \
                        ret.push_back(name);                               \
                    }                                                      \
                    return ret;                                            \
                })                                                         \
    .def_static("values", []() {                                           \
        std::vector<MyEnum> ret;                                           \
        ret.reserve(MyEnum::_size());                                      \
        for (auto &value : MyEnum::_values()) {                            \
            ret.push_back(value);                                          \
        }                                                                  \
        return ret;                                                        \
    })


// need https://github.com/TartanLlama/optional to bind *_nothrow functions in better enums
// you can remove these if not needed
namespace pybind11
{
namespace detail
{
template <typename T>
struct type_caster<tl::optional<T>> : optional_caster<tl::optional<T>>
{
};
}
}

BETTER_ENUM(MyBetterEnum, int, Red, Green, Blue)
BIND_BETTER_ENUM(MyBetterEnum)
BIND_BETTER_ENUM_VALUE(MyBetterEnum, Red)
BIND_BETTER_ENUM_VALUE(MyBetterEnum, Green)
BIND_BETTER_ENUM_VALUE(MyBetterEnum, Blue)
//
;

Test Binded Python Code

// testing in python
import unittest
self = unittest.TestCase() # hack here for testing

red = m.MyBetterEnum.Red
self.assertEqual(red.name, 'Red')
self.assertEqual(m.MyBetterEnum.Red, m.MyBetterEnum.Red)
self.assertNotEqual(m.MyBetterEnum.Red, m.MyBetterEnum.Green)
self.assertListEqual(m.MyBetterEnum.keys(), ['Red', 'Green', 'Blue'])
self.assertListEqual(m.MyBetterEnum.values(), [
    m.MyBetterEnum.Red,
    m.MyBetterEnum.Green,
    m.MyBetterEnum.Blue,
])
self.assertEqual(m.MyBetterEnum.from_string('Red'), m.MyBetterEnum.Red)
self.assertEqual(m.MyBetterEnum.from_string_nocase('red'), m.MyBetterEnum.Red)
self.assertEqual(m.MyBetterEnum.from_string_nocase_nothrow('null'), None)
self.assertEqual(1, len(set([m.MyBetterEnum.Red, m.MyBetterEnum.Red])))
self.assertEqual(2, len(set([m.MyBetterEnum.Red, m.MyBetterEnum.Green])))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant