如何使用boost::python将模板化的类作为一个类公开给python

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

本文关键字:python 一个 何使用 boost      更新时间:2023-10-16

在很多关于boost::python的例子中,你会看到:

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

当然,如果你需要一个double向量你会有第二个类,叫做DoubleVector。在我看来,这不是很"蟒"。如果c++中的一个(模板化的)类实际上是python中的一个类,它会更直观(我认为),它接受像, ..., type='float', ... ,这样的参数。这样,类也只在pydocs中出现一次,并且只需要在boost::python module中添加一次。

假设我们有一个简单的c++模板类:

template <typename T>
MyClass
{
    T val;
public:
    MyClass(const T& tVal) : val(tVal) {}
    T    getVal()              { return val; }
    void setVal(const T& tVal) { val = tVal; }
};

现在我们要为Python编写一个接口。我的想法是:

使用boost::变异

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());
}

这个解决方案的一个明显缺点是我猜boost::variant可以处理完全不同的类型,而我的类除了它们存储的数据类型之外几乎是相同的。因此,可能有更多的信息被抽象掉了。

所以我想这个问题可以归结为例子中的两个空函数。然而,更简洁、更短或更少的"if"链式答案当然也会被接受。正如标题所说,它是关于模板化类和boost::python,而不一定是boost::variant。

这里有几个令人困惑的地方。首先,在Python中,object基本上已经是一个变体——一切都是object。在c++中,类模板不是类型——它是创建类型的方法。所以如果你想把MyClass<T>从c++暴露给Python,你应该暴露所有的MyClass<T>s

我会写一个函数模板来做绑定:

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);
        ;
}

然后命名为:

bind_MyClass<float>("MyClassFloat");
bind_MyClass<double>("MyClassDouble");
相关文章: