Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Boost.Python - How to return by reference?

I'm using Boost.Python to create Python modules from C++ classes. And I ran into a problem with references.

Condider the following case where I have a class Foo with overloaded get methods that can either return by value or reference.

Specifying that the return by value should be used was easy once I typedefed a signature. But I think it should be possible return a reference as well by using a return_value_policy. However, using what seemed appropriate (doc); return_value_policy<reference_existing_object> did not seem to work.

Have I misunderstood what it does?

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
        ;
}

Note: I know it could be dangerous to reference existing object without life-time managing.

Update:
It looks like it works for objects but not basic data types.
Take this revised example:

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);
        ;
}

Which in a test gave the expected result:

>>> foo1 = Foo(123)
>>> foo1.get()
123.0
>>> foo2 = foo1.get_self()
>>> foo2.set(1)
>>> foo1.get()
1.0
>>> id(foo1) == id(foo2)
False
like image 792
mandrake Avatar asked Oct 15 '09 08:10

mandrake


1 Answers

In Python, there's the concept of immutable types. An immutable type can't have its value changed. Examples of built-in immutable types are int, float and str.

Having said that, you can't do what you want with boost::python, because Python itself does not allow you to change the value of the float returned by the function.

Your second example shows one solution, another would be to create thin-wrappers and exposing that:

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

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

Which is a better solution than having to change the original C++ class.

like image 66
Bruno Oliveira Avatar answered Oct 06 '22 23:10

Bruno Oliveira