-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Description
I am going to address this issue one more time. It was first brought up in #642, then again in #1382.
In short, we cannot make a custom type that implicitly converts from None. @bstaletic suggested a solution that doesn't work.
Consider the following example code (paraphrased, might have typos):
#include "pydip.h"
class A {
public:
int value = 0;
A() = default;
A(int v) : value(v) {};
}
int X(A const&) {
return A.value;
}
PYBIND11_MODULE(mymodule, m) {
auto a = py::class_<A>(m, "A");
a(py::init<>());
a("__init__", [](py::object const& o) { // @bstaletic's solution
if (o.is_none()) {
return A{};
}
throw py::type_error("boom");
});
//a(py::init([](py::none const&) { return A{}; })); // I had tried this one too
py::implicitly_convertible<py::none, A>();
a(py::init<int>());
py::implicitly_convertible<int, A>();
m.def("X", X);
}I can now do in Python:
a = A(None)
a = A(1)
X(a)
X(1)But I cannot do
X(None)However, if I edit pybind11/cast.h, at the top of bool load_impl(handle src, bool convert), where it says:
if (src.is_none()) {
// Defer accepting None to other overloads (if we aren't in convert mode):
if (!convert) return false;
value = nullptr;
return true;
}If I remove the value = nullptr; return true; lines, then the above all works correctly.
I have not found any downsides to doing so. This means that the custom converter function gets a py::none object as input, it can choose to reject that or accept that. Any conversion function already tests what the type of the input object is, so this should not affect any existing code.
I have been using this "fix" for some months now, but would prefer a solution that doesn't require patching pybind11.
In real life, the above example is for an image class where the default-constructed image has no pixels, and is used to signal "I don't need this input" to specific functions. In C++ one can use {} for a default-constructed object, None is the most Pythonic substitution. See this other comment for details.
Sure, I could write a function for X(None), but this is for a large project with a lot of functions, and creating multiple versions of each for various inputs being None is not doable.