NOhs
NOhs

Reputation: 2830

How to expose a templated class as one class to python with boost::python

In many examples about boost::python you see something like:

using namespace boost::python;
typedef class_<std::vector<float>> VectorFloat;

And then of course if you need a double vector you will have a second class called DoubleVector or so. In my opinion this is not very "pythonic". It would be more intuitive (I think), if one (templated) class in C++ is actually one class in python which takes an argument like , ..., type='float', ... ,. This way the class also appears only once in the pydocs and has to be added only once to the boost::python module.

So let's say we have a simple templated class for our C++ code:

template <typename T>
MyClass
{
    T val;

public:
    MyClass(const T& tVal) : val(tVal) {}

    T    getVal()              { return val; }
    void setVal(const T& tVal) { val = tVal; }
};

Now we want to write an interface for Python. My idea so far:

Using boost::variant

typedef boost::variant<MyClass<float>, MyClass<double>> VariantClass;    

class MyPythonClass
{
    VariantClass vClass;

public:
    MyPythonClass(
        const PyObject& tVal, 
        const boost::python::str& type)
    {
        using namespace boost::python;
        std::string type_string = extract<std::string>(type);

        if( type_string == "float" )
        {
            float f = extract<float>(tVal);
            vClass = MyClass(f);
        }
        else if( type_string == "double" )
        {
            double d = extract<double>(tVal);
            vClass = MyClass(d);
        }
    }

    boost::python::PyObject* getVal()
    {
        // What to put here?
    }

    void setVal(const boost::python::PyObject& tVal)
    {
        //What to put here?
    }
};

BOOST_PYTHON_MODULE(my_module)
{
    class_<MyPythonClass>("MyClass", init<boost::python::PyObject, int, boost::python::str>).def("getVal", &MyClass::getVal());
}

A clear drawback of this solution is I guess that boost::variant can work with types that are quite different wheras my classes are almost identitcal, except for the type of data they store. So probably more information is abstracted away than necessary.

So the question I guess boils down to the two empty functions in the example. However, cleaner and shorter or less "if"-chain like answers will of course also be accepted. As the title says, it is about templated classes and boost::python and not necessarily about boost::variant.

Upvotes: 0

Views: 828

Answers (1)

Barry
Barry

Reputation: 303017

There's a couple sources of confusion here. First, in Python, object is already basically a variant - everything is an object. And in C++, a class template isn't a type - it's a recipe for creating a type. So if you want to expose MyClass<T> from C++ to Python, you should just expose all the MyClass<T>s

I would write a function template that does the bindings:

template <class T>
void bind_MyClass(const char* name) {
    class_<MyClass<T>>(name, init<T const&>())
        .add_property("val", &MyClass<T>::getVal, &MyClass<T>::setVal);
        ;
}

And then just call it:

bind_MyClass<float>("MyClassFloat");
bind_MyClass<double>("MyClassDouble");

Upvotes: 1

Related Questions