using some template magic, your problems can be solved by variadic template and template recursion.
template< size_t N, typename T, typename U, typename... Ts >
struct Rows : public Rows<N-1, Ts...> {
vector<T> row;
U rowinfo;
};
template<typename T, typename U>
struct Rows<1, T, U> {
vector<T> row;
U rowinfo;
};
Rows< 3, int, string, float, string, double, rss_feed > rows;
=> struct Rows {
vector<int> v1;
string s1;
vector<foat> v2;
string s2;
vector<double> v3;
rss_feed feed;
}
to access the value of those field, you can implement a templated get()
member function
template< size_t N, typename T, typename U, typename... Ts >
struct Rows : public Rows<N-1, Ts...> {
vector<T> row;
U rowinfo;
template< size_t M >
typename enable_if< M == N, tuple<vector<T>&, U&> >::type
get() {
return make_tuple( ref(row), ref(rowinfo) );
}
};
template<typename T, typename U>
struct Rows<1, T, U> {
vector<T> row;
U rowinfo;
template< size_t M >
typename enable_if< M == 1, tuple<vector<T>&, U&> >::type
get() {
return make_tuple( ref(row), ref(rowinfo) );
}
};
take a moment and digest those. The get()
methods return a tuple that contains reference to the request fields, and they are only enabled if the passed in number match with the class's template, what about other numbers? well we can enable them like this
template< size_t M >
typename enable_if< M != N, tuple<vector<T>&, U&> >::type
get() {
return Rows<N-1, Ts...>::template get<M>(); // call parent's get,
// ::template is required to avoid ambiguity
}
but this is wrong!!! The return type is not tuple<vector<T>&, U&>
, it should be tuple<vector<A>&, B&>
where A
and B
are the corresponding types in the variadic template Ts...
But how the hell do we get the types we want from Ts...
, well we can find out the types of a variadic template with the following technique.
template< size_t N, typename... Ts >
struct variadic_type;
template< typename T, typename U, typename... Ts >
struct variadic_type< 0, T, U, Ts... > {
typedef T T_type;
typedef U U_type;
};
template< size_t k, typename T, typename U, typename... Ts >
struct variadic_type< k, T, U, Ts... > {
typedef typename variadic_type< k-1, Ts... >::T_type T_type;
typedef typename variadic_type< k-1, Ts... >::U_type U_type;
};
so variadic_type< 1, int, int, short, short, float, float >::T_type
and U_type
would be short
and short
.
Now we have a way to find out the types of a specific location on the variadic template, we can apply it to our wrong get()
method to get the correct return type...
template< size_t M >
typename
enable_if< M != N,
tuple<
vector<typename variadic_type< N-M, T, U, Ts...>::T_type>&,
typename variadic_type< N-M, T, U, Ts...>::U_type&
> >::type
get() {
return Rows<N-1, Ts...>::template get<M>();
}
We are done, we can call get()
like
Rows< 3, int, string, double, string, float, string > rows;
auto r3 = rows.get<3>(); // { vector<int>& v, string& s };
auto r2 = rows.get<2>(); // { vector<double>& v, string& s };
auto r1 = rows.get<1>(); // { vector<float>& v, string& s };
auto r4 = rows.get<4>(); // error:: other numbers will not compile!!
now the index rule is not very pleasant to deal with, but I think this gets the idea across.