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;
^
source to share
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
// ~~~~~~~~~~~~^ ^
source to share
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];
}
Note that in your example Tuple
there is std::tuple<A&, B&, C&>
, and you are testing for equality with A
...
source to share