Here is a rough tuple class implementation toy.
Firstly, some metaprogramming pattern to represent a sequence of integers:
template<int...> struct seq {}; template<int max, int... s> struct make_seq:make_seq< max-1, max-1, s... > {}; template<int... s> struct make_seq<0, s...> { typedef seq<s...> type; }; template<int max> using MakeSeq = typename make_seq<max>::type;
Next, a class tag that actually stores data:
template<int x, typename Arg> struct foo_storage { Arg data; };
This tagging method is a general scheme whenever we want to associate data with some tag at compile time (in this case, an integer). The tag ( int here) is usually not used anywhere in the repository, it is simply used to mark the repository.
foo_helper decompresses the sequence and set of arguments into a bunch of foo_storage and inherits them linearly. This is a fairly common pattern - if you do this a lot, you will end up creating metaprogramming tools that will do this for you:
template<typename Seq, typename... Args> struct foo_helper {}; template<int s0, int... s, typename A0, typename... Args> struct foo_helper<seq<s0, s...>, A0, Args...>: foo_storage<s0, A0>, foo_helper<seq<s...>, Args...> {};
My crude tuple type, foo , creates a sequence index and args sequence package and passes it to the helper above. Then the helper creates a bunch of tagged data containing the parent classes:
template<typename... Args> struct foo: foo_helper< MakeSeq<sizeof...(Args)>, Args... > {};
I removed everything from the body of foo because I do not need to implement get .
get pretty simple: we take the storage type (not the tuple type), and the explicit argument template N removes the ambiguity which we are going to get from foo_storage<n, T> . Now that we have the storage type, we simply return the data field:
template<int N, typename T> T& get( foo_storage<N, T>& f ) { return f.data; } template<int N, typename T> T const& get( foo_storage<N, T> const& f ) { return f.data; }
We use C ++ langauge overload mechanisms for heavy lifting. When you call a function with an instance of a class, this instance, since each of the parent classes has gone over to check if they can be matched. For fixed N there is only one parent class, which is a valid argument, so the parent class (and therefore T ) is automatically displayed.
And finally, some basic test code:
#include <iostream> int main() { foo<int, double> f; get<0>( f ) = 7; get<1>( f ) = 3.14; std::cout << get<0>(f) << "," << get<1>(f) << "\n"; }