-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
WIP: Python 3 enums #781
WIP: Python 3 enums #781
Conversation
Another point, implementation-wise, I guess it could be possible to avoid the unordered map of registered py3 enums ( |
Thoughts on having this replace the current It would break some potential backwards compatibility in that you can't use I definitely like the idea of storing a the values in a static variable in the |
It's caused by the |
I just had a thought about recreating the object each time you call template <typename T>
class py3_enum {
...
class value_loader {
private:
py3_enum &owner;
friend class py3_enum;
value_loader(py3_enum &e) : owner{e} {}
public:
value_loader &value(const char *name, T value) {
owner.entries[name] = cast(static_cast...);
return *this;
}
~value_loader() { owner.update(); }
};
value_loader value(const char *name, T value) {
value_loader vl(*this);
vl.value(name, value);
return vl;
}
...
}; Then the common approach: py_enum3<MyEnum>(m, "MyEnum")
.value("v1", MyEnum.v1)
.value("v2", MyEnum.v2)
.value("v3", MyEnum.v3)
; will only do the |
Thanks -- yes, that fixes it.
This could work if you still call |
a5d6d27
to
c5b5b58
Compare
The only disadvantage of the current implementation, as I see it, is that you can't define methods / properties on the newly created enum class, because it's not a Hypothetically, here's also an alternative implementation: define py3 enums through
Also, in this case you won't be able to do |
2fe1849
to
a4381aa
Compare
fc629e2
to
d5e39cf
Compare
I've fixed a few things like setting @jagerman I've also tried out a very cheesy way of allowing to define properties and methods on Python 3 enums -- surprisingly it seemed to work, at least for simple cases I've tested -- so I pushed it as WIP in the last commit. If it's too hacky, can always roll it back. Basically, this seems to work: py3_enum<Foo>(m, "Foo")
.value("A", Foo::A)
.value("B", Foo::B)
.extend() // requires rvalue *this
.def_property_readonly("is_a", [](Foo x) { return x == Foo::A; }); |
Darn AppVeyor complaining all the time... |
3cf5a1c
to
b303e4d
Compare
I wonder if we should just turn off C4456 through C4459 warnings within pybind code. I don't really subscribe to the every-variable-must-be-unique idea of MS coding mentality that leads to silly things like prefixing all class members with some random character. |
I'd be +1 for sure, those warnings are very opinionated. |
Sound good to me. |
Actually, on second thought: let's try to keep pybind11 clean with respect to these warnings. Other projects may choose to activate a similarly strict set of warnings, and it would be unfortunate if pybind11 causes issues in that context. |
It won't cause external issues: the warnings are pushed at the beginning of pybind11.h and popped off at the end. |
Right.. I was thinking of |
I'd keep the warnings. C++ core guidelines / |
Fair enough. |
b303e4d
to
14d655a
Compare
Guys, any ideas on this? I'd be willing to finish it (if anything) but there's a few unclear points. |
Assuming you mean your original post checklist, I vote for calling it But I vote for it being neither distinct nor public. Rather I think that I'm not sure it's really worth it to worry about supporting additional definitions and attributes on the bound enum. (Or in other words, I'm perfectly happy for this to be a documented limitation, possibly to be revisited later if someone has a strong use case for it). |
I agree with @jagerman: |
If we roll it into one
The biggest question re: backwards-compatibility here is what to do with Basically, declaration semantics are slightly different as enum34 needs to be "finalised" (currently, the type is fully recreated when you call py::enum_<Foo>(m, "Foo").value("A", Foo::A).def("f", ...).value("B", Foo::B).def("g", ...); -- I can't see how to make it work with enum34 implementation. |
That doesn't actually compile with the current I think the more important things for maintaining compatibility are: |
Hmm, seems like I've managed to make this work: py3_enum<Foo>(m, "Foo").value(...).value(...).def(...).def(...) -- however it required slight changes to If this is acceptable, I'll take a look at Re: |
Is there a significant value in being able to define custom methods on an enum? It feels a bit like chasing backwards compatibility with a feature which was never really intended or advertised, which I think we'd be reasonably justified to just stop supporting given the complexity that it adds. |
Well, idk, there's definitely some value. We actually have quite a few in our pybind11 codebase, it's quite handy -- mostly |
If adding methods to py::enum_<Foo>(m, "Foo")
.value("First", Foo::First)
.value("Second", Foo::Second)
.classify() // `class_` functions permitted after this point; no more `enum_`
.def("__str__", [](Foo const&) { ... }); The class enum_ : public class_ { ... }; to: class enum_ {
public:
class_& clasify() { return cls; }
private:
class_ cls;
}; Using composition here instead of inheritance would simplify things overall since there would be no need for CRTP. |
@dean0x7d That's definitely reasonable and involves less hacks. In fact, that's exactly what I did initially if you look at the commit history for this branch (the only difference was that the method was called Note that there are potential edge cases with your suggested implementation, like: auto e = py::enum_<Foo>(m, "Foo")
.value("First", Foo::First)
.value("Second", Foo::Second);
auto c = e.classify()
.def("__str__", [](Foo const&) { ... });
e.value("Third", Foo::Third); // ??? To avoid this, I tried marking P.S. |
I'll close this PR.: While wrapping Python 3 enums would be nice, it was never quite clear how to do this in a backwards-compatible way that preserves the semantics of the current enums. pybind11 now uses a less bloaty way of creating |
This adds support for wrapping C/C++ enums as Python 3's
enum.IntEnum
(with@unique
decorator applied). It would also work withenum34
backport. If neither is available,ImportError
will be thrown. The existingenum_
wrappers are untouched and should work as before.enum_
fromclass_
value().value().def().def()
API, like the existingenum_<>
class_interface<>
APIpy::arithmetic
.export_values()
enum34
enum_
andenum34
(Closes #530)