[C++-sig] Custom from-python converter (almost working)

pyplusplus at assumetheposition.nl pyplusplus at assumetheposition.nl
Fri Dec 5 13:39:23 CET 2008


I'm trying to add some custom from-python converters to my bindings (see
code at the end of the mail).
These basically should convert Python 3-tuples of floats to a C++ vec3f
instance. I can't seem to find reference docs on classes related to
conversion, e.g. boost::python::converter::registry, so after some
googling and experimenting I think the code at the end of this mail is a
pretty clean implementation of what I want, and it almost fully works:

Python 2.4.3 (#1, Jun 13 2006, 16:41:18)
[GCC 4.0.2 20051125 (Red Hat 4.0.2-8)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import doh
>>> doh.f((1,2,3))
1.000000, 2.000000, 3.000000
>>> doh.p((1,2,3))
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
Boost.Python.ArgumentError: Python argument types in
    doh.p(tuple)
did not match C++ signature:
    p(vec3f*)
>>>

So the implicit conversion to a pointer to a vec3f doesn't work. After
some more googling I came across the thread [1], which seemed to suggest
that there is another way of registering a converter, i.e.

"""
    Don't try to convert to pointers; there are no pointers in
    Boost.Python; only lvalues and rvalues ;-)

      void* extract_foo(PyObject* op)
      {
          return FooObject_ptr(op);
      }

      boost::python::converter::registry::insert(
                &extractor_foo, boost::python::type_id<FOO>());
"""

In the February 2002 progress report [2] (does that reflect the current
situation in 1.37 w.r.t. to/from-python converters, b.t.w?) it says:

"""There are basically two categories of from_python conversions:
those which lvalues stored within or held by the Python object
(essentially extractions), like what happens when an instance of a
C++ class exposed with class_ is used as the target of a wrapped
member function), and those in which a new rvalue gets created, as
when a Python Float is converted to a C++ complex<double> or a Python
tuple is converted to a C++ std::vector<>"""

It seems my non-working case of conversion to a vec3f* is of the latter
category.

Can I assume that the two different ways of registering a converter
reflect these categories?

If so, then I don't see a way to make this work, as I need actual
conversion, not extraction, i.e. I need to create a new object that
doesn't exist yet. Or should I register my converter in both ways to the
registry?

Any help is greatly appreciated,

Regards,
Paul

[1] http://mail.python.org/pipermail/cplusplus-sig/2006-November/011269.html
[2] http://www.boost.org/doc/libs/1_37_0/libs/python/doc/v2/feb2002.html




#include <boost/python.hpp>

namespace bp = boost::python;

struct vec3f
{
    vec3f(): x(0), y(0), z(0) {}

    vec3f(float xx, float yy, float zz)
    {
        x = xx; y = yy; z = zz;
    }

    float x, y, z;
};

struct convert_py_tuple_to_vec3f
{
    convert_py_tuple_to_vec3f()
    {
        bp::converter::registry::push_back(
            &convertible,
            &construct,
            bp::type_id<vec3f>());
    }

    // Check if given Python object is convertible to a vec3f.
    // If so, return obj, otherwise return 0
    static void* convertible(PyObject* obj)
    {
        if (PyTuple_Check(obj))
            return obj;

        return 0;
    }

    // Construct a vec3f object from the given Python object, and
    // store it in the stage1 (?) data.
    static void construct(PyObject* obj,
bp::converter::rvalue_from_python_stage1_data* data)
    {
        // Fill in values
        float x, y, z;
        if (!PyArg_ParseTuple(obj, "fff", &x, &y, &z))
        {
            // Raise exception, error will have been set by PyArg_ParseTuple
            boost::python::throw_error_already_set();
        }

        typedef bp::converter::rvalue_from_python_storage<vec3f>
vec3f_storage;
        void* const storage =
reinterpret_cast<vec3f_storage*>(data)->storage.bytes;
        vec3f *v = new (storage) vec3f(x, y, z);

        data->convertible = storage;
    }
};

void
f(vec3f v)
{
    printf("%f, %f, %f\n", v.x, v.y, v.z);
}

void
p(vec3f *v)
{
    printf("%f, %f, %f\n", v->x, v->y, v->z);
}

BOOST_PYTHON_MODULE(doh)
{
    bp::class_< vec3f >("vec3f")
        .def(bp::init<float, float, float>())
    ;

    bp::def("f", &f);
    bp::def("p", &p);

    convert_py_tuple_to_vec3f();
}




More information about the Cplusplus-sig mailing list