【问题标题】:Boost.Python - How to return by reference?Boost.Python - 如何通过引用返回?
【发布时间】:2010-12-06 23:06:23
【问题描述】:

我正在使用 Boost.Python 从 C++ 类创建 Python 模块。我遇到了引用问题。

考虑以下情况,我有一个类 Foo,它具有可以按值或引用返回的重载 get 方法。

一旦我定义了一个签名,就很容易指定应该使用按值返回。但我认为使用return_value_policy 也应该可以返回引用。但是,使用看似合适的方法 (doc); 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

【问题讨论】:

    标签: python boost boost-python


    【解决方案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】:

      我想你想要return internal reference。我以前用它做类似的事情。

      编辑:Latest doc

      【讨论】:

      • 同样的交易。它适用于对象,但不适用于基本数据类型。
      • 文档链接适用于版本 1.38,截至本评论该版本非常旧(当前版本为 1.61)
      【解决方案3】:

      我对 Boost.Python 了解不多,所以我可能会误解这个问题,在这种情况下,这完全没有帮助。但这里是:

      在 Python 中,您无法在按引用或按值返回之间进行选择,这种区别在 Python 中没有意义。我发现最容易将其视为通过引用处理的所有内容。

      你只有对象,你有这些对象的名字。所以

      foo = "ryiuy"
      

      创建字符串对象“ryiuy”,然后让您使用名称“foo”引用该字符串对象。所以在 Python 中,当你传递一些东西时,你就传递了那个对象。没有这样的“价值”,所以你不能传递价值。但话又说回来,这也是一个有效的观点,即也没有引用,只有对象及其名称。

      所以我猜答案是,当您在 C 中获得引用时,您需要将引用传递给 Python 中引用的对象。当您在 C 中获得一个值时,您需要将一个对您从该值创建的对象的引用传递给 Python。

      【讨论】:

      • 是的,我知道 Python 中的一切都是引用。当我使用返回“按值”的 Boost.Python 创建 python 方法时,它返回该类型的副本。我想做的是复制,而是创建对同一实例的引用。
      【解决方案4】:

      您确定正在复制 c++ 对象吗?您每次都会得到一个新的 python 对象,但它引用了相同的 c++ 对象。您如何确定该对象已被复制?

      【讨论】:

      • 当我尝试使用引用时它不会编译。至于如何确定它是引用还是副本很容易。创建两个对您认为是同一个对象的引用,修改一个并查看更改是否出现在另一个中。 (使用 id(obj) 不起作用)。
      • 是的,要求检查您没有使用 id()。编译错误是什么?
      • 这是一个 boost::STATIC_ASSERTION_FAILURE,表示我所做的事情要么不受支持,要么非法。我只是不知道是什么。
      • 我更新了我的示例,参考了 Foo 而不是 float,这很有效。
      猜你喜欢
      • 2016-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多