Get Index of a Tuple Element's Type

Get index of a tuple element's type?

template <class T, class Tuple>
struct Index;

template <class T, class... Types>
struct Index<T, std::tuple<T, Types...>> {
static const std::size_t value = 0;
};

template <class T, class U, class... Types>
struct Index<T, std::tuple<U, Types...>> {
static const std::size_t value = 1 + Index<T, std::tuple<Types...>>::value;
};

See it live at Coliru.

This implementation returns the index of the first occurrence of a given type. Asking for the index of a type that is not in the tuple results in a compile error (and a fairly ugly one at that).

How to get an element of type list by index

I want to avoid using tuples as type lists for use cases, that require
instantiation for passing a list like f(L{})

If you don't want to instanciate std::tuple but you're ok with it in
unevaluated contexts, you may take advantage of std::tuple_element to
implement your typeAt trait:

template <std::size_t I, typename T>
struct typeAt;

template <std::size_t I, typename... Args>
struct typeAt<I, type_list<Args...>> : std::tuple_element<I, std::tuple<Args...>> {};
// ^ let library authors do the work for you

using L = type_list<int, char, float, double>;
using T = typename typeAt<2, L>::type;

static_assert(std::is_same<T, float>::value, "");

How to extract all tuple elements of given type(s) into new tuple

Only C++17 is needed here.

std::tuple_cat is one of my favorite tools.

  1. Use a std::index_sequence to chew through the tuple

  2. Use a specialization to pick up either a std::tuple<> or a std::tuple<T> out of the original tuple, for each indexed element.

  3. Use std::tuple_cat to glue everything together.

  4. The only tricky part is checking if each tuple element is wanted. To do that, put all the wanted types into its own std::tuple, and use a helper class for that part, too.

#include <utility>
#include <tuple>
#include <iostream>

// Answer one simple question: here's a type, and a tuple. Tell me
// if the type is one of the tuples types. If so, I want it.

template<typename wanted_type, typename T> struct is_wanted_type;

template<typename wanted_type, typename ...Types>
struct is_wanted_type<wanted_type, std::tuple<Types...>> {

static constexpr bool wanted=(std::is_same_v<wanted_type, Types>
|| ...);
};

// Ok, the ith index in the tuple, here's its std::tuple_element type.
// And wanted_element_t is a tuple of all types we want to extract.
//
// Based on which way the wind blows we'll produce either a std::tuple<>
// or a std::tuple<tuple_element_t>.

template<size_t i, typename tuple_element_t,
typename wanted_element_t,
bool wanted=is_wanted_type<tuple_element_t, wanted_element_t>::wanted>
struct extract_type {

template<typename tuple_type>
static auto do_extract_type(const tuple_type &t)
{
return std::tuple<>{};
}
};

template<size_t i, typename tuple_element_t, typename wanted_element_t>
struct extract_type<i, tuple_element_t, wanted_element_t, true> {

template<typename tuple_type>
static auto do_extract_type(const tuple_type &t)
{
return std::tuple<tuple_element_t>{std::get<i>(t)};
}
};

// And now, a simple fold expression to pull out all wanted types
// and tuple-cat them together.

template<typename wanted_element_t, typename tuple_type, size_t ...i>
auto get_type_t(const tuple_type &t, std::index_sequence<i...>)
{
return std::tuple_cat( extract_type<i,
typename std::tuple_element<i, tuple_type>::type,
wanted_element_t>::do_extract_type(t)... );
}

template<typename ...wanted_element_t, typename ...types>
auto get_type(const std::tuple<types...> &t)
{
return get_type_t<std::tuple<wanted_element_t...>>(
t, std::make_index_sequence<sizeof...(types)>());
}

int main()
{
std::tuple<int, const char *, double> t{1, "alpha", 2.5};

std::tuple<double, int> u=get_type<int, double>(t);

std::cout << std::get<0>(u) << " " << std::get<1>(u) << std::endl;

std::tuple<int, int, int, char, char, char, double, double, float> tt;

auto uu=get_type<float, double>(tt);

static_assert(std::is_same_v<decltype(uu),
std::tuple<double, double, float>>);

return 0;
}

How to get the position of a tuple element

UPDATE:

I eventually figured out a way to achieve this in a simpler way that also uses short-circuiting (and therefore performs less comparisons).

Given some machinery:

namespace detail
{
template<int I, int N, typename T, typename... Args>
struct find_index
{
static int call(std::tuple<Args...> const& t, T&& val)
{
return (std::get<I>(t) == val) ? I :
find_index<I + 1, N, T, Args...>::call(t, std::forward<T>(val));
}
};

template<int N, typename T, typename... Args>
struct find_index<N, N, T, Args...>
{
static int call(std::tuple<Args...> const& t, T&& val)
{
return (std::get<N>(t) == val) ? N : -1;
}
};
}

The function that clients are going to invoke eventually boils down to this simple trampoline:

template<typename T, typename... Args>
int find_index(std::tuple<Args...> const& t, T&& val)
{
return detail::find_index<sizeof...(Args), T, Args...>::
call(t, std::forward<T>(val));
}

Finally, this is how you would use it in your program:

#include <iostream>

int main()
{
std::tuple<int, int, int, int> a(2, 3, 1, 4);
std::cout << find_index(a, 1) << std::endl; // Prints 2
std::cout << find_index(a, 2) << std::endl; // Prints 0
std::cout << find_index(a, 5) << std::endl; // Prints -1 (not found)
}

And here is a live example.


EDIT:

If you want to perform the search backwards, you can replace the above machinery and the trampoline function with the following versions:

#include <tuple>
#include <algorithm>

namespace detail
{
template<int I, typename T, typename... Args>
struct find_index
{
static int call(std::tuple<Args...> const& t, T&& val)
{
return (std::get<I - 1>(t) == val) ? I - 1 :
find_index<I - 1, T, Args...>::call(t, std::forward<T>(val));
}
};

template<typename T, typename... Args>
struct find_index<0, T, Args...>
{
static int call(std::tuple<Args...> const& t, T&& val)
{
return (std::get<0>(t) == val) ? 0 : -1;
}
};
}

template<typename T, typename... Args>
int find_index(std::tuple<Args...> const& t, T&& val)
{
return detail::find_index<0, sizeof...(Args) - 1, T, Args...>::
call(t, std::forward<T>(val));
}

Here is a live example.


ORIGINAL ANSWER:

This does not really sound like a typical way one would use tuples, but if you really want to do this, then here is a way (works with tuples of any size).

First, some machinery (the well-known indices trick):

template <int... Is>
struct index_list { };

namespace detail
{
template <int MIN, int N, int... Is>
struct range_builder;

template <int MIN, int... Is>
struct range_builder<MIN, MIN, Is...>
{
typedef index_list<Is...> type;
};

template <int MIN, int N, int... Is>
struct range_builder : public range_builder<MIN, N - 1, N - 1, Is...>
{ };
}

template<int MIN, int MAX>
using index_range = typename detail::range_builder<MIN, MAX>::type;

Then, a couple of overloaded function templates:

#include <tuple>
#include <algorithm>

template<typename T, typename... Args, int... Is>
int find_index(std::tuple<Args...> const& t, T&& val, index_list<Is...>)
{
auto l = {(std::get<Is>(t) == val)...};
auto i = std::find(begin(l), end(l), true);
if (i == end(l)) { return -1; }
else { return i - begin(l); }
}

template<typename T, typename... Args>
int find_index(std::tuple<Args...> const& t, T&& val)
{
return find_index(t, std::forward<T>(val),
index_range<0, sizeof...(Args)>());
}

And here is how you would use it:

#include <iostream>

int main()
{
std::tuple<int, int, int, int> a(2, 3, 1, 4);
std::cout << find_index(a, 1) << std::endl; // Prints 2
std::cout << find_index(a, 2) << std::endl; // Prints 0
std::cout << find_index(a, 5) << std::endl; // Prints -1 (not found)
}

And here is a live example.

How to get the index of an element in a tuple via address?

