7

I use pybind11 as a wrapper of my C++ code into a python library.

It happens that there are arguments that I can't provide or sometimes I want to do a conversion/initialization that I know in the C++ side. It could be because the class is not known in python, for instance. How could that be done? The only "solution" I see so far would be to create an inherited proxy class in C++.

Example: I want to define/bind a python class A:

class A:
  def __init__(self, B b):
    ...

With a C++ equivalent class:

class A {
  A(C c, D d);
}

Is there some kind of lambda or an equivalent that I could create for the pybind11::init<>?

Emile D.
  • 602
  • 2
  • 11
  • 20
  • How would you get the instance of `c` and `d` from `b`? Your question is not really clear... Could you provide an example of how you want to use the class on the python side and what you would want to happen on the C++ side, even if it's not valid C++? – Holt Jun 10 '20 at 17:47
  • Valid point. I was trying to be generalistic, but it could be a conversion of std::string known in pybind11 to a QString for instance, with the info of the locale to use. Among all the examples possible. Or another case where I had B = C but D was a local variable (in my case, a pointer initialized to nullptr, thus not "that" local, but you get the spirits of the wanted use cases.) – Emile D. Jun 10 '20 at 18:23

1 Answers1

8

pybind11 lets you bind factory functions as init methods. So you would have to provide a function in c++ that took a B and return an A and then you could bind that as an init method for A.

An example from the pybind11 docs

class Example {
private:
    Example(int); // private constructor
public:
    // Factory function:
    static Example create(int a) { return Example(a); }
};

py::class_<Example>(m, "Example")
    .def(py::init(&Example::create));

You should be able to bind in a free function as well (not just a static function), if you do not want to (or can't) change class A in c++.

So it could look something like this (changed to return a unique_ptr, which pybind can just take ownership of vs a raw instance. But either should work)

std::unique_ptr<A> createA(const B& arg)
{
  // returns an instance of A that you made using B
}

py::class_<A>(m, "A")
    .def(py::init(&createA));

You obviously then have to also provide a binding for B in python.

Docs are here and include even more examples, including how to do an init lambda as well: https://pybind11.readthedocs.io/en/stable/advanced/classes.html#custom-constructors

Jesse C
  • 779
  • 4
  • 11