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 <tuple>
#include <string>
#include <iostream>
using std::cout;
using std::endl;
using std::string;
#include <boost/python/tuple.hpp>
#include <boost/python/extract.hpp>
#include <boost/python/object.hpp>
#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/def.hpp>
using boost::python::extract;
template<int ...> struct seq{};
template<int N, int ...S> struct gens : gens<N-1, N-1, S...>{};
template<int ...S> struct gens<0, S...> {typedef seq<S...> type;};
template <typename ...Args>
struct cpptuple2pytuple_wrapper
{
std::tuple<Args...> params;
cpptuple2pytuple_wrapper(const std::tuple<Args...>& _params):params(_params){}
boost::python::tuple delayed_dispatch()
{
return callFunc(typename gens<sizeof...(Args)>::type());
}
template<int ...S>
boost::python::tuple callFunc(seq<S...>)
{
return boost::python::make_tuple(std::get<S>(params) ...);
}
};
template <typename ...Args>
struct pytuple2cpptuple_wrapper
{
boost::python::tuple params;
pytuple2cpptuple_wrapper(const boost::python::tuple& _params):params(_params){}
std::tuple<Args...> delayed_dispatch()
{
return callFunc(typename gens<sizeof...(Args)>::type());
}
template<int ...S>
std::tuple<Args...> callFunc(seq<S...>)
{
return std::make_tuple((static_cast<Args>(extract<Args>(params[S])))...);
}
};
// Convert (C++) tuple to (Python) tuple as PyObject*.
template<typename ... Args> PyObject* cpptuple2pytuple(const std::tuple<Args...>& t)
{
cpptuple2pytuple_wrapper<Args...> wrapper(t);
boost::python::tuple bpt = wrapper.delayed_dispatch();
return boost::python::incref(boost::python::object(bpt).ptr());
}
// Convert (Python) tuple to (C++) tuple.
template<typename ... Args> std::tuple<Args...> pytuple2cpptuple(PyObject* obj)
{
boost::python::tuple tup(boost::python::borrowed(obj));
pytuple2cpptuple_wrapper<Args...> wrapper(tup);
std::tuple<Args...> bpt = wrapper.delayed_dispatch();
return bpt;
}
template<typename ... Args>
struct cpptuple_to_python_tuple
{
static PyObject* convert(const std::tuple<Args...>& t)
{
return cpptuple2pytuple<Args...>(t);
}
};
template<typename ... Args>
struct cpptuple_from_python_tuple
{
cpptuple_from_python_tuple()
{
boost::python::converter::registry::push_back(
&convertible,
&construct,
boost::python::type_id<std::tuple<Args...> >());
}
static void* convertible(PyObject* obj_ptr)
{
if (!PyTuple_CheckExact(obj_ptr)) return 0;
return obj_ptr;
}
static void construct(
PyObject* obj_ptr,
boost::python::converter::rvalue_from_python_stage1_data* data)
{
void* storage = (
(boost::python::converter::rvalue_from_python_storage<std::tuple<Args...> >*)
data)->storage.bytes;
new (storage) std::tuple<Args...>(pytuple2cpptuple<Args...>(obj_ptr));
data->convertible = storage;
}
};
template<typename ...Args> void create_tuple_converter()
{
boost::python::to_python_converter<std::tuple<Args...>, cpptuple_to_python_tuple<Args...> >();
cpptuple_from_python_tuple<Args...>();
}
void export_cpptuple_conv()
{
create_tuple_converter<int, float>();
create_tuple_converter<int, double, string>();
}
std::tuple<int, float> tupid1(std::tuple<int, float> t){return t;}
std::tuple<int, double, string> tupid2(std::tuple<int, double, string> t){return t;}
BOOST_PYTHON_MODULE(bptuple)
{
export_cpptuple_conv();
boost::python::def("tupid1", tupid1);
boost::python::def("tupid2", tupid2);
}
This can be compiled with the following SConstruct file.
SConstruct
#!/usr/bin/python
import commands, glob, os
def pyversion():
pystr = commands.getoutput('python -V')
version = pystr.split(' ')[1]
major, minor = version.split('.')[:2]
return major + '.' + minor
boost_python_env = Environment(
#CXX="g++",
CXX="g++-4.7",
CPPPATH=["/usr/include/python"+pyversion()],
CXXFLAGS="-ftemplate-depth-100 -fno-strict-aliasing -ansi -Wextra -Wall -Werror -Wno-unused-function -g -O3 -std=c++11",
CPPDEFINES=['BOOST_PYTHON_DYNAMIC_LIB'],
LIBPATH=["/usr/lib/python"+pyversion()+"/config"],
LIBS=["python"+pyversion(), "m", "boost_python"],
SHLIBPREFIX="", #gets rid of lib prefix
)
boost_python_env.SharedLibrary(target="bptuple", source=["cpptup_conv_pif.cpp"])
The compilation looks like this on my machine:
g++-4.7 -o cpptup_conv_pif.os -c -ftemplate-depth-100 -fno-strict-aliasing -ansi -Wextra -Wall -Werror -Wno-unused-function -g -O3 -std=c++11 -fPIC -DBOOST_PYTHON_DYNAMIC_LIB -I/usr/include/python2.6 cpptup_conv_pif.cpp g++-4.7 -o bptuple.so -shared cpptup_conv_pif.os -L/usr/lib/python2.6/config -lblitz -lRmath -lpython2.6 -lm -lboost_python
Usage:
In [1]: import bptuple In [2]: bptuple.tupid1((1,1.2)) Out[2]: (1, 1.2000000476837158) In [3]: bptuple.tupid2((1,1.2, "foo")) Out[3]: (1, 1.2, 'foo')