Reputation: 5044
struct VectorOfMaps {
std::tuple<std::map<std::string,double>,
std::map<std::string,int>,
std::map<std::string,short>
> tuple;
};
I want to generalize this into a template, so the above class would be produced by something like
VectorOfMaps<3, std::string,double,
std::string,int,
std::string,short>
Can this, or something similar, be done?
I know I could just put the map types directly into the template, but wouldn't such repetition of "std::map
" considered a bit ugly? What do you think?
(I thought about whether I could specify std::pair
types in the template, but I was not sure if those could be used directly to create the std::map
types) ..
Upvotes: 2
Views: 304
Reputation: 948
I think using std::tuple provides a simpler and cleaner way (but it might be slower...).
template<int pos,typename... Args> struct TypeTransformer_ {
using ValueType = typename std::tuple_element<1,std::tuple<Args...>>::type;
using KeyType = typename std::tuple_element<0,std::tuple<Args...>>::type ;
using Type = decltype(std::tuple_cat(std::tuple<std::map<KeyType,ValueType>>(),
typename TypeTransformer_<pos - 2,Args...>::Type()));
};
//Base case with two elements...
template<typename... Args>
struct TypeTransformer_<2,Args...> {
using ValueType = typename std::tuple_element<1,std::tuple<Args...>>::type ;
using KeyType = typename std::tuple_element<0,std::tuple<Args...>>::type ;
using Type = std::tuple<std::map<KeyType,ValueType>> ;
};
//Handling the case of an odd number of parms
template<typename... Args> struct TypeTransformer_<1,Args...> ;
//Nicer interface so we don't have to specify the number of variadic param
template <typename... Args> struct SomeStruct{
using TupleType = typename TypeTransformer_<sizeof...(Args),Args...>::Type;
TupleType tp ;
};
Upvotes: 0
Reputation: 35449
Since you mention passing specializations of pairs to the class template:
template<typename... Pairs>
struct VectorOfMaps {
std::tuple<std::map<
typename Pairs::first_type
, typename Pairs::second_type
>...> tuple;
};
This is effectively using std::pair
as a type-list -- you could use something like template<typename T, typename U> struct pair { using first_type = T; using second_type = U; };
just as well.
It is possible to pass the types without the a pair as well, but this requires some metacomputations. A possible solution:
// First argument is accumulator, supposed to be an empty tuple
template<typename Acc, typename... T> struct compute_tuple
{
// Only triggered when the primary template is instantiated, which should
// only happen if sizeof...(T) is odd -- it's possible to assert on that
// instead, too.
static_assert( !sizeof(Acc), "An even number of arguments is required" );
};
// Recursive case
template<typename... Acc, typename First, typename Second, typename... Rest>
struct compute_tuple<std::tuple<Acc...>, First, Second, Rest...>
: compute_tuple<std::tuple<Acc..., std::map<First, Second>>, Rest...> {};
// Terminal case
template<typename Acc>
struct compute_tuple<Acc> { using type = Acc; };
template<typename... T>
struct VectorOfMaps {
/*
* You can assert that sizeof...(T) is even here; it might be more
* helpful than an error deeper inside compute_tuple.
*/
using tuple_type = typename compute_tuple<std::tuple<>, T...>::type;
tuple_type tuple;
};
Upvotes: 3
Reputation: 2089
The first_of_pair metafunction is unnecessary as pair has first_type and second_type members. Here is the solution with variadic template argument:
template<class... pairtypes>
struct VectorOfMaps
{
std::tuple<
std::map<
typename pairtypes::first_type,
typename pairtypes::second_type
>...
> tuple;
};
int main()
{
VectorOfMaps<
std::pair<int,char>,
std::pair<char,long>
> v;
std::get<0>(v.tuple)[4]='a';
std::get<1>(v.tuple)['z']=5l;
return 0;
}
Upvotes: 2
Reputation: 2089
If you can accept an upper limit on the number of maps to store, you can do it with template specializations for different values of n:
template<int n, class key0, class value0, class key1 = void, class value1 = void>
struct VectorOfMaps;
template<class key0, class value0>
struct VectorOfMaps<1,key0,value0>
{
// ...
};
template<class key0, class value0, class key1, class value1>
struct VectorOfMaps<2,key0,value0,key1,value1>
{
// ...
};
A nicer approach would be using a variadic template parameter.
From a pair argument you can extract the types with helper metafunctions:
template<class T>
struct first_of_pair;
template<class First, class Second>
struct first_of_pair<std::pair<First,Second>>
{
typedef First type;
};
// usage in a template where T is an std::pair: typename first_of_pair<T>::type
Upvotes: 0