Boost.Python如何通过引用返回?

60 阅读2分钟

Boost.Python 是一个用于从 C++ 类创建 Python 模块的库。在使用 Boost.Python 时,遇到了一个有关引用的问题。

huake_00183_.jpg 考虑以下情况:有一个名为 Foo 的类,它具有重载的 get 方法,该方法可以按值或按引用返回。一旦对签名进行类型定义,指定按值返回就变得容易。但是,我认为也可以使用 return_value_policy 返回引用。然而,使用看似合适的 (文档); return_value_policy<reference_existing_object> 似乎不起作用。

这是不是误解了它的作用?

struct Foo {
    Foo(float x) { _x = x; }
    float& get() { return _x; }
    float  get() const { return _x; }
private:
    float _x;
};

// Wrapper code
BOOST_PYTHON_MODULE(my_module)
{
    using namespace boost::python;
    typedef float (Foo::*get_by_value)() const;
    typedef float& (Foo::*get_by_ref)();

    class_<Foo>("Foo", init<float>())
        .def("get", get_by_value(&Foo::get))
        .def("get_ref", get_by_ref(&Foo::get),
            return_value_policy<reference_existing_object>())//Doesn't work
        ;
}

注意:知道引用现有对象而没有生命周期管理可能是危险的。

更新: 看起来它适用于对象但不适用于基本数据类型。 采用这个修改后的示例:

struct Foo {
    Foo(float x) { _x = x; }
    float& get() { return _x; }
    float  get() const { return _x; }
    void set( float f ){ _x = f;}
    Foo& self(){return *this;}
private:
    float _x;
};

// Wrapper code
using namespace boost::python;
BOOST_PYTHON_MODULE(my_module)
{
    typedef float (Foo::*get_by_value)() const;

    class_<Foo>("Foo", init<float>())
        .def("get", get_by_value(&Foo::get))
        .def("get_self", &Foo::self,
            return_value_policy<reference_existing_object>())
        .def("set", &Foo::set);
        ;
}

在测试中给出了预期的结果:

>>> foo1 = Foo(123)
>>> foo1.get()
123.0
>>> foo2 = foo1.get_self()
>>> foo2.set(1)
>>> foo1.get()
1.0
>>> id(foo1) == id(foo2)
False

2、解决方案

答案 1:

在 Python 中,存在不可变类型。即值不可更改的类型。内置不可变类型的示例包括 int、float 和 str。

也就是说,无法使用 boost::python 来完成想要做的事情,因为 Python 本身不允许更改函数返回的 float 的值。

第二个示例展示了一种解决方案,另一种解决方案是创建薄封装器并公开它:

void Foo_set_x(Foo& self, float value) {
    self.get() = value;
}

class_<Foo>("Foo", init<float>())
    ...
    .def("set", &Foo_set_x);
;

这比必须更改原始 C++ 类更好的解决方案。

答案 2:

想要返回内部引用。以前使用过它来做类似的事情。 编辑:最新文档

// Source: https://www.boost.org/doc/libs/1_65_1/libs/python/doc/v2/reference/existing/existingvaluepolicy.html

void modify_existing_value() {
    using namespace boost::python;

    struct A {
        int x, y;
    };

    void set_x(A& a, int x) { a.x = x; }

    typedef A& A_ref;
    class_<A>("A", init<int, int>())
        .def_readwrite("x", &A::x)
        .def_readwrite("y", &A::y)
        .def("get_ref", return_value_policy<return_by_value>())
        .def("get_ref2", return_by_value())
        .def("set_x", set_x, return_internal_reference<>())
        ;
}

这些代码示例演示了如何通过引用返回。