-
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
Undocumented required default constructor for types with type casters #1036
Comments
Thanks for reporting. This is now documented in 93528f5. Note that this limitation should be resolved with #864 in v2.3. There are ways to get around this limitation even with the current implementation of |
To elaborate on the workaround a little (because I was asked externally), what you'd do is basically expand the PYBIND11_TYPE_CASTER macro in your own code, then make some modifications to change the #include <pybind11/pybind11.h>
namespace py = pybind11;
struct Foo {
Foo() = delete;
Foo(int i) {}
};
namespace pybind11 { namespace detail {
template <> struct type_caster<Foo> {
/// Modified PYBIND11_TYPE_CASTER code:
protected:
std::unique_ptr<Foo> value_ptr;
public:
static constexpr auto name = _("Foo");
template <typename T_, enable_if_t<std::is_same<Foo, remove_cv_t<T_>>::value, int> = 0>
static handle cast(T_ *src, return_value_policy policy, handle parent) {
if (!src) return none().release();
if (policy == return_value_policy::take_ownership) {
auto h = cast(std::move(*src), policy, parent); delete src; return h;
} else {
return cast(*src, policy, parent);
}
}
operator Foo*() { return value_ptr.get(); }
operator Foo&() {
if (!value_ptr) throw std::runtime_error("Cannot extract value from nullptr");
return *value_ptr;
}
operator Foo&&() && { return std::move((Foo &) *this); }
template <typename T_> using cast_op_type = pybind11::detail::movable_cast_op_type<T_>;
/// End of modified PYBIND11_TYPE_CASTER code.
bool load(handle src, bool) {
// ... use `src` to extract and construct a Foo value inside the unique ptr, e.g.:
value_ptr.reset(new Foo(123));
return true;
}
static handle cast(Foo f, return_value_policy /* policy */, handle /* parent */) {
// ... use `f` to create and return an appropriate Python handle
return none().release();
}
};
}}
PYBIND11_MODULE(tcnd, m) {
m.def("f", []() { return Foo(123); });
m.def("g", [](Foo &) { return 999; });
} Note that, as @dean0x7d mentioned, there is a chance that this will break in v2.3 as the type caster system is expected to undergo an overhaul. We'll maintain compatibility with type casters using the currently documented approach (i.e. with the macro), but there might be some necessary changes to the macro itself for compatibility that you would need to incorporate to make this sort of code compatible with 2.3. (I think, however, that as @dean0x7d mentioned, the new approach should eliminate the limitation, so a better (and probably simpler) approach for making the code work with 2.3 will be to just update custom casters to the new caster mechanism that will be added). |
Thank you very much for further elaboration. I asked you as well as dean0x7d privately, but more than happy to continue here. I am trying your code, but having issues compiling the line: static constexpr auto name = _("Foo"); I apologize for my ignorance, but what is the underscore here? FWIW, I plan to use this to custom type cast types that look as follows: std::shared_ptr<some_array<double, 2>>, ideally. Thanks edit: I guess that my problem is that this does not compile with VS 2017 for me. I am getting: 1>------ Build started: Project: cmake_example, Configuration: Debug x64 ------ |
After fiddling around a bit, I replaced static constexpr The code is throwing an exception however. The code now looks like this. Note that I had to use & in the reset statement at the end of the load function. When I debug into the load function, I am getting the required shared_ptr to the array. However, something then happens to to value_ptr downstream which I don't get. Any clue what is going on? Thanks.
|
I just did a quick test for the case of casting the array directly - no std::shared_ptr, and it seems to work fine, which is great, but the server code I am trying to comply with requires shared pointers to these arrays (don't ask me why). So I am looking for an issue with the std::shared_ptr wrap I guess. I will try to debug carefully to diagnose where the problem occurs. edit: As I debug into the code, I am finding that the reference count of the std::shared_ptr probably goes to zero as I leave the load function. (I put a break point in the destructor of the array being held on to and hit that break point.) I must be doing something idiotic. The statement here must be wrong:
|
You might be able to adapt this to simply store the |
I weakly tried this before writing to you, if I remember well. The problem is that I don't have a default constructor for the underlying type. I thought at some point that the pybind11 mechanism might call the But now I figured that I am just not handling
Before, I guess I was resetting the Thank you for all the help jagerman. It's greatly appreciated. |
Issue description
When defining a custom type caster as per http://pybind11.readthedocs.io/en/master/advanced/cast/custom.html it is required that the type you are casting to/from has a default constructor. Upon careful consideration of the sample code, it is not surprising that this is the case, but it is not documented, and it gives a rather opaque template expansion error:
Reproducible example code
Do the same example as in the docs, but delete the default constructor.
The text was updated successfully, but these errors were encountered: