-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Pure virtual overloaded functions #2351
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
This is a Python and not a pybin11 issue. Python doesn't do overloading. The second Write a single class Listener(pyfoo.Listener):
def func(self, n):
print("int" if isinstance(n, int) else "float" if isinstance(n, float) else "unknown") |
@YannickJadoul Thanks for the response. Right, I got that part. I am actually very curious about how:
pybind is able to achieve overloading in the above case. Any pointers to documentation/code would be very helpful.
I understand. So the c++ 'pattern' like this:
There really is no good alternative on python side. (apart from doing isinstance checks in a single function) |
A high-level description is here: https://pybind11.readthedocs.io/en/stable/advanced/functions.html#overload-resolution-order. The actual implementation is not explicitly documented, but that's because it's implementation, I suppose. You - most likely - very much don't want to look at the But yes, pybind11 just accepts
Nope. Python is dynamically typed and doesn't do overload resolution on type arguments. So either implement your own overload resolution (in principle, it should be simple enough in this case?), or make the C++ interface you expose more pythonic by just giving it separate names, I'd say. |
Thanks for sharing this code! I actually never looked at implementation, and it seems the core components in dispatcher are fairly well documented.
In this case, yes. But I have a listener class which has more than 20 overloads for different types.
I am going to provide a pythonic shim over the c++ interface. Thanks for your help. |
I see. I'd just suggest to think how you would design the interface in Python, if there wasn't a C++ implementation. pybind11 is often flexible enough to allow you to wrap the C++ interface and provide that Python interface. |
@YannickJadoul I followed your advice and am running into an issue. Here are the details: This is a third party code that I have no control over: class Listener {
public:
virtual ~Listener() = default;
virtual void func(int) = 0;
virtual void func(double) = 0;
};
void invoke(Listener* listener) {
listener->func(42);
} To make it work nicely with python, I added the following: class MyListener : public Listener {
public:
virtual ~MyListener() = default;
void func(int i) override { funci(i); }
void func(double d) override { funcd(d); };
// inject new interface
virtual void funci(int) = 0;
virtual void funcd(double) = 0;
};
class PyListener : public MyListener {
public:
void funci(int i) override {
PYBIND11_OVERLOAD_PURE(void, MyListener, funci, i);
}
void funcd(double d) override {
PYBIND11_OVERLOAD_PURE(void, MyListener, funcd, d);
}
};
PYBIND11_MODULE(pyfoo, m) {
py::class_<MyListener, PyListener>(m, "Listener")
.def(py::init<>())
.def("funci", &MyListener::funci)
.def("funcd", &MyListener::funcd);
m.def("invoke", &invoke);
} On the python side, I am doing the following: import pyfoo
class A(pyfoo.Listener):
def funci(self, a):
print("answer is {}".format(a))
def funcd(self, d):
print("double answer is {}".format(d))
a = A()
pyfoo.invoke(a) And I get the following error:
My guess is that I need to make pybind aware that my Once again, thanks for your help. |
ok. I think I have stumbled upon a working solution. py::class_<MyListener, PyListener, Listener>(m, "Listener")
.def(py::init<>())
.def("funci", &MyListener::funci)
.def("funcd", &MyListener::funcd); However, this gave the following error when importing my module:
This was interesting. It meant that python is not able to find a previous Listener class. So I added one in module: PYBIND11_MODULE(pyfoo, m) {
py::class_<Listener>(m, "Listener");
py::class_<MyListener, PyListener, Listener>(m, "MyListener")
.def(py::init<>())
.def("funci", &MyListener::funci)
.def("funcd", &MyListener::funcd); And this seemed to have done the trick. I don't fully understand the template arguments of the
However, in the template arguments, the order we have passed is a bit bizzare. Looking at the code, it seems that both base and subtypes can be passed as template args (thought I don't understand the code yet) template <typename T> using is_subtype = detail::is_strict_base_of<type_, T>;
template <typename T> using is_base = detail::is_strict_base_of<T, type_>; Anyway, I have a working solution now. Thanks for your help on this. |
@skgbanga This should also work, I believe? class Listener {
public:
virtual ~Listener() = default;
virtual void func(int) = 0;
virtual void func(double) = 0;
};
void invoke(Listener* listener) {
listener->func(42);
}
class PyListener : public Listener {
public:
void func(int i) override {
PYBIND11_OVERLOAD_PURE_NAME(void, Listener, funci, func, i);
}
void func(double d) override {
PYBIND11_OVERLOAD_PURE_NAME(void, Listener, funcd, func, d);
}
};
PYBIND11_MODULE(pyfoo, m) {
py::class_<Listener, PyListener>(m, "Listener")
.def(py::init<>())
.def("funci", py::overload_cast<int>(&Listener::func))
.def("funcd", py::overload_cast<double>(&Listener::func));
m.def("invoke", &invoke);
} |
Note that this question combines two sections in the documentation:
Consider this simple c++ class:
As explained in the documentation, one can create python bindings for this via:
And everything works perfectly on the python side.
(It is a bit weird that one can do overloading in python via this).
Now consider another class:
As explained in the documentation, this requires us to setup a trampoline like this:
Now, my question is: How does one override these methods on the python side. Python clearly doesn't support overloading, so how can I write my own class in this case.
Failed attempt:
The text was updated successfully, but these errors were encountered: