I've got a class structure similar to the one below, where I have two types A and B with similar signatures that only differ in argument/return type. I then use a class template to be able to process both classes, achieving a static variant of ducktyping in Python. Now I want to wrap this code into Python with pybind11, where I hope to get a class that takes both types using standard, dynamic ducktyping. How do I do this?
I'm basically looking for a way to disable the strict type check in pybind11 or specify multiple types such that both TypeA and TypeB are accepted. The way the example below is defined, only TypeA passes the check. I also can't unify A and B into a base class because of the different function signatures.
#include <pybind11/pybind11.h>
#include <iostream>
class TypeA {
public:
typedef double InType;
typedef std::string OutType;
const OutType operator()(const InType arg)
{
return std::to_string(arg);
}
};
class TypeB {
public:
typedef std::string InType;
typedef double OutType;
const OutType operator()(const InType arg)
{
return std::stod(arg);
}
};
template<typename T>
class DuckType {
public:
void runType(const typename T::InType arg)
{
T x;
const typename T::OutType y = x(arg);
std::cout << y << std::endl;
}
};
namespace py = pybind11;
PYBIND11_PLUGIN(ducktyping) {
pybind11::module m("ducktyping", "Testing ducktyping with templates");
typedef DuckType<TypeA> Type;
py::class_<Type>(m, "DuckType")
.def("run_type", &Type::runType);
;
return m.ptr();
}
If the function you're binding has arguments that can "drive" inference in C++, you could potentially use them to drive the overload resolution in pybind11
:
https://pybind11.readthedocs.io/en/stable/advanced/functions.html#overload-resolution-order
For example, you can bind your instantiations as overloads, and let pybind11
figger it out:
// Let's say `DuckType::runType` is static.
m.def("runType", &DuckType<TypeA>::runType, py::arg("arg"));
m.def("runType", &DuckType<TypeB>::runType, py::arg("arg"));
Then the dispatch will work in Python, assuming that users can instantiate those types, and the InType
s are defined such that they won't accidentally intercept another class's type:
runType(arg=1.2) # TypeA
runType(arg="hello world") # TypeB
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With