Use std :: tuple for a list of template parameters instead of a list of types

I am trying to make a template function call like this:

typedef std::tuple<int, double, bool> InstrumentTuple; Cache cache; InstrumentTuple tuple = cache.get<InstrumentTuple>(); 

I know that I can "just" pass the types of tuples. This is what I know, but it is rather cumbersome, since I make many calls to this function, and since the tuples are quite long:

 InstrumentTuple tuple = c.get<int, double, bool>(); // syntax I'd like to avoid 

So, I tried several implementations of the get method, but without success:

Inclusion using a template parameter

 #include <tuple> class Cache { private: template<int I, typename T, typename = typename std::enable_if<I == std::tuple_size<T>::value>::type> std::tuple<> get() // line 6 { return std::tuple<>(); } template<int I, typename T, typename = typename std::enable_if<I != std::tuple_size<T>::value>::type> std::tuple<typename std::tuple_element<I,T>::type, decltype(get<I+1, T>())> get() // line 12 { std::tuple<typename std::tuple_element<I,T>::type> value; return std::tuple_cat(value, get<I+1, T>()); } public: template<typename T> T get() { return get<0, T>(); // line 22 } }; int main(int argc, char** argv) { Cache cache; typedef std::tuple<int, double, bool> InstrumentTuple; InstrumentTuple tuple = cache.get<InstrumentTuple>(); // line 30 } 

Which gives me this error:

 main.cpp: In instantiation of 'T Cache::get() [with T = std::tuple<int, double, bool>]': main.cpp:30:56: required from here main.cpp:22:26: error: no matching function for call to 'Cache::get()' main.cpp:22:26: note: candidates are: main.cpp:6:18: note: template<int I, class T, class> std::tuple<> Cache::get() main.cpp:6:18: note: template argument deduction/substitution failed: main.cpp:5:33: error: no type named 'type' in 'struct std::enable_if<false, void>' main.cpp:12:81: note: template<int I, class T, class> std::tuple<typename std::tuple_element<I, T>::type, decltype (get<(I + 1), T>())> Cache::get() // ----- Important part main.cpp:12:81: note: template argument deduction/substitution failed: main.cpp: In substitution of 'template<int I, class T, class> std::tuple<typename std::tuple_element<I, T>::type, decltype (get<(I + 1), T>())> Cache::get() [with int I = 0; T = std::tuple<int, double, bool>; <template-parameter-1-3> = <missing>]': // ----- main.cpp:22:26: required from 'T Cache::get() [with T = std::tuple<int, double, bool>]' main.cpp:30:56: required from here main.cpp:12:81: error: no matching function for call to 'Cache::get()' main.cpp:12:81: note: candidate is: main.cpp:6:18: note: template<int I, class T, class> std::tuple<> Cache::get() main.cpp:6:18: note: template argument deduction/substitution failed: main.cpp:5:33: error: no type named 'type' in 'struct std::enable_if<false, void>' main.cpp: In instantiation of 'T Cache::get() [with T = std::tuple<int, double, bool>]': main.cpp:30:56: required from here main.cpp:20:7: note: template<class T> T Cache::get() main.cpp:20:7: note: template argument deduction/substitution failed: main.cpp:22:26: error: wrong number of template arguments (2, should be 1) 

I do not understand why the template parameter is missing.

So, I tried another implementation:

Template Named Parameter

 #include <tuple> class Cache { private: template<int> std::tuple<> get() // line 7 { return std::tuple<>(); } template<int index, typename type, typename... rest> std::tuple<type, rest...> get() // line 13 { return std::tuple_cat(std::tuple<type>(), get<index+1, rest...>()); } public: template<template<typename... types> class tuple> typename std::tuple<(tuple::types)...> get() { return get<0, (tuple::types)...>(); } }; // line 24 int main(int argc, char** argv) { Cache cache; typedef std::tuple<int, double, bool> InstrumentTuple; InstrumentTuple tuple = cache.get<InstrumentTuple>(); // line 30 } 

But then I get this error:

 // ----- Important part main.cpp:24:1: error: expected identifier before '}' token main.cpp:24:1: error: expected unqualified-id before '}' token // ----- main.cpp: In function 'int main(int, char**)': main.cpp:30:56: error: no matching function for call to 'Cache::get()' main.cpp:30:56: note: candidates are: main.cpp:7:18: note: template<int <anonymous> > std::tuple<> Cache::get() main.cpp:7:18: note: template argument deduction/substitution failed: main.cpp:13:31: note: template<int index, class type, class ... rest> std::tuple<_Head, _Tail ...> Cache::get() main.cpp:13:31: note: template argument deduction/substitution failed: 

Again, I do not understand the error due to the missing identifier.

Now I wonder if what I want to achieve is even possible. Is it possible to use std::tuple as I want? Or is there a better way?

+4
source share
2 answers

Your first solution does not work, because the second overload on get not visible at the point of your own declaration of the type of the return type; to get around this, you will need to separate the return type computation into your own routine.

The second solution is closer; the problem is that you only output the std::tuple template, not its arguments. An easy way to output variational arguments (for example, arguments of the tuple type) is to use an empty tag structure, requiring an additional level of indirection:

 template<typename T> struct type_tag {}; class Cache { // ... (as above) template<typename... Ts> std::tuple<Ts...> get(type_tag<std::tuple<Ts...>>) { return get<0, Ts...>(); } public: template<typename T> T get() { return get(type_tag<T>{}); } }; 

You should check if you can write a solution using the package extension instead of recursion, for example:

 template<typename T> struct type_tag {}; class Cache { template<typename... Ts> std::tuple<Ts...> get(type_tag<std::tuple<Ts...>>) { return std::tuple<Ts...>{Ts{}...}; } public: template<typename T> T get() { return get(type_tag<T>{}); } }; 
+6
source

Here is what I came up with:

 #include <tuple> struct Cache; /* typename = std::tuple<...> */ template<int, typename> struct cache_getter; /* typename = parameters from std::tuple<...> */ template<int, typename...> struct tuple_walker; template<int I, typename... Ts> struct cache_getter<I, std::tuple<Ts...> > { static std::tuple<Ts...> get(Cache & c); }; struct Cache { protected: template<int, typename...> friend struct tuple_walker; private: /* here T is a type from within a std::tuple<...> */ template<int I, typename T> std::tuple<T> get_ex() { return std::tuple<T>(); } public: /* here T is actually a std::tuple<...> */ template<typename T> T get() { return cache_getter<0, T>::get(*this); } }; /* since std::tuple_cat only accepts 2 std::tuples per call but we don't have control over the number of types in the passed in std::tuple, we'll need to chain our calls */ template<typename...> struct my_tuple_cat; template<typename H, typename... T> struct my_tuple_cat<H, T...> { static auto cat(H h, T... t) -> decltype(std::tuple_cat(h, my_tuple_cat<T...>::cat(t...))) { return std::tuple_cat(h, my_tuple_cat<T...>::cat(t...)); } }; template<typename T> struct my_tuple_cat<T> { static T cat(T t) { return t; } }; /* this one is used to call Cache.get_ex<int I, typename T>() with incrementing values for I */ template<int I, typename H, typename... T> struct tuple_walker<I, H, T...> { static std::tuple<H, T...> get(Cache & c) { return my_tuple_cat<std::tuple<H>, std::tuple<T...>>::cat(c.get_ex<I, H>(), tuple_walker<I + 1, T...>::get(c)); } }; template<int I, typename H> struct tuple_walker<I, H> { static std::tuple<H> get(Cache & c) { return c.get_ex<I, H>(); } }; /* this one will forward the types in std::tuple<...> to tuple_walker to get each tuple separately */ template<int I, typename... Ts> std::tuple<Ts...> cache_getter<I, std::tuple<Ts...> >::get(Cache & c) { return tuple_walker<I, Ts...>::get(c); } int main(int argc, char ** argv) { Cache cache; typedef std::tuple<int, double, bool> InstrumentTuple; InstrumentTuple tuple = cache.get<InstrumentTuple>(); return 0; } 

Hope it's worth it. I haven't done much in C ++ 11 yet, so maybe this is not an optimal solution.

The proof that it compiles can be found here.

+3
source

All Articles