I've been using Boost::Python for a while, and everything always turned out ok. However yesterday I was trying to find out why a particular type I thought I had registered (a tuple) was giving me errors when I was trying to access it from Python.
Turns out that while the tuple was actually registered, when trying to access it through an std::vector
wrapped via the vector_indexing_suite
this is not enough anymore.
I was wondering, why is it not working? Is there any way to make this work? Should I try to wrap the vector by hand?
Below is my MVE:
#include <tuple>
#include <vector>
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
template <typename T>
struct TupleToPython {
TupleToPython() {
boost::python::to_python_converter<T, TupleToPython<T>>();
}
template<int...>
struct sequence {};
template<int N, int... S>
struct generator : generator<N-1, N-1, S...> { };
template<int... S>
struct generator<0, S...> {
using type = sequence<S...>;
};
template <int... I>
static boost::python::tuple boostConvertImpl(const T& t, sequence<I...>) {
return boost::python::make_tuple(std::get<I>(t)...);
}
template <typename... Args>
static boost::python::tuple boostConvert(const std::tuple<Args...> & t) {
return boostConvertImpl(t, typename generator<sizeof...(Args)>::type());
}
static PyObject* convert(const T& t) {
return boost::python::incref(boostConvert(t).ptr());
}
};
using MyTuple = std::tuple<int>;
using Tuples = std::vector<MyTuple>;
MyTuple makeMyTuple() {
return MyTuple();
}
Tuples makeTuples() {
return Tuples{MyTuple()};
}
BOOST_PYTHON_MODULE(h)
{
using namespace boost::python;
TupleToPython<MyTuple>();
def("makeMyTuple", makeMyTuple);
class_<std::vector<MyTuple>>{"Tuples"}
.def(vector_indexing_suite<std::vector<MyTuple>>());
def("makeTuples", makeTuples);
}
Accessing the resulting .so
via Python results in:
>>> print makeMyTuple()
(0,)
>>> print makeTuples()[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: No Python class registered for C++ class std::tuple<int>
>>>
EDIT:
I've realized that the error does not happen if the vector_indexing_suite
is used with the NoProxy
parameter set to true. However, I'd prefer if this wasn't necessary, as it makes the exported classes unintuitive in Python.
Tuples makeTuples() { return Tuples{MyTuple()}; }
beTuples makeTuples() { return Tuples(); }
instead ? – ContrabandistmakeTuples()[0]
without triggering theout_of_bounds
error, since then the vector would be empty and you wouldn't see the tuple error. – Preciado