8

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();
}
Johan
  • 135
  • 1
  • 6
  • 1
    (I don't know pybind11) Can you have multiple `py::class_`es in one `py::module`? `py::class_>(m, "DuckTypeA");` ... `py::class_>(m, "DuckTypeZ");` – Caleth Jul 05 '17 at 12:35

2 Answers2

1

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 InTypes are defined such that they won't accidentally intercept another class's type:

runType(arg=1.2)  # TypeA
runType(arg="hello world")  # TypeB
eacousineau
  • 3,457
  • 3
  • 34
  • 37
  • 1
    The question was not about overloading at the function level. It was on templating at the class level. – chaitan94 Apr 20 '20 at 16:57
  • 1
    True, but if you look at the actual question, the functionality ultimately turns into function overloading. I'll defer it to the OP to follow-up on what their true intent was. If you search on `pybind11`s Gitter or what not, you can find more examples on wrapping template classes. Basically, just `py::class_>(m, "MyTemplate")`. If you need parameterization and all, you can write shims to handle the type resolution. Example: https://git.io/JfTKY – eacousineau Apr 21 '20 at 03:57
1

An example can be found over in the following github issue

https://github.com/pybind/pybind11/issues/199#issuecomment-323995589

I'll copy the example here to be complete

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>

namespace py = pybind11;

template <typename T>
class MyClass : public SuperClass
{
 MyClass(int foo)   {}
 void func (int baa)
}

template <typename T>
void MyClass<T> :: func(int baa) {}

PYBIND11_MODULE(example, m) {
py::class_< MyClass<double>, shared_ptr< MyClass<double> >, SuperClass>(m, "MyClass")
 .def(py::init<int>())
 .def("func", &MyClass<double>::func);
}
jxramos
  • 7,356
  • 6
  • 57
  • 105