Boost.Python 不匹配签名左值

Boost.Python did not match signature lvalue

本文关键字:Python 不匹配 Boost      更新时间:2023-10-16

我有一个类Foo,其中包含一个我想公开的成员x,但通过 getter 函数而不是属性。我刚刚发现了make_getter,所以我想我会试一试:

#include <boost/python.hpp>    
namespace py = boost::python;
struct Base {
    int x;
};
struct Foo : Base {
    Foo(int i): Base{i} { }
};
BOOST_PYTHON_MODULE(Foo)
{
    py::class_<Foo>("Foo", py::init<int>())
        .def_readonly("x", &Foo::x)
        .def("getX", py::make_getter(&Foo::x))
        ;
}

但是,这将失败:

>>> import Foo
>>> f = Foo.Foo(42)
>>> f.x
42
>>> f.getX()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
    Foo.getX(Foo)
did not match C++ signature:
    getX(Base {lvalue})
>>> 

这个错误到底意味着什么?显然签名匹配!我该如何解决这个问题?

如果您仔细检查ArgumentError异常,问题是,您正在使用Foo调用getX()

>>> f.getX()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
    Foo.getX(Foo)

而您尝试访问的数据成员实际上是 Base 的成员:

did not match C++ signature:
    getX(Base {lvalue})

Boost.Python 需要执行从左值Foo到左值Base的转换,而你实际上还没有告诉 Boost 它可以做到这一点。问题最终源于&Base::xint Base::*而不是int Foo::*,所以boost::python::make_getter中的模板推导会生成一个采用Base而不是Foo的函数。

最简单的解决方案是确保将正确的指向成员的指针传递给make_getter

BOOST_PYTHON_MODULE(Foo)
{
    py::class_<Foo>("Foo", py::init<int>())
        .def_readonly("x", &Foo::x)
        .def("getX", py::make_getter(static_cast<int Foo::*>(&Foo::x)))
        ;
}

有了这个演员表,一切正常:

>>> from Foo import Foo
>>> Foo(4).getX()
4

不过这有点乏味,所以你可以写一个快速的方法/宏来为你做这件事:

template <typename Derived, typename Base, typename T>
T Derived::* as_derived(T Base::*member) {
    return static_cast<T Derived::*>(member);
}
#define AS_DERIVED(CLS, FIELD) as_derived<CLS>(&CLS::FIELD)

你可以用它做:

BOOST_PYTHON_MODULE(Foo)
{
    py::class_<Foo>("Foo", py::init<int>())
        .def_readonly("x", &Foo::x)
        .def("getX", py::make_getter(AS_DERIVED(Foo, x)))
        ;
}
<小时 />

或者,你可以直接告诉Boost.Python层次结构:

BOOST_PYTHON_MODULE(Foo)
{
    py::class_<Base>("Base", py::no_init)
        .def("getX", py::make_getter(&Base::x))
        ;
    py::class_<Foo, py::bases<Base>>("Foo", py::init<int>())
        .def_readonly("x", &Foo::x)
        ;
}

这样,Foo继承了getX(),一切都很好。