Boost::python 中使用和返回模板的 C++ 函数暴露

9 投票
3 回答
4650 浏览
提问于 2025-04-16 10:54

我需要为一个C++代码库创建Python的接口。我使用的是boost::python,但在尝试暴露包含使用和返回模板的函数的类时遇到了问题。下面是一个典型的例子。

class Foo 
{ 
    public: 
        Foo(); 
        template<typename T> Foo& setValue(
            const string& propertyName, const T& value); 
        template<typename T> const T& getValue(
            const string& propertyName); 
}; 

这里的典型类型包括字符串、双精度浮点数和向量。

在阅读了文档后,我尝试为每种使用的类型创建简单的包装器。以下是字符串和双精度浮点数的包装器以及相应的类声明。

Foo & (Foo::*setValueDouble)(const std::string&,const double &) = 
    &Foo::setValue; 
const double & (Foo::*getValueDouble)(const std::string&) = 
    &Foo::getValue;

Foo & (Foo::*setValueString)(const std::string&,const std::string &) = 
    &Foo::setValue; 
const std::string & (Foo::*getValueString)(const std::string&) = 
    &Foo::getValue;

class_<Foo>("Foo") 
    .def("setValue",setValueDouble, 
        return_value_policy<reference_existing_object>()) 
    .def("getValue",getValueDouble,
        return_value_policy<copy_const_reference>()) 
    .def("getValue",getValueString, 
        return_value_policy<copy_const_reference>()) 
    .def("setValue",setValueString, 
        return_value_policy<reference_existing_object>());

代码编译得很好,但当我尝试使用这些Python接口时,却遇到了C++的异常。

>>> f = Foo()  
>>> f.setValue("key",1.0) 
>>> f.getValue("key") 
Traceback (most recent call last): 
  File "<stdin>", line 1, in ? 
  RuntimeError: unidentifiable C++ exception

有趣的是,当我只暴露处理双精度浮点数或字符串值的Foo类时,也就是

class_<Foo>("Foo") 
    .def("getValue",getValueString, 
        return_value_policy<copy_const_reference>()) 
    .def("setValue",setValueString, 
        return_value_policy<reference_existing_object>());

这样就能正常工作。

我是不是漏掉了什么?

3 个回答

0

关于为获取器(getter)和设置器(setter)创建一个C++的包装器,这个包装器可以返回或接收一个boost::python::object,你可以在你的C++包装器中简单地判断你得到的是什么类型,然后把它包装成boost::python::object,或者从boost::python::object中解包出来。

struct FooWrap : public Foo
{
    using boost::python;
    Foo& setValueO(const string& propertyName, const object& obj)
    {
        object value;
        if(PyInt_Check(obj.ptr())) {
            return setValue<int>(propertyName, extract<int>(obj);
        } else if(PyFloat_Check(obj.ptr())) {
            return setValue<double>(propertyName, extract<double>(obj);
        } else if(PyString_Check(obj.ptr())) {
            return setValue<std::string>(propertyName, extract<std::string>(obj);
        }
        // etc...
    }

    object getValueO(const std::string& propertyName)
    {
        if(determineType() == TYPE_INT) { // however you determine the type
            return object(getValue<int>(propertyName));
        } else if(determineType() == TYPE_DOUBLE)   {
            return object(getValue<double>(propertyName));
        } else if(determineType() == TYPE_STRING)   {
            return object(getValue<std::string>(propertyName));
        }
        // etc...
    }
};

class_<Foo>("Foo") 
    .def("setValue", &FooWrap::setValueO, 
        return_value_policy<reference_existing_object>()) 
    .def("getValue", &FooWrap::getValueO,
        return_value_policy<copy_const_reference>()) 
0

我觉得问题可能出在boost::python不知道该调用哪个版本的“getValue”方法——是调用getValueDouble还是getValueString呢?如果你把它们明确地绑定为getValueString和getValueDouble(作为方法名),我敢打赌这样就能正常工作了。

3

这可能跟你的问题没有直接关系,但我不太信任像这样的模板函数签名转换。我会这样包装它:

class_<Foo>("Foo") 
    .def("setValue", &Foo::setValue<double>, 
        return_value_policy<reference_existing_object>()) 
    .def("getValue", &Foo::getValue<double>,
        return_value_policy<copy_const_reference>()) 
    .def("getValue", &Foo::getValue<std::string>, 
        return_value_policy<copy_const_reference>()) 
    .def("setValue", &Foo::setValue<std::string>, 
        return_value_policy<reference_existing_object>());

如果这样还不行,你可能需要创建一些辅助函数:

Foo& setValueDouble(foo& self, const string& propertyName, const double value)
{ 
    return self.setValue(propertyName, value)
}
...

然后把这些函数导出,就像它们是类的成员函数一样。

在Boost::Python中,把多个重载函数导出为同一个名字是完全可以的,所以我觉得这不是问题所在。

撰写回答