Using c ++ tuple types as a key in a map

I am trying to map a collection of values ​​of different types into a continuous space. To do this (effectively) I need to know the total size of all elements and the offset of each one. Getting the full size if the collection is provided as a Tuple is easy. It also allows offsets to be precomputed. I am struggling with getting the offset by type. To simplify the example, I am assuming that the types inside the Tuple will be unique (in fact, the Tuple will have pairs of unique tags + a unique value). Here is my current attempt at not compiling:

#include <cstddef>
#include <iostream>
#include <tuple>

struct A
{
    std::size_t size()
    {
        return 3;
    }
};

struct B
{
    std::size_t size()
    {
        return 2;
    }
};

struct C
{
    std::size_t size()
    {
        return 4;
    }
};

template <typename Tuple>
struct Foo
{
    const Tuple& tuple_;
    std::array<int, std::tuple_size<Tuple>::value> array_;

    Foo(const Tuple& tuple) : tuple_(tuple)
    {
        std::cout << init() << '\n';
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////

    template <std::size_t INDEX = 0>
    typename std::enable_if<std::tuple_size<Tuple>::value == INDEX, std::size_t>::type
    init()
    {
        return 0;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////

    template <std::size_t INDEX = 0>
    typename std::enable_if<std::tuple_size<Tuple>::value != INDEX, std::size_t>::type
    init()
    {
        auto offset = init<INDEX + 1>();

        std::cout << "index: "<< INDEX << "; offset: " << offset << '\n';

        array_[INDEX] = offset;

        return offset + std::get<INDEX>(tuple_).size();
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////

    template <std::size_t INDEX = 0, typename T>
    typename std::enable_if<std::tuple_size<Tuple>::value == INDEX, std::size_t>::type
    offset(const T&)
    {
        return 0;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////

    template <std::size_t INDEX = 0, typename T>
    typename std::enable_if<(std::tuple_size<Tuple>::value != INDEX) && !std::is_same<typename std::tuple_element<INDEX, Tuple>::type, T>::value, std::size_t>::type
    offset(const T& t)
    {
        return offset<INDEX + 1>(t);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////

    template <std::size_t INDEX = 0, typename T>
    typename std::enable_if<(std::tuple_size<Tuple>::value != INDEX) && std::is_same<typename std::tuple_element<INDEX, Tuple>::type, T>::value, std::size_t>::type
    offset(const T&)
    {
        return array_[INDEX];
    }    
};

int main()
{
    A a;
    B b;
    C c;
    auto t = std::tie(a, b, c);
    using T = decltype(t);
    Foo<T> foo(t);

    std::cout << foo.offset(a) << '\n';
    //std::cout << foo.offset(b) << '\n';
    //std::cout << foo.offset(c) << '\n';
}

      

Compiler error:

In file included from prog.cpp:3:0:
/usr/include/c++/4.8/tuple: In instantiation of β€˜struct std::tuple_element<1u, std::tuple<C&> >’:
/usr/include/c++/4.8/tuple:680:12:   recursively required from β€˜struct std::tuple_element<2u, std::tuple<B&, C&> >’
/usr/include/c++/4.8/tuple:680:12:   required from β€˜struct std::tuple_element<3u, std::tuple<A&, B&, C&> >’
prog.cpp:79:22:   recursively required from β€˜typename std::enable_if<((std::tuple_size<_Tp>::value != INDEX) && (! std::is_same<typename std::tuple_element<INDEX, Tuple>::type, T>::value)), unsigned int>::type Foo<Tuple>::offset(const T&) [with unsigned int INDEX = 1u; T = A; Tuple = std::tuple<A&, B&, C&>; typename std::enable_if<((std::tuple_size<_Tp>::value != INDEX) && (! std::is_same<typename std::tuple_element<INDEX, Tuple>::type, T>::value)), unsigned int>::type = unsigned int]’
prog.cpp:79:22:   required from β€˜typename std::enable_if<((std::tuple_size<_Tp>::value != INDEX) && (! std::is_same<typename std::tuple_element<INDEX, Tuple>::type, T>::value)), unsigned int>::type Foo<Tuple>::offset(const T&) [with unsigned int INDEX = 0u; T = A; Tuple = std::tuple<A&, B&, C&>; typename std::enable_if<((std::tuple_size<_Tp>::value != INDEX) && (! std::is_same<typename std::tuple_element<INDEX, Tuple>::type, T>::value)), unsigned int>::type = unsigned int]’
prog.cpp:101:27:   required from here
/usr/include/c++/4.8/tuple:680:12: error: invalid use of incomplete type β€˜struct std::tuple_element<0u, std::tuple<> >’
     struct tuple_element<__i, tuple<_Head, _Tail...> >
            ^
In file included from /usr/include/c++/4.8/tuple:38:0,
                 from prog.cpp:3:
/usr/include/c++/4.8/utility:84:11: error: declaration of β€˜struct std::tuple_element<0u, std::tuple<> >’
     class tuple_element;
           ^

      

+3


source to share


2 answers


Your code is correct except for one problem.

The problem is what you create std::tuple

with a std::tie()

helper method, which results in a tuple of links - std::tuple<A&, B&, C&>

.

On the other hand, you have offset(const T& t)

where the inferred type T

can be just A

, B

or C

(that is, the reference is not part of the inferred type, only the type of the parameter). However, your condition is_same<tuple_element<...>::type, T>

always fails because one type is a reference while the other is not, eg. std::is_same<A&, A>

... To solve this problem, you must first decompose the type (or at least remove the reference) before it can be compared to another for equality.

Having said that, each of your conditions should look like this:



std::is_same<typename std::decay<typename std::tuple_element<INDEX, Tuple>::type>::type, T>::value
//           ~~~~~~~~~~~~~~~~~~~^                                               ~~~~~~^

      

or (C ++ 14):

std::is_same<std::decay_t<typename std::tuple_element<INDEX, Tuple>::type>::type>, T>::value
//           ~~~~~~~~~~~~^                                                      ^

      

DEMO

+2


source


It seems that you want something like:

template <typename T, typename Tuple> struct get_index;

template <typename T, typename... Ts>
struct get_index<T, std::tuple<T, Ts...>> : std::integral_constant<std::size_t, 0> {};

template <typename T, typename Tail, typename... Ts>
struct get_index<T, std::tuple<Tail, Ts...>> :
    std::integral_constant<std::size_t, 1 + get_index<T, std::tuple<Ts...>>::value> {};

      

And then replace your whole offset method with



template <typename T>
std::size_t
offset(const T&)
{
    return array_[get_index<T&, Tuple>::value];
}

      

A living example .

Note that in your example Tuple

there is std::tuple<A&, B&, C&>

, and you are testing for equality with A

...

+1


source







All Articles