2
votes

I know that there is a similar question here: Binding a function with std::initializer_list argument using pybind11 but because I cannot comment (not enough reputation) I ask my question here: Do the results from the above-linked question also apply to constructors: I.e. if I have a constructor which takes std::initializer_list<T> is there no way to bind it?

1

1 Answers

2
votes

There's no simple way to bind it, at least. Basically, as mentioned in the other post (and my original response in the pybind11 issue tracker), we can't dynamically construct a std::initializer_list: it's a compile-time construct. Constructor vs method vs function doesn't matter here: we cannot convert a set of dynamic arguments into the compile-time initializer_list construct.

But let me give you a way that you could, partially, wrap it if you're really stuck with a C++ design that requires it. You first have to decide how many arguments you're going to support. For example, let's say you want to support 1, 2, or 3 int arguments passed via initializer_list<int> in the bound constructor for a MyType. You could write:

#include <stl.h>

py::class_<MyType>(m, "MyClass")
    .def(py::init([](int a) { return new MyClass({ a }); }))
    .def(py::init([](int a, int b) { return new MyClass({ a, b }); }))
    .def(py::init([](int a, int b, int c) { return new MyClass({ a, b, c }); }))
    .def(py::init([](std::vector<int> v) {
        if (vals.size() == 1) return new MyClass({ v[0] });
        elsif (vals.size() == 2) return new MyClass({ v[0], v[1] });
        elsif (vals.size() == 3) return new MyClass({ v[0], v[1], v[2] });
        else throw std::runtime_error("Wrong number of ints for a MyClass");
    });

where the first three overloads take integer values as arguments and the last one takes a list. (There's no reason you'd have to use both approaches--I'm just doing it for the sake of example).

Both of these are rather gross, and don't scale well, but they exhibit the fundamental issue: each size of an initializer_list needs to be compiled from a different piece of C++ code. And that's why pybind11 can't support it: we'd have to compile different versions of the conversion code for each possible initializer_list argument length--and so either the binary size explodes for any number of arguments that might be used, or there's an arbitrary argument size cut-off beyond which you start getting a fatal error. Neither of those are nice options.

Edit: As for your question specifically about constructors: there's no difference here. The issue is that we can't convert arguments into the required type, and argument conversion is identical whether for a constructor, method, or function.