Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions docs/classes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -443,3 +443,46 @@ The entries defined by the enumeration type are exposed in the ``__members__`` p
...

By default, these are omitted to conserve space.

Python 3 enumerations
=====================

Python 3 provides support for rich enumeration types in ``enum`` module [#enum]_ of the
standard library; it can be also used in Python 2 by installing ``enum34`` backport package.

In order to expose a C++ enumeration type as a subclass of ``enum.IntEnum`` in Python,
one can use the :class:`py3_enum` wrapper whose interface is similar to that of :class:`enum_`:

.. code-block:: cpp

enum class Captain {
Kirk = 0,
Picard
};

The binding code will look like this:

.. code-block:: cpp

py::py3_enum<Captain>(m, "Captain")
.value("Kirk", Captain::Kirk)
.value("Picard", Captain::Picard);

The corresponding Python type is a subclass of ``enum.IntEnum`` and, as a result, is also
a subclass of ``int``:

.. code-block:: pycon

>>> Captain.mro()
[<enum 'Captain'>, <enum 'IntEnum'>, int, <enum 'Enum'>, object]
>>> list(Captain)
[<Captain.Kirk: 0>, <Captain.Picard: 1>]
>>> int(Captain.Picard)
1
>>> Captain.Kirk.name, Captain.Kirk.value
('Kirk', 0)

For more details on ``IntEnum`` functionality, see the official docs [#intenum]_.

.. [#enum] https://docs.python.org/3/library/enum.html
.. [#intenum] https://docs.python.org/3/library/enum.html#intenum
66 changes: 66 additions & 0 deletions include/pybind11/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,72 @@ template <typename... Tuple> class type_caster<std::tuple<Tuple...>> {
std::tuple<make_caster<Tuple>...> value;
};

template<typename U>
struct py3_enum_info {
handle type = {};
std::unordered_map<U, handle> values = {};

py3_enum_info() = default;

py3_enum_info(handle type, const dict& values) : type(type) {
for (auto item : values) {
this->values[item.second.cast<U>()] = type.attr(item.first);
}
}
};

template<typename T>
struct type_caster<T, enable_if_t<std::is_enum<T>::value>> {
using underlying_type = typename std::underlying_type<T>::type;

private:
using base_caster = type_caster_base<T>;

static std::unique_ptr<py3_enum_info<underlying_type>>& py3_info() {
static std::unique_ptr<py3_enum_info<underlying_type>> info;
return info;
}

base_caster caster;
T value;

public:
template<typename U> using cast_op_type = pybind11::detail::cast_op_type<U>;

operator T*() { return py3_info() ? &value : static_cast<T*>(caster); }
operator T&() { return py3_info() ? value : static_cast<T&>(caster); }

static handle cast(const T& src, return_value_policy rvp, handle parent) {
if (py3_info()) {
auto it = py3_info()->values.find(static_cast<underlying_type>(src));
if (it == py3_info()->values.end())
return {};
return it->second.inc_ref();
}
return base_caster::cast(src, rvp, parent);
}

bool load(handle src, bool convert) {
if (!src)
return false;
if (py3_info()) {
if (!isinstance(src, py3_info()->type))
return false;
value = static_cast<T>(src.cast<underlying_type>());
return true;
}
return caster.load(src, convert);
}

static PYBIND11_DESCR name() {
return base_caster::name();
}

static void bind(handle type, const dict& values) {
py3_info().reset(new py3_enum_info<underlying_type>(type, values));
}
};

/// Helper class which abstracts away certain actions. Users can provide specializations for
/// custom holders, but it's only necessary if the type has a non-standard interface.
template <typename T>
Expand Down
Loading