Return None for "optional" PImpls #992
Unanswered
bethebunny
asked this question in
Q&A
Replies: 2 comments 2 replies
-
We use a wrapper function to support this case. It's a bit of a hack (it would be nice if there was a function annotation like #include <nanobind/nanobind.h>
#include <nanobind/stl/optional.h>
NAMESPACE_BEGIN(detail)
template <typename Fun, typename... Args>
std::optional<std::invoke_result_t<Fun, Args...>> wrapNullableImpl(
Fun&& fun, Args&&... args) {
auto ret = std::invoke(std::forward<Fun>(fun), std::forward<Args>(args)...);
if (ret == nullptr) {
return std::nullopt;
}
return ret;
}
NAMESPACE_END(detail)
/// `wrapNullable` wraps a function that returns a pointer of type T into a
/// function that is typed with return value `T | None` in python.
///
/// NOTE: Lambdas are omitted here because if you're writing your own lambda,
/// you might as well implement this convention from the get-go.
template <class R, typename Class, typename... Args>
auto wrapNullable(R (Class::*fun)(Args...)) {
return [=](Class& self, Args... args) {
return detail::wrapNullableImpl(fun, self, args...);
};
}
template <class R, typename Class, typename... Args>
auto wrapNullable(R (Class::*fun)(Args...) const) {
return [=](const Class& self, Args... args) {
return detail::wrapNullableImpl(fun, self, args...);
};
}
template <class R, typename Class, typename... Args>
auto wrapNullable(R (Class::*fun)(Args...) &) {
return [=](Class& self, Args... args) {
return detail::wrapNullableImpl(fun, self, args...);
};
}
template <class R, typename Class, typename... Args>
auto wrapNullable(R (Class::*fun)(Args...) const&) {
return [=](const Class& self, Args... args) {
return detail::wrapNullableImpl(fun, self, args...);
};
}
template <class R, typename... Args> auto wrapNullable(R (*fun)(Args...)) {
return [=](Args... args) { return detail::wrapNullableImpl(fun, args...); };
} |
Beta Was this translation helpful? Give feedback.
1 reply
-
If this sort of detection is supposed to work within object hierarchies, I think that you will have to make a custom type caster and specify partial template overloads for all types that should use it. Not sure what you mean by "I can't define a type caster for these types because I'm explicitly binding them". Example: namespace nanobind { namespace detail {
template <typename T> nullable_pimpl_type_caster {
... copy of base type caster code with extra bits for the C++ -> Python transition ...
};
template <> struct type_caster<MyType1> : nullable_pimpl_type_caster<MyType1> { };
template <> struct type_caster<MyType1> : nullable_pimpl_type_caster<MyType2> { };
}} |
Beta Was this translation helpful? Give feedback.
1 reply
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I'm binding a bunch of types which use a PImpl pattern. These types are sometimes populated with a null impl pointer, in which case they are effectively an empty optional.
What's the right way to bind these? I'd like to return
None
if any function returns an "empty" object, even though the function appears to return that type to nanobind.It's dangerous for Python to get instances of these null types, because they have all of the methods of the normal bound type, but the methods on them are unchecked which causes a segfault.
Is there any way to tell nanobind that it should return None when it would return one of these empty objects?
&typeid(std::nullptr_t)
I get a bad_castBeta Was this translation helpful? Give feedback.
All reactions