简体   繁体   中英

pybind11 emulate python Enum behaviour

I'm using pybind11 to provide a Python interface to my C++ library. My library contains enums for which I've provided convenience functions to allow iterating through the enum values and conversion to string; as an example

template <typename Enum>
struct iter_enum
{
    struct iterator
    {
        using value_type = Enum;
        using difference_type = ptrdiff_t;
        using reference = const Enum&;
        using pointer = const Enum*;
        using iterator_category = std::input_iterator_tag;

        iterator(Enum value) : cur(value) {}

        reference operator * () { return cur; }
        pointer operator -> () { return &cur; }
        bool operator == (const iterator& other) { return cur == other.cur; }
        bool operator != (const iterator&other) { return!(*this == other); }
        iterator& operator ++ () { if (cur != Enum::Unknown) cur = static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(cur) + 1); return *this; }
        iterator operator ++ (int) { iterator other = *this; ++(*this); return other; }
    private:
        Enum cur;
    };

    iterator begin()
    {
        return iterator(static_cast<Enum>(0));
    }

    iterator end()
    {
        return iterator(Enum::Unknown);
    }
};

enum class Colour : char
{
    Black = 0,
    Blue,
    Red,
    Yellow,
    Unknown
};

const char* to_string(Colour colour)
{
    switch (colour) {
    case Colour::Black: return "Black";
    case Colour::Blue: return "Blue";
    case Colour::Red: return "Red";
    case Colour::Yellow: return "Yellow";
    default: return "Unknown";
    }
}

int main()
{
    for (auto colour : iter_enum<Colour>())
        std::cout << to_string(colour) << '\n';
}

When porting such code using pybind11 I am currently defining the iterator as a static member function,

 pybind11::enum_<Colour>(m, "Colour")
    .value("Black", Colour::Black)
    // add other values and __str__
    .def_static("iter", []() { 
        iter_enum<Colour> i; 
        return pybind11::make_iterator(i.begin(), i.end(); 
    });

however I would like to be able to on the Python side write something along the lines of

for c in Colour:
    print("Colour")

as I would for an enum defined using Python's enum.Enum . I know I could implement this in Python using a metaclass, but I can't work out from the pybind11 documentation I could find on metaclasses here and the example on github (line 334) how to write such a metaclass on the C++ end. From what I can gleam it looks like the metaclass must be exposed using the C API PyObject type.

Any information with regards to whether I'm on the right track, and if so how to write such a metaclass which uses my iter_enum class, would be very appreciated.

You might be able to iterate over a C++ enum exposed by pybind11 with something like the below.

for name, enum_value in Color.__members__.items():
    print(name, enum_value)

The way I figured out this approach was possible was by running dir(EnumClass) and seeing all the private attributes available, of which __members__ was one.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM