The Python Oracle

Boost::Python, converting tuple to Python works, vector<tuple> does not

This video explains
Boost::Python, converting tuple to Python works, vector does not

--

Become part of the top 3% of the developers by applying to Toptal
https://topt.al/25cXVn

--

Music by Eric Matyas
https://www.soundimage.org
Track title: Realization

--

Chapters
00:00 Question
02:11 Accepted answer (Score 2)
04:12 Thank you

--

Full question
https://stackoverflow.com/questions/4218...

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Tags
#python #c++ #boost #tuples #boostpython

#avk47



ACCEPTED ANSWER

Score 2


TupleToPython registers C++-to-Python converters and Python-to-C++ converters. This is fine.

On the other hand, you want your vector elements to be returned by reference. But there's nothing on the Python side that can serve as a reference to your tuple. A converted-to-Python tuple may hold the same values, but it is completely detached from the original C++ tuple.

It looks like in order to export a tuple by reference, one would need to create an indexing suite for it, rather than to/from-Python converters. I have never done that and cannot guarantee it will work.

Here's how one could expose a tuple as a minimal tuple-like Python object (with only len() and indexing). First define some helper functions:

template <typename A>
int tuple_length(const A&)
{
    return std::tuple_size<A>::value;
}

template <int cidx, typename ... A>
typename std::enable_if<cidx >= sizeof...(A), boost::python::object>::type
get_tuple_item_(const std::tuple<A...>& a, int idx, void* = nullptr)
{
    throw std::out_of_range{"Ur outta range buddy"};
}

template <int cidx, typename ... A, typename = std::enable_if<(cidx < sizeof ...(A))>>
typename std::enable_if<cidx < sizeof...(A), boost::python::object>::type
get_tuple_item_(const std::tuple<A...>& a, int idx, int = 42)
{
    if (idx == cidx)
        return boost::python::object{std::get<cidx>(a)};
    else
        return get_tuple_item_<cidx+1>(a, idx);
};

template <typename A>
boost::python::object get_tuple_item(const A& a, int index)
{
    return get_tuple_item_<0>(a, index);
}

Then expose specific tuples:

using T1 = std::tuple<int, double, std::string>;
using T2 = std::tuple<std::string, int>;

BOOST_PYTHON_MODULE(z)
{
    using namespace boost::python;

    class_<T1>("T1", init<int, double, std::string>())
      .def("__len__", &tuple_length<T1>)
      .def("__getitem__", &get_tuple_item<T1>);

    class_<T2>("T2", init<std::string, int>())
      .def("__len__", &tuple_length<T2>)
      .def("__getitem__", &get_tuple_item<T2>);
}

Note these quasi-tuples, unlike real Python tuples, are mutable (via C++). Because of tuple immutability, exporting via converters and NoProxy looks like a viable alternative to this.