Reputation: 3099
The title says it: Given an std::tuple, I'd like to
Is there a solution provided by STL? Or a workaround? Can someone try to complete my code?
#include <tuple>
int main ()
{
std::tuple<int,char,int> mytuple (10,'a', 5);
// how to get the first int element here? (10)
// int x = std::get_me_the_first<int>(mytuple);
// how to get the type of the second element here?
// std::get_me_type_of<1> ch = 'x';
return 0;
}
Compiled like this:
g++ -std=c++11 -Wall main.cpp -o main
Upvotes: 5
Views: 7277
Reputation: 3842
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));
}
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.
Upvotes: 10
Reputation: 7136
Just for giggles, def not what you want and is very limited in a sense that you have to add new types and it works in reverse but here is getNthTypeReverse
which only supports 'int' and 'char' types haha http://ideone.com/Uk2JTC
#include <tuple>
#include <iostream>
using namespace std;
typedef std::tuple<int,char,int> TupleType;
template<typename TupleT,typename OnElementHandler, size_t N>
struct TupleIterator{
static void call(const TupleT& tuple, OnElementHandler& OnElement){
auto nthElem = std::get<N>(tuple);
OnElement(nthElem);
TupleIterator<TupleT,OnElementHandler,N-1>::call(tuple,OnElement);
}
};
template<typename TupleT,typename OnElementHandler>
struct TupleIterator<TupleT,OnElementHandler,0>{
static void call(const TupleT& tuple, OnElementHandler& OnElement){
auto firstElem = std::get<0>(tuple);
OnElement(firstElem);
}
};
template<typename T1,typename T2>
struct IsSame{enum{result = 0};};
template<typename T>
struct IsSame<T,T>{ enum{result = 1}; };
template<typename T,size_t TargetCount, size_t BeginIndex, size_t EndIndex, typename Tuple>
T getNthTypeReverse(const Tuple& t, const T& defaultValue = T()){
//assert 0 <= N <= tuple.size
T result = defaultValue;
struct NthGrabber{
T& result;
const size_t n;
size_t count;
NthGrabber(T& r, const size_t n): result(r),n(n),count(0){}
void operator()(const int i){
if(IsSame<T,int>::result){
++count;
if(count == n) result = i;
}
}
void operator()(const char c){
if(IsSame<T,char>::result){
++count;
if(count == n) result = c;
}
}
//overload for other version too...
}OnElement(result,TargetCount+1); //WILL update result if condition meet
const size_t tupleSize = EndIndex - BeginIndex;
TupleIterator<TupleType,NthGrabber,tupleSize>::call(t,OnElement);
return result;
}
int main(){
TupleType t(10,'a',5);
const size_t tupleSize = std::tuple_size<decltype(t)>::value - 1;
int lastInt = getNthTypeReverse<int,0,0,tupleSize,TupleType>(t,-1);
int secondLastInt = getNthTypeReverse<int,1,0,tupleSize,TupleType>(t,-1);
int thirdLastInt = getNthTypeReverse<int,2,0,tupleSize,TupleType>(t,-1);
cout << "lastInt = " << lastInt << endl;
cout << "SecondLast = " << secondLastInt << endl;
cout << "ThirdLast = " << thirdLastInt << endl;
char lastChar = getNthTypeReverse<char,0,0,tupleSize,TupleType>(t,'\0');
cout << "LastChar = " << lastChar << endl;
return 0;
}
output:
lastInt = 5
SecondLast = 10
ThirdLast = -1
LastChar = a
Upvotes: 0