HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Boost Python converter for std::tuple

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
stdtupleforpythonboostconverter

Problem

I could not find a Boost Python converter which converts std::tuple, so I wrote one.

This was tested with a g++ 4.7 snapshot on Debian squeeze. It uses C++ 11 features,
specifically variadic templates, so requires a recent compiler. This is based on the answer to ""unpacking" a tuple to call a matching function pointer".

It is rather long, and could probably be improved/simplified. In particular, the two *_wrapper structures look redundant. How do I replace the cpptuple2pytuple_wrapper and pytuple2cpptuple_wrapper with one struct, and perhaps template on the differences using
something like type traits, if that is possible? Any other simplifications would also be interesting.

cpptup_conv_pif.cpp

```
#include
#include
#include
using std::cout;
using std::endl;
using std::string;

#include
#include
#include
#include
#include
#include
using boost::python::extract;

template struct seq{};
template struct gens : gens{};
template struct gens {typedef seq type;};

template
struct cpptuple2pytuple_wrapper
{
std::tuple params;
cpptuple2pytuple_wrapper(const std::tuple& _params):params(_params){}

boost::python::tuple delayed_dispatch()
{
return callFunc(typename gens::type());
}

template
boost::python::tuple callFunc(seq)
{
return boost::python::make_tuple(std::get(params) ...);
}
};

template
struct pytuple2cpptuple_wrapper
{
boost::python::tuple params;
pytuple2cpptuple_wrapper(const boost::python::tuple& _params):params(_params){}

std::tuple delayed_dispatch()
{
return callFunc(typename gens::type());
}

template
std::tuple callFunc(seq)
{
return std::make_tuple((static_cast(extract(params[S])))...);
}
};

// Convert (C++) tuple to (Python) tuple as PyObject*.
template PyObject* cpptuple2pytuple(const std::tuple& t)
{
cpptuple2pytuple_wrapper wrapper(t);
boost::python::tuple bpt = wrapper.delayed_dispatch();
return boost::python::incref(boost::python::object(bpt).ptr());
}

// Convert (Pytho

Solution

One thing that's not immediately clear to me is if the tuple2tuple_wrapper's are supposed to be internal helpers, or actually part of the interface. They seem to be instantiated only once each in the code shown, both immediately followed by a call to their delayed_dispatch.

If these are only internal helpers, you could trim them down a lot. You'll still need a templated struct with a templated member function, since you have two template parameter packs. But you're not actually using delayed_dispatch as it's name suggests, so that can go. And then the params member and ctor can go as well, with params becoming an extra parameter to callFunc. Taking cpptuple2pytuple_wrapper as an example:

template
struct cpp_to_py_wrapper { // I suck at typing typle
    template
    boost::python::tuple callFunc(std::tuple params, seq) {
        return boost::python::make_tuple(std::get(params)...);
    }
};


cpptuple2pytuple then becomes:

template PyObject* cpptuple2pytuple(const std::tuple& t)
{
    boost::python::tuple bpt = cpp_to_py_wrapper().callFunc(t, typename gens::type());
    return boost::python::incref(boost::python::object(bpt).ptr());
}


Now you're just left with two instances of callFunc, both of which show a similar composition. If we just abstract for a bit:

template
return_type callFunc(params_type params, seq)
{
    return construct_tuple(extract_tuple_value(params) ...);
}


Now, this is definitely something that can be implemented generically. However, if you're only using this here, I'd actually advise against it. Since you're only defining two conversions, hardcoding both overloads of callFunc will likely be less work than providing suitable implementations of construct_tuple and extract_tuple_value. A generic implementation will likely not pay off until you want to convert between more than three types. That's the point were the number of pair-wise combinations of N elements starts to grow faster than N itself.

Code Snippets

template<typename... Args>
struct cpp_to_py_wrapper { // I suck at typing typle
    template<int... S>
    boost::python::tuple callFunc(std::tuple<Args...> params, seq<S...>) {
        return boost::python::make_tuple(std::get<S>(params)...);
    }
};
template<typename ... Args> PyObject* cpptuple2pytuple(const std::tuple<Args...>& t)
{
    boost::python::tuple bpt = cpp_to_py_wrapper<Args...>().callFunc(t, typename gens<sizeof...(Args)>::type());
    return boost::python::incref(boost::python::object(bpt).ptr());
}
template<int ...S>
return_type callFunc(params_type params, seq<S...>)
{
    return construct_tuple<return_type>(extract_tuple_value<S, Args>(params) ...);
}

Context

StackExchange Code Review Q#9202, answer score: 3

Revisions (0)

No revisions yet.