(boost.python) 错误,无法暴露重载运算符+(). "TypeError: 找不到to_python(按值)转换器

1 投票
1 回答
933 浏览
提问于 2025-04-17 04:05

我刚接触boost.python,遇到了一个错误,希望能得到一些帮助。在一个更大的项目中,我正在为一个向量类写一个包装器。从下面的代码可以看出,这个类可以是二维或三维的,但对于我现在遇到的问题,这两者没有太大区别。我正在尝试包装我定义的向量类中的一些运算符,这样做没问题,只要这些函数(或者重载的运算符)不返回一个“值”的类型为Vector。我猜测Python不知道该如何处理我按值返回的对象。这是代码:

struct D2
{
   //...
   typedef Eigen::Matrix< long double, 2, 1 > Vector;
   //...
};
struct D3
{
   //...
   typedef Eigen::Matrix< long double, 3, 1 > Vector;
   //...
};

接下来定义了一个宏,用来处理这两种可能的维度。

BOOST_PYTHON_MODULE(types)
{
using namespace boost::python;

#define GPS_PY_EXPOSE_VECTOR(DIM, NAME)                                                 \
        class_<DIM::Vector>(NAME)                                                       \
                .def(init<DIM::Vector>())                                               \
                .def("norm", &DIM::Vector::norm)                                        \
                .def("__setitem__", &PyVectorHelper<DIM>::setVectorElem)                \
                .def("__getitem__", &PyVectorHelper<DIM>::getVectorElem)                \
                .def(self += self)                                                      \
                .def(self -= self)                                                      \
                .def(self + self)   /*Not working!!!*/                                  \
                .def(self - self)   /*Not working!!!*/                                  \
                .def("toStr", &PyVectorHelper<DIM>::toPyString)                         \
                .def("__str__", &PyVectorHelper<DIM>::toStdString)                      \
        ;

GPS_PY_EXPOSE_VECTOR(D2, "Vector2D")
GPS_PY_EXPOSE_VECTOR(D3, "Vector3D")
}

上面的代码运行正常,除了当我在Python中尝试使用“+”或“-”运算符时,如下所示:

>>>import types
>>>v1 = types.Vector2D()
>>>v2 = types.Vector2D()
>>>v3 = v1 + v2                //ERROR!!!

出现的错误是:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: No to_python (by-value) converter found for C++ type: Eigen::CwiseBinaryOp<Eigen::internal::scalar_sum_op<long double>, Eigen::Matrix<long double, 2, 1, 0, 2, 1> const, Eigen::Matrix<long double, 2, 1, 0, 2, 1> const>

我按照这个教程进行操作,但在网上没有找到与这个错误相关的信息。

任何帮助都非常欢迎!提前谢谢。

在Luc Danton的回答后编辑

感谢Luc Danton的回答,我尝试了你说的方法,但没有成功。这是我得到的错误:

error: no matching function for call to ‘boost::python::class_<Eigen::Matrix<long double, 2, 1, 0, 2, 1>, boost::python::detail::not_specified, boost::python::detail::not_specified, boost::python::detail::not_specified>::def(boost::python::detail::operator_<(boost::python::detail::operator_id)0u, boost::python::self_ns::self_t, boost::python::self_ns::self_t>, boost::python::return_value_policy<boost::python::to_python_value<Eigen::Matrix<long double, 2, 1, 0, 2, 1> >, boost::python::default_call_policies>)’

不过,感谢你的评论,我想到一个主意:写一个辅助函数来执行加法,并转换Eigen的“表达式对象”(这是表示运算符的私有类型)。这个函数是:

template <typename TDim>
struct PyVectorHelper
{
    //...
    static typename TDim::Vector sum(typename TDim::Vector const &v1, typename TDim::Vector const &v2)
    {
        return v1 + v2;
    }
    //...
};

然后

.def("__add__", &PyVectorHelper<DIM>::sum)

这样做效果很好,正如我想要的那样,因为PyVectorHelper<DIM>::sum返回一个DIM::Vector。问题是,几乎所有Eigen重载的运算符都返回一个表达式对象,这意味着我必须为几乎所有运算符实现类似的函数!这既不方便也不实际 :P。

也许仍然可以使用return_value_policyto_python_value,这样我就可以避免繁琐的运算符重写?

1 个回答

3

看起来这个矩阵/向量库使用了表达式模板。在这种情况下,正如你所提到的,operator+ 并不是返回一个向量,而是返回一个表示这个操作的私有类型。这个返回的值被困在C++的环境中,无法传递到Python,因为这个私有类型没有被注册。

我对Boost.Python不太熟悉,但我觉得你可以通过使用返回值策略来解决你的问题。传递一个 return_value_policy<to_python_value<DIM::Vector> > 作为策略,会强制将返回的类型转换为你已经注册的 DIM::Vector。这看起来像这样:

.def(self + self, return_value_policy<to_python_value<DIM::Vector> >())

撰写回答