user2209008
user2209008

Reputation:

Boost Python def member function that calls static function with self as only argument

So I've gotten to the point where I need to start wrapping a lot of functionality of GLM into my embedded Python module.

One of the nice things about GLM is that it defines all it's typical vector math functions (length, normalize etc.) as free-standing static functions. This works lovely when writing C++, but it's throwing a wrench in things when trying to wrap this functionality as part of a class definition.

I want to call len on my Vec3F class and have it call the correct overload of length while passing self as the first and only argument. The Boost.Python documentation is fairly opaque and there's not a lot of examples, so I don't even know if this is possible.

Implementing a length function in the vec2 and vec3 structs is not possible, since these structs are in another project; I have to deal with the structs as-is.

Any help is greatly appreciated!

Python Interface

Vec3F v
v.x = 1.0
v.y = 1.0
v.z = 1.0
print len(v) # should print 1.732050...

Module Definition

template<typename T>
struct vec2
{
    T x;
    T y;
};

template<typename T>
struct vec3
{
    T x;
    T y;
    T z;
};

template<typename T>
float length(vec2& v)
{
    return sqrt(v.x * v.x + v.y * v.y);
}

template<typename T>
float length(vec3& v)
{
    return sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}

BOOST_PYTHON_MODULE(naga)
{
    class_<vec2<float>, "Vec2F")
        .def("x", &vec2<float>::x)
        .def("y", &vec2<float>::y)
        .def("__len__", /* ??? */); // not sure what needs to be here!

    class_<vec3<float>, "Vec3F")
        .def("x", &vec2<float>::x)
        .def("y", &vec2<float>::y)
        .def("__len__", /* ??? */);  // not sure what needs to be here!
}

Upvotes: 0

Views: 311

Answers (1)

doqtor
doqtor

Reputation: 8494

You will need a wrapper class but still __len__ is not going to return the correct result probably because the return value is converted into integer internally (I suspect __len__ must always return integer value). So you will need to expose it as len to get correct floating point return value.

namespace bp = boost::python;

template<typename T>
struct vec2
{
    T x;
    T y;
};

template<typename T>
struct vec3
{
    T x;
    T y;
    T z;
};

template <typename T>
struct vec2_wrapper : vec2<T>
{
    float length()
    {
        return sqrt(x * x + y * y);
    }
};

template <typename T>
struct vec3_wrapper : vec3<T>
{
    float length()
    {
        return sqrt(x * x + y * y + z * z);
    }
};

BOOST_PYTHON_MODULE(naga)
{
    bp::class_ < vec2_wrapper<float> >("Vec2F")
        .def_readwrite("x", &vec2_wrapper<float>::x)
        .def_readwrite("y", &vec2_wrapper<float>::y)
        .def("__len__", &vec2_wrapper<float>::length)
        .def("len", &vec2_wrapper<float>::length)
        ;

    bp::class_ < vec3_wrapper<float> >("Vec3F")
        .def_readwrite("x", &vec3_wrapper<float>::x)
        .def_readwrite("y", &vec3_wrapper<float>::y)
        .def_readwrite("z", &vec3_wrapper<float>::z)
        .def("__len__", &vec3_wrapper<float>::length)
        .def("len", &vec3_wrapper<float>::length)
        ;
}

Python test:

import naga as vec

v = vec.Vec3F()
v.x = 1.0
v.y = 1.0
v.z = 1.0
print "len(v):", len(v)
print "v.len():", v.len()

Output:

len(v): 1
v.len(): 1.73205077648 

Upvotes: 0

Related Questions