-
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
Add check for matching holder_type when inheriting #588
Conversation
3538dd6
to
652a73a
Compare
I think the test could be simplified so it doesn't require a separate module. It should be possible to pack up everything into a function within the existing test module. Something like: m.def("test_matching_holder_type", []() {
struct Base { };
struct Derived : Base { };
try {
py::class_<Base>(m, "Base");
py::class_<Derived, std::shared_ptr<Derived>, Base>(m, "Derived")
.def(py::init<>());
} catch (std::runtime_error &) {
return true;
}
return false;
}); assert test_matching_holder_type() is True It might be possible to simplify template <typename... Ts>
struct void_holder<std::unique_ptr<Ts...>> {
using type = std::unique_ptr<void>;
}; where the tinfo->void_holder_type = &typeid(detail::void_holder<holder_type>::type); A more generic approach for template <typename T, typename SFINAE = void>
struct void_holder {
using type = void;
}
template <typename...> using void_t = void;
template <template<typename...> class Holder, typename... Ts>
struct void_holder<Holder<Ts...>, void_t<Holder<void>>> {
using type = Holder<void>;
}; The |
+1 for the single module approach proposed by @dean0x7d. FWIW I think that storing a full type ID is overkill. I suggest to record a single |
Thanks for the feedback. I pushed a simplified version. |
That is why it was variadic, but I agree this is better. I'll make the change. |
Wondering if we should split the error reporting telling which one has non-default holder type? |
I split it up. Easier to debug. |
/// Is the default (unique_ptr) holder type used? | ||
bool default_holder : 1; | ||
|
||
PYBIND11_NOINLINE void add_base(const std::type_info *base, void *(*caster)(void *), const bool check_holder_type = false) { |
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 is the check_holder_type
parameter needed?
py::class_<Base>(m, "Base"); | ||
py::class_<Derived, std::shared_ptr<Derived>, Base>(m, "Derived") | ||
.def(py::init<>()); | ||
} |
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.
Unless I'm missing something, the second test is going to be skipped completely because the first one throws. Consider separate functions.
Also, .def(py::init<>())
is probably not needed for the test.
.def(py::init<>()); | ||
} | ||
} catch (std::runtime_error &) { | ||
return true; |
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.
Sorry, I need to revise my original suggestion a bit. It might be nicer to catch the exception in Python in order to also check that the correct exception was thrown.
m.def("test_matching_holder_type", []() {
struct Base { };
struct Derived : Base { };
py::class_<Base>(m, "Base");
py::class_<Derived, std::shared_ptr<Derived>, Base>(m, "Derived");
});
with pytest.raises(RuntimeError) as excinfo:
test_matching_holder_type()
assert str(excinfo.value) == "generic_type: ..."
I followed the suggestions from @dean0x7d to clean things up a bit more. Please let me know if I should squash the commits into one. |
There are some weird failures on AppVeyor. It may have to do with the uninitialized There are also some minor style issues. See the log on Travis. |
7333402
to
38cf38d
Compare
Changes implemented and commits squashed. |
Looks great -- thank you! |
Instead of a segfault. Fixes pybind#751. This covers the case of loading a custom holder from a default-holder instance. Attempting to load one custom holder from a different custom holder (i.e. not `std::unique_ptr`) yields undefined behavior, just as pybind#588 established for inheritance.
One frequently encountered source of errors are mismatching holder types between bases and derived classes. If either the base has
unique_ptr
holder type and the derived classshared_ptr
(or vice versa) a segfault results.Tracking holder types for all classes in a large codebase can be cumbersome and error prone.
This pull request is an example of how import-time checks for matching holder types can be added. I welcome your comments.