Something you do not need in C++14, a mini indexes library:

template<unsigned...>struct indexes{using type=indexes;};
template<unsigned Cnt,unsigned...Is>
struct make_indexes:make_indexes<Cnt-1,Cnt-1,Is...>{};
template<unsigned...Is>
struct make_indexes<0,Is...>:indexes<Is...>{};
template<unsigned Cnt>
using make_indexes_t=typename make_indexes<Cnt>::type;

Function that does actual work. Creates an array pointers-to-elements. Then searches for p. The nullptr and -1 make it work for empty tuples.

template<unsigned...Is,class Tuple>
unsigned index_of(indexes<Is...>,void const* p, Tuple const&t){
void const* r[]={ nullptr, &std::get<Is>(t)... };
auto it = std::find( std::begin(r), std::end(r), p );
if (it==std::end(r))
return -1;
else
return (it-std::begin(r))-1;
}

you can put that in a details namespace.

The final function:

template<class...Ts>
unsigned index_of( void const*p, std::tuple<Ts...> const& t ){
return index_of( make_indexes_t<sizeof...(Ts)>{}, p, t );
}

return unsigned(-1) on failure.

Get a tuple element in runtime

The example codes can be rewritten. Since multiple return types are not possible in C++, so that you will have to get it via a lambda function passed as an argument. The same approach was also mentioned by the guy in the comment

#include <tuple>
#include <utility>
#include <type_traits>
#include <stdexcept>

template<
typename Tuple,
typename F,
typename Indices=std::make_index_sequence<std::tuple_size<Tuple>::value>>
struct runtime_get_func_table;

template<typename Tuple, typename F, size_t I>
void applyForIndex(Tuple& t, F f) {
f(std::get<I>(t));
}

template<typename Tuple, typename F, size_t ... Indices>
struct runtime_get_func_table<Tuple,F,std::index_sequence<Indices...>>{
using FuncType = void(*)(Tuple&, F);
static constexpr FuncType table[]={
&applyForIndex<Tuple, F, Indices>...
};
};

template<typename Tuple, typename F>
void runtime_get(Tuple& t,size_t index, F f) {
using tuple_type=typename std::remove_reference<Tuple>::type;
if(index>=std::tuple_size<tuple_type>::value)
throw std::runtime_error("Out of range");
runtime_get_func_table<tuple_type, F>::table[index](t, f);
}

int main() {
std::tuple<int ,char, float> t(10, 's', 10.2);
runtime_get(t, 0, [] (auto& v) {
// Do something v = get<0>(t);
});
}

How to get N-th type from a tuple?

You can use a class template and partial specializations to do what you want. (Note that std::tuple_element does almost the same like the other answer says):

#include <tuple>
#include <type_traits>

template <int N, typename... Ts>
struct get;

template <int N, typename T, typename... Ts>
struct get<N, std::tuple<T, Ts...>>
{
using type = typename get<N - 1, std::tuple<Ts...>>::type;
};

template <typename T, typename... Ts>
struct get<0, std::tuple<T, Ts...>>
{
using type = T;
};

int main()
{
using var = std::tuple<int, bool, std::string>;
using type = get<2, var>::type;

static_assert(std::is_same<type, std::string>::value, ""); // works
}

For std::tuple, how to get data by type, and how to get type by index?

Getting value from tuple by type (instead of index)

As of C++11, there is no STL way to get the first element of a tuple of type T.

In C++14, there should be a way using a new overload of std::get to do what you want. The ISO paper is located here N3404 and here N3670.

You can do this in C++11 with the following:

#include<tuple>
#include<type_traits>
#include<string>
#include<iostream>

template<int Index, class Search, class First, class... Types>
struct get_internal
{
typedef typename get_internal<Index + 1, Search, Types...>::type type;
static constexpr int index = Index;
};

template<int Index, class Search, class... Types>
struct get_internal<Index, Search, Search, Types...>
{
typedef get_internal type;
static constexpr int index = Index;
};

template<class T, class... Types>
T get(std::tuple<Types...> tuple)
{
return std::get<get_internal<0,T,Types...>::type::index>(tuple);
}

