Skip to content

Commit 9a25aed

Browse files
riclarssonwjakob
authored andcommitted
Permit nb::implicitly_convertible involving opaque enums (#1107)
1 parent d20944f commit 9a25aed

File tree

4 files changed

+40
-1
lines changed

4 files changed

+40
-1
lines changed

include/nanobind/nb_class.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,10 @@ template <typename T> class enum_ : public object {
838838
template <typename Source, typename Target> void implicitly_convertible() {
839839
if constexpr (!std::is_same_v<Source, Target>) {
840840
using Caster = detail::make_caster<Source>;
841-
static_assert(!std::is_enum_v<Target>, "implicitly_convertible(): 'Target' cannot be an enumeration.");
841+
static_assert(
842+
!std::is_enum_v<Target> || !detail::is_base_caster_v<Target>,
843+
"implicitly_convertible(): 'Target' cannot be an enumeration "
844+
"unless it is opaque.");
842845

843846
if constexpr (detail::is_base_caster_v<Caster>) {
844847
detail::implicitly_convertible(&typeid(Source), &typeid(Target));

tests/test_enum.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#include <nanobind/nanobind.h>
2+
#include <nanobind/operators.h>
3+
#include <nanobind/stl/string.h>
24

35
namespace nb = nanobind;
46

@@ -14,6 +16,9 @@ enum ClassicEnum { Item1, Item2 };
1416

1517
struct EnumProperty { Enum get_enum() { return Enum::A; } };
1618

19+
enum class OpaqueEnum { X, Y };
20+
NB_MAKE_OPAQUE(OpaqueEnum)
21+
1722
NB_MODULE(test_enum_ext, m) {
1823
nb::enum_<Enum>(m, "Enum", "enum-level docstring")
1924
.value("A", Enum::A, "Value A")
@@ -67,4 +72,17 @@ NB_MODULE(test_enum_ext, m) {
6772
nb::class_<EnumProperty>(m, "EnumProperty")
6873
.def(nb::init<>())
6974
.def_prop_ro("read_enum", &EnumProperty::get_enum);
75+
76+
77+
auto oe = nb::class_<OpaqueEnum>(m, "OpaqueEnum")
78+
.def_prop_ro_static("X", [](nb::object&){return OpaqueEnum::X;})
79+
.def_prop_ro_static("Y", [](nb::object&){return OpaqueEnum::Y;})
80+
.def(nb::init<>())
81+
.def("__init__", [](OpaqueEnum* p, std::string v){
82+
if (v == "X") new (p) OpaqueEnum{OpaqueEnum::X};
83+
else if (v == "Y") new (p) OpaqueEnum{OpaqueEnum::Y};
84+
else throw std::runtime_error(v);
85+
})
86+
.def(nb::self == nb::self);
87+
nb::implicitly_convertible<std::string, OpaqueEnum>();
7088
}

tests/test_enum.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,6 @@ def test09_enum_methods():
184184
assert t.Item1.get_value() == 0 and t.Item2.get_value() == 1
185185
assert t.Item1.foo() == t.Item1
186186
assert t.ClassicEnum.bar(t.Item1) == t.Item1
187+
188+
def test10_enum_opaque():
189+
assert t.OpaqueEnum.X == t.OpaqueEnum("X") and t.OpaqueEnum.Y == t.OpaqueEnum("Y")

tests/test_enum_ext.pyi.ref

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,18 @@ class EnumProperty:
105105

106106
@property
107107
def read_enum(self) -> Enum: ...
108+
109+
class OpaqueEnum:
110+
@overload
111+
def __init__(self) -> None: ...
112+
113+
@overload
114+
def __init__(self, arg: str, /) -> None: ...
115+
116+
X: test_enum_ext.OpaqueEnum = ...
117+
"""(arg: object, /) -> test_enum_ext.OpaqueEnum"""
118+
119+
Y: test_enum_ext.OpaqueEnum = ...
120+
"""(arg: object, /) -> test_enum_ext.OpaqueEnum"""
121+
122+
def __eq__(self, arg: OpaqueEnum, /) -> bool: ...

0 commit comments

Comments
 (0)