Skip to content

Commit

Permalink
Update masks directly with wrappers, tweak docs re: enum types
Browse files Browse the repository at this point in the history
Partially addresses the reviews Left to do is the Py->C++ conversion in
the flag case within enum_from_python() overload with PyObject *.
  • Loading branch information
nicholasjng committed Aug 29, 2024
1 parent 77f7dfc commit 11a771e
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 14 deletions.
8 changes: 6 additions & 2 deletions docs/api_core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1534,7 +1534,7 @@ Casting

Convert the C++ object ``value`` into a Python object. The return value
policy `policy` is used to handle ownership-related questions when a new
Python object must be created. A valid `parent` object is required when
Python object must be created. A valid `parent` object is required when
specifying a `reference_internal` return value policy.

The function raises a :cpp:type:`cast_error` when the conversion fails.
Expand Down Expand Up @@ -1970,14 +1970,18 @@ The following annotations can be specified using the variable-length
``Shape(2) * 1.5 == 3.0``.) It is unspecified whether operations on
mixed enum types (such as ``Shape.Circle + Color.Red``) are
permissible.
Changes the resulting Python enumeration type to be based on `enum.IntEnum`.

.. cpp:struct:: flag_enum

Indicate that the enumeration may be used with bitwise
operations. This enables the bitwise operators ``| & ^ ~``
with two enumerators as operands.
The result will have the same enumeration type as the operands.
So ``Shape(2) | Shape(1)`` is equivalent to ``Shape(3)`
So ``Shape(2) | Shape(1)`` is equivalent to ``Shape(3)`.
Changes the resulting Python enumeration type to be based on either
`enum.IntFlag` or `enum.Flag`, depending on whether or not the enumeration
is marked to support arithmetic operations.
Function binding
----------------
Expand Down
12 changes: 7 additions & 5 deletions docs/classes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -296,12 +296,14 @@ C++11-style strongly typed enumerations.
When the annotation :cpp:class:`nb::is_arithmetic() <is_arithmetic>` is
passed to :cpp:class:`nb::enum_\<T\> <enum_>`, the resulting Python type
will support arithmetic and bit-level operations (and, or,
xor, negation). The operands of these operations may be either enumerators
xor, negation). The operands of these operations may be either enumerators.
When the annotation :cpp:class:`nb::flag_enum() <flag_enum>` is passed to
:cpp:class:`nb::enum_\<T\> <enum_>`, the resulting Python type will be an
`enum.IntFlag`, meaning its enumerators can be combined using bitwise operators
in a type-safe way: the result will have the same enumeration type as the operands,
and only enumerators of the same type can be combined.
:cpp:class:`nb::enum_\<T\> <enum_>`, the resulting Python type will be a class
derived from ``enum.Flag``, meaning its enumerators can be combined using bitwise
operators in a type-safe way: the result will have the same enumeration type
as the operands, and only enumerators of the same type can be combined.
When passing both ``is_arithmetic`` and ``flag_enum``, the resulting Python type
will be ``enum.IntFlag``, supporting both arithmetic and bitwise operations.

.. code-block:: cpp
Expand Down
12 changes: 5 additions & 7 deletions src/nb_enum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,16 +123,14 @@ void enum_append(PyObject *tp_, const char *name_, int64_t value_,
// In Python 3.11+, update the flag and bit masks by hand,
// since enum._proto_member.__set_name__ is not called in this code path.
if (t->flags & (uint32_t) enum_flags::flag_enum) {
int64_t flag_mask = cast<int64_t>(tp.attr("_flag_mask_"));
tp.attr("_flag_mask_") = int_(flag_mask | value_);
setattr(tp, "_flag_mask_", tp.attr("_flag_mask_") | val);

bool is_single_bit = (value_ != 0) && (value_ & (value_ - 1)) == 0;
if (is_single_bit) {
int64_t singles_mask = cast<int64_t>(tp.attr("_singles_mask_"));
tp.attr("_singles_mask_") = int_(singles_mask | value_);
setattr(tp, "_singles_mask_", tp.attr("_singles_mask_") | val);
}
int64_t bit_length = cast<int64_t>(tp.attr("_flag_mask_").attr("bit_length")());
tp.attr("_all_bits_") = int_((2 << bit_length) - 1);
int_ bit_length = int_(tp.attr("_flag_mask_").attr("bit_length")());
setattr(tp, "_all_bits_", (int_(2) << bit_length) - int_(1));
}
#endif

Expand Down Expand Up @@ -174,7 +172,7 @@ bool enum_from_python(const std::type_info *tp, PyObject *o, int64_t *out, uint8
return false;

if ((t->flags & (uint32_t) enum_flags::flag_enum) != 0 && Py_TYPE(o) == t->type_py) {
auto pValue = PyObject_GetAttrString(o, "value");
PyObject *pValue = PyObject_GetAttrString(o, "value");
if (pValue == nullptr) {
PyErr_Clear();
return false;
Expand Down

0 comments on commit 11a771e

Please sign in to comment.