-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Do not derive enum_ from class_ #837
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
Conversation
5cad257
to
11b8d13
Compare
include/pybind11/pybind11.h
Outdated
} | ||
|
||
/// Add an enumeration entry | ||
enum_& value(char const* name, Type value) { | ||
enum_ value(char const* name, Type value) && { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why only rvalues? I'm a bit more worried about this breaking backward compatibility than the inherited class_
functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, if you return a reference, you won't be able to call .into_class()
which needs an rvalue (which is an important point, in order to avoid all kinds of edge cases). Taking an rvalue *this
here, disabling copy ctor and returning by value basically helps ensure we never copy it (accidentally or not) and always move. The usual chaining-style enum definitions still all work as expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, right, it would be required for the new enum34
. I was only looking at it for the enum_
here, which does not need to return an rvalue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added back the lvalue overloads which seems to work and won't break backwards compatibility if someone chose to assign enum object to a variable and not chain the calls:
// ok
auto e = enum_<>();
e.value().value();
// ok
enum_<>().value().into_class().def();
// not ok
auto e = enum_<>().value();
e.into_class().def(); // <-- compile fail
// not ok either
auto e = enum_<>().value();
e.value().into_class().def(); // <-- compile fail
include/pybind11/pybind11.h
Outdated
return cls; | ||
} | ||
|
||
operator class_<Type>() && { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why make it implicitly convertible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's how I had it initially (in the other PR) -- with hindsight, this could probably be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now removed.
Just to make sure we don't break anything important, I'll just cc this for others how have also contributed to The gist is that |
11b8d13
to
9f06964
Compare
Thinking a bit more about this, I find problematic that Why are we changing this? It seems to be making things more difficult that were simpler before. In that case, I would find it preferable to document the old behavior and keep it as is. |
@wjakob I've initially chosen We are changing it because it was a bit broken initially (and undocumented) since |
I think that it would be straightforward to fix the |
(i.e. no CRTP needed) |
Sort of, yea. It's a bit more than def though... |
(For the CRTP example, see cf1ef95) |
This seems fine to me -- ~9-ish lines of code that will all be optimized away, no? |
That's it, no? template <typename... Args> def(Args&&... args) { return (enum_&) Base::def(std::forward<Args>(args)...); }
template <typename... Args> def_property_readonly(Args&&... args) { return (enum_&) Base::def_property_readonly(std::forward<Args>(args)...); }
template <typename... Args> def_readonly_static(Args&&... args) { return (enum_&) Base::def_readonly_static(std::forward<Args>(args)...); } Or am I missing something? |
For the new enum34 implementation - yea, something like this (x3) would work; hacky but should suffice. For If we're going this way... this is exactly what a separate CRTP would do, less the copy/paste. With a bit of effort I think it could benefit both enum implementations -- for |
I don't think it's hacky :). CRTP brings its own set of issues -- it definitely doesn't make the source easier to read ;). In one of my current projects, MSVC 2017 needs to be spoon-fed some of the CRTP-related type aliases to avoid compiler segfaults (accessing members of the derived class in template default parameters still seems somewhat fragile). |
If the duplication bothers you, we could add a macro: #define PYBIND11_FORWARD(name) template <typename... Args> name(Args&&... args) { return static_cast<decltype(*this)>(Base::name(std::forward<Args>(args)...)); } I'd really prefer this to making something that should be a |
A macro would be reasonable, it will certainly deal with |
Could |
Btw @wjakob could you post examples of code you've have that relies on |
@jagerman I was thinking about it too, will take a look tomorrow. |
One simple example are cases where an enumeration exists in separate classes. Something like this: class A {
enum E {Value = 0};
};
class B {
using E = A::E;
};
class_<A> cls_a(m, "A");
class_<B> cls_b(m, "B");
enum_<A::E> e(cls_a, "E");
e.value("Value", A::E::Value);
cls_b.attr("E") = e; |
Hm -- what about @jagerman's suggestion ( |
I thought a little bit about it and don't think |
I'm not sure I see the problem with the |
@jagerman Hmm yea you're right, in the current state |
... nor will @wjakob's if the lvalue version is removed, which doesn't seem desirable. Requiring you to write |
Yep, you're right. Either way something will be broken it seems :) Unless there's some other, smarter way (deriving from object but not class_? idk). If there's no middle ground, I'm ok with retracting this PR altogether since it was opened out of a suggestion voiced in enum34 PR. if everyone thinks the |
I guess the entire point here was to make |
Yea; far as I see now, without big changes it's only possible to make them API compatible if we accept the possibility of runtime errors thrown in enum34 API at module initialization time due to incorrect use (which gets compiled ok) -- I really wouldn't like this to be the case. So (soon as I get back to IE...) I'll probably close this and revert all changes to |
In fact I'll close this one now so it doesn't annoy anyone anymore :) (I still think |
(This is related to work on Python 3 enums in #781)
class_
API now requires calling.into_class()
or doing a rvalue cast.value()
and.export_values()
now return rvalue*this
instead of lvalueNote: this is technically a breaking change, but the consensus seems to be that this functionality was more of a side-effect (of deriving from
class_
) rather than an intended feature.