I have it hosted on Ideone here, but here's my test function for posterity

int main()
{
std::tuple<int, double, std::string> test{1, 1.7, "test"};
std::cout<<"get<0> == get<int> :"<< (std::get<0>(test) == get<int>(test))<< "\n";
std::cout<<"get<1> == get<double> :"<<(std::get<1>(test) == get<double>(test))<< "\n";
std::cout<<"get<2> == get<std::string> :"<<(std::get<2>(test) == get<std::string>(test))<< "\n";
}

Based of @Yakk's idea of extending this to support multiple instances of a type as well as a predicate to test for in the tuple, he provided the code below (also hosted on Ideone here)

Be warned: in C++14 the new overload of std::get does not allow multiple instances of the same type in the tuple. It instead issues a compile error. In addition the C++14 version will not support predicates either.

//Include same headers as before
template<bool b, typename T=void>
using EnableIf = typename std::enable_if<b,T>::type;

template<int Index, template<typename T>class Search, int Which, typename, class First, class... Types>
struct get_internal:
get_internal<Index + 1, Search, Which, void, Types...>
{};

template<int Index, template<typename T>class Search, int Which, class First, class... Types>
struct get_internal<Index, Search, Which, EnableIf<!Search<First>::value>, First, Types...>:
get_internal<Index + 1, Search, Which, void, Types...>
{};
template<int Index, template<typename T>class Search, int Which, class First, class... Types>
struct get_internal<Index, Search, Which, EnableIf<Search<First>::value>, First, Types...>:
get_internal<Index + 1, Search, Which-1, void, Types...>
{};
template<int Index, template<typename T>class Search, class First, class... Types>
struct get_internal<Index, Search, 0, EnableIf<Search<First>::value>, First, Types...>:
std::integral_constant<int, Index>
{};

template<template<typename>class Test, int Which=0, class... Types>
auto get(std::tuple<Types...>& tuple)->
decltype(std::get<get_internal<0,Test,Which,void,Types...>::value>(tuple))
{
return std::get<get_internal<0,Test,Which,void,Types...>::value>(tuple);
}
template<template<typename>class Test, int Which=0, class... Types>
auto get(std::tuple<Types...> const& tuple)->
decltype(std::get<get_internal<0,Test,Which,void,Types...>::value>(tuple))
{
return std::get<get_internal<0,Test,Which,void,Types...>::value>(tuple);
}
template<template<typename>class Test, int Which=0, class... Types>
auto get(std::tuple<Types...>&& tuple)->
decltype(std::move(std::get<get_internal<0,Test,Which,void,Types...>::value>(tuple)))
{
return std::move(std::get<get_internal<0,Test,Which,void,Types...>::value>(tuple));
}

template<typename T>
struct is_type {
template<typename U>
using test = std::is_same<T,U>;
};

template<class T, int Which=0, class... Types>
T& get(std::tuple<Types...>& tuple)
{
return get<is_type<T>::template test,Which>(tuple);
}
template<class T, int Which=0, class... Types>
T const& get(std::tuple<Types...> const& tuple)
{
return get<is_type<T>::template test,Which>(tuple);
}
template<class T, int Which=0, class... Types>
T&& get(std::tuple<Types...>&& tuple)
{
return std::move(get<is_type<T>::template test,Which>(tuple));
}

Getting type of n-th element in tuple

There is a way to get the type of the n-th element. std::tuple_element<n, decltype(tuple)>::type (thanks @syam) is the type of the n-th element of the tuple.

Finding the max element's index in a Tuple list? (Python)

If you want only max tuple value:

max_tuple = max(temp_tuple, key=lambda x:x[1])
print(max_tuple)

>> ('B', 3)

if you want index of max tuple too:

max_tuple = max(temp_tuple, key=lambda x:x[1])
max_tuple_index = temp_tuple.index(max_tuple)
print(max_tuple)
print(max_tuple_index)

>> ('B', 3)
>> 1


Related Topics



Leave a reply



Submit