Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a new tuple type from an old one and a type in boost?

Tags:

c++

tuples

boost

I have a tuple type. I want to add a element type in it to get a new tuple type. I can do it like

decltype tuple_cat(MyTuple, std::tuple<MyType>())

However, I don't find tuple_cat in boost::tuple, how to do it in boost?

like image 869
user1899020 Avatar asked Jul 14 '13 20:07

user1899020


People also ask

How to create a tuple of tuples in Python?

Creating a Tuple A tuple in Python can be created by enclosing all the comma-separated elements inside the parenthesis (). Elements of the tuple are immutable and ordered. It allows duplicate values and can have any number of elements.

Can tuples have different data types?

A tuple can have any number of items and they may be of different types (integer, float, list, string, etc.). A tuple can also be created without using parentheses.


2 Answers

I assume you want all this in compile time.

Here is the general explanation: concatening tuples is similar to concatening lists or arrays, is that the algorithm is the same. Here, given tuples a and b, I choosed to move the last element of a to the beginning of b, and repeat until a is empty.

First: base structures. The following structure keeps a parameter pack. It can be anything, for example a tuple:

template<typename... T>
struct pack
{
    static const unsigned int size = sizeof...(T);
};

Note that the size of the pack is stored inside it. It is not mandatory, but it is convenient for the explanation. Boost uses the struct boost::tuples::length<T>::value (which is more verbose).

To access an element at i-th position, we use a structure similar to boost::tuples::element<n, T>:

// Get i-th element of parameter pack
// AKA 'implementation'
// Principle: the element i is the first element of the sub-array starting at indice i-1
template<int n, typename F, typename... T>
struct element_at : public element_at<n-1, T...>
{
};

template<typename F, typename... T>
struct element_at<0, F, T...>
{
    typedef F type;
};

// Get i-th element of pack
// AKA 'interface' for the 'pack' structure
template<int n, typename P>
struct element
{
};

template<int n, typename... T>
struct element<n, pack<T...>>
{
    typedef typename element_at<n, T...>::type type;
};

Now, we must use a low-level operation which is adding one element to a side of a pack (adding at left or at right). Here adding at left is choosed, but it is not the only choice:

// Concat at left (only for structure 'pack')
template<typename a, typename b>
struct tuple_concat_left
{
};

template<typename a, typename... b>
struct tuple_concat_left<a, pack<b...>>
{
    typedef pack<a, b...> type;
};

For templates, a is not changed, and instead we use an indice to know what element to add. The inheritance define a 'type' typedef which is the concatenation of all indices after n and the other tuple (not including n, and in order). We just have to concatenate at left the element at indice n.

// Concat 2 tuples
template<typename a, typename b, int n = 0, bool ok = (n < a::size)>
struct tuple_concat : public tuple_concat<a, b, n+1>
{
    typedef typename tuple_concat_left<
        typename element<n, a>::type,
        typename tuple_concat<a, b, n+1>::type
    >::type type;
};

template<typename a, typename b, int n>
struct tuple_concat<a, b, n, false>
{
    typedef b type;
};

And that's it! Live example here.

Now, for tuple specifics: you noticed I didn't used boost::tuple nor std::tuple. That is because a lot of implementations of boost tuplesdo not have access to variadic templates, so a fixed number of template parameters is used (they default to boost::tuples::null_type). Putting this directly with variadic templates is a headache, thus the need to have another abstraction.

I also assumed that you can use C++11 (with the decltype in your question). Concatening 2 tuples in C++03 is possible, but more repetitive and boring.

You can convert a pack to a tuple really easily: just change the pack definition to:

template<typename... T>
struct pack
{
    static const unsigned int size = sizeof...(T);
    typedef boost::tuple<T...> to_tuple; // < convert this pack to a boost::tuple
};
like image 150
Synxis Avatar answered Sep 18 '22 22:09

Synxis


C++14 offers a library to generate a sequence of integers at compile type. This helps manipulating static sequences like tuples and arrays (example). An integer sequence can be obtained

template<size_t... Ints>
struct integer_sequence {};

template<size_t Size, size_t... Ints>
struct implementation : implementation<Size-1, Size-1, Ints...> {};

template<size_t... Ints>
struct implementation<0, Ints...>
{
    typedef integer_sequence<Ints...> type;
};

template<class... T>
using index_sequence_for = typename implementation<sizeof...(T)>::type;

To concat MyTuple and MyType you can write the simple functions:

template<typename X, typename Tuple, size_t... Ints>
auto concat(X x, Tuple t, integer_sequence<Ints...>)
    -> decltype( std::make_tuple(x, std::get<Ints>(t)...) )
{
    return std::make_tuple(x, std::get<Ints>(t)...);
}

template<typename X, typename... T>
std::tuple<X, T...> concat(X x, std::tuple<T...> t)
{
    return concat(x, t, index_sequence_for<T...>());
}

concat(MyType, MyTuple);
like image 41
a.lasram Avatar answered Sep 19 '22 22:09

a.lasram