Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Boost python getter/setter with the same name

I am wrapping C++ classes with boost-python and I am wondering is there is a better way to do it than what I am doing now.

The problem is that the classes have getters/setters that have the same name and there doesn't seem to be a painless way to wrap this with boost-python.

Here is a simplified version of the problem. Given this class:

#include <boost/python.hpp>

using namespace boost::python;

class Foo {
public:
    double
    x() const
    {
        return _x;
    }

    void
    x(const double new_x)
    {
        _x = new_x;
    }

private:
    double _x;
};

I would like to do something like:

BOOST_PYTHON_MODULE(foo)
{
    class_<Foo>("Foo", init<>())
        .add_property("x", &Foo::x, &Foo::x)
    ;
}

This doesn't work because boost-python can't figure out which version of the function to use.

In fact, you can't even do

.def("x", &Foo::x)

for the same reason.

I was re-reading the tutorial at boost.org and the section on overloading seemed super promising. Unfortunately it doesn't seem to be what I'm looking for.

In the overloading section, it mentions a BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS macro that works like this:

if there were another member function in Foo that took defaulted arguments:

void z(int i=42)
{
    std::cout << i << "\n";
}

you can then use the macro:

BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(z_member_overloads, z, 0, 1)

and then in the BOOST_PYTHON_MODULE:

.def("z", &Foo::z, z_member_overloads())

z_member_overloads lets you call def once and it will expose methods to python for both 0 arguments and 1 argument.

I was hoping that this would work for my x() and x(double val) getter/setter, but it doesn't work.

doing:

BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(x_member_overloads, x, 0, 1)
...
.def("x", &Foo::x, x_member_overloads())

doesn't compile:

error: no matching member function for call to 'def'

.def("x", &Foo::x, x_member_overloads())
~^~~

Question: So, is there another macro or something that can make this work?

For completeness, this is how I'm currently handling cases like this:

    .add_property(
        "x",
        make_function(
            [](Foo& foo) {
                return foo.x();
            },
            default_call_policies(),
            boost::mpl::vector<double, Foo&>()
        ),
        make_function(
            [](Foo& foo, const double val) {
                foo.x(val);
        },
        default_call_policies(),
        boost::mpl::vector<void, Foo&, double>()
        )
    )
like image 434
Stephen Avatar asked May 28 '17 20:05

Stephen


People also ask

Can getters and setters have the same name?

“Another thing to keep in mind when using getter (and setter) methods is that properties cannot share the same name as the getter/setter function.”

What is the more pythonic way to use getter and setter?

Getters and Setters in python are often used when: We use getters & setters to add validation logic around getting and setting a value. To avoid direct access of a class field i.e. private variables cannot be accessed directly or modified by external user.

Is @property a getter?

@property is used to get the value of a private attribute without using any getter methods. We have to put a line @property in front of the method where we return the private variable. To set the value of the private variable, we use @method_name.

What is the pythonic way to write getters and setters in python?

You can use the magic methods __getattribute__ and __setattr__ . Be aware that __getattr__ and __getattribute__ are not the same. __getattr__ is only invoked when the attribute is not found.


1 Answers

You can do this by casting to appropriate overload (untested):

class_<Foo>("Foo", init<>())
        .add_property("x", 
                      static_cast< double(Foo::*)() const >(&Foo::x), // getter
                      static_cast< void(Foo::*)(const double) >(&Foo::x)) // setter
    ;
like image 122
doqtor Avatar answered Sep 18 '22 11:09

doqtor