I'm trying to mimic implicit conversion/construction of a simple struct in Python using pybind11:
struct MyType {
double a;
double b;
};
void func(size_t foo, const MyType& bar) {
// ...
}
// ...
PYBIND11_MODULE(pymylib, module) {
// ...
py::class_< MyType >(module, "MyType")
.def(py::init< double, double >(), "a"_a, "b"_a)
.def(py::init([](const std::array< double, 2 >& ab){ return MyType({ab[0], ab[1]}); }), "ab"_a = std::array< double, 2>{ 1.0, 0.25 }) // not implicit
.def_readwrite("a", &MyType::a)
.def_readwrite("b", &MyType::b);
py::implicitly_convertible< std::array< double, 2 >, MyType >(); // conversion is not implicit
module.def("func", &func, "foo"_a, "bar"_a);
}
While in C++ one can use brace initialization {} without having to implicitly call MyType constructor (simplifying the syntax), I am still unable to have an implicit conversion from a list object when passing MyType as argument to func:
obj = module.MyType(8.7, 5.6)
module.func(47, obj) # works
module.func(47, module.MyType([4.1, 7.8])) # works
module.func(47, [4.1, 7.8]) # does not work ('incompatible function arguments')
From what I understand from pybind11 documentation, the constructor declaration py::init< double, double >() should bind brace-initialization implicitly, but it's not working as I expected:
TypeError: func(): incompatible function arguments. The following argument types are supported:
1. module.func(a: float, b: module.MyType)
I tried adding a custom constructor accepting a list/array, but it is still not called implicitly.
Due to the simplicity of the struct, having to explicitly call the constructor seems unnecessary, particularly if defined in a Python submodule or nested on another class; How - if possible - do I achieve this behavior in Python?
The documentation you are citing refers to the way the python initializer calls the C++ constructor. It makes no remark as far as I can tell regarding implicit conversion from
list. As you suspected, you need to register a specialpy::init()for it.Now regarding why your special
py::init()does not work: My best guess is you are missing#include <pybind11/stl.h>, see https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html:Here is a working demo: https://godbolt.org/z/WrPrb8cM8