I want to bind a function that takes a lambda (std::function) as an argument. The lambda itself has a reference to an abstract type as an argument. When I attempt to invoke the function in python, I get an error because the abstract type is not copyable.
Below is a small example. I have an interface IFoo which is abstract and its concrete implementation Foo. I then have a function evaluate which takes as an argument a std::function<void(IFoo&)>.
I would prefer not to expose the concrete implementation in the python binding so I only bind the interface.
#include <iostream>
#include <pybind11/pybind11.h>
#include <pybind11/functional.h>
namespace py = pybind11;
class IFoo
{
public:
virtual ~IFoo() = default;
virtual void setValue(int a) = 0;
};
class Foo : public IFoo
{
public:
~Foo() override = default;
void setValue(int a) override { m_a = a; }
int getValue() const { return m_a; }
protected:
int m_a{18};
};
int evaluate(std::function<void(IFoo&)>&& callback)
{
Foo foo;
callback(foo);
return foo.getValue() + 34;
}
PYBIND11_MODULE(libbindtest, module)
{
py::class_< IFoo >(module, "Foo").def("setValue", &IFoo::setValue);
module.def("evaluate", &evaluate);
}
/*
int main (int, char**)
{
int res1 = evaluate([](auto& foo){ foo.setValue(22); });
int res2 = evaluate([](auto&){});
std::cout << "Res1: " << res1 << " Res2: " << res2 << std::endl;
return 0;
}
*/
This builds fine and runs with this definition of main.
My python looks like
import libbindtest as bt
def foo_operator(foo: bt.Foo):
foo.setValue(22)
x = bt.evaluate(foo_operator)
With this, I get the error
Traceback (most recent call last):
File "/path/to/bindtest.py", line 6, in <module>
x = bt.evaluate(foo_operator)
RuntimeError: return_value_policy = copy, but type IFoo is non-copyable!
I don't understand why IFoo needs to be copyable here. If I change the function definition to
int evaluate(std::function<void(IFoo*)>&& callback)
then everything works fine. Does someone know what I'm missing here.