Working in C++, there's some times I'm working with nested maps. For instance, hypothetically:
enum Color { RED, GREEN, BLUE};
enum Shape { CIRCLE, SQUARE, TRIANGLE };
std::unordered_map<Color, std::unordered_map<Shape, int>> shapeColorCount;
For these situations, it would be useful to use variadic templates to write setter and getter functions that are templated over the types of the keys. The goal would be something like:
template<typename TValue, typename TKeys...>
TValue& nestedGet(MapTypeHere t_map, const TKeys&... t_keys);
void nestedSet(MapTypeHere t_map, const TValue& t_value, const TKeys&... t_keys);
It's not too hard to define these functions recursively, but my main problem is getting the type inference for the template parameters to work correctly. The problem is specifying MapTypeHere. I can almost write something like
template<typename TValue, typename TKey>
using Map = std::unordered_map<TKey, TValue>;
template<typename TValue, typename TOuterKey, typename... TInnerKeys>
using Map = std::unordered_map<TOuterKey, Map<TValue, TInnerKeys...>;
template<typename TValue, typename... TKeys>
TValue& nestedGet(Map<TValue, TKeys...>& t_map, const TKeys&... t_keys);
void nestedSet(Map<TValue, TKeys...>& t_map, const TValue& t_value, const TKeys&... t_keys);
Trying to create a recursive using directive, but it complains that I'm trying to use a parameter pack in a non-pack template variable when it attempts to use the base case for Map
. If I wrap them in structs, it seems to allow it to do this recursive using declaration, but then I have a problem where the type inference doesn't work. Going back to the above example:
std::unordered_map<Color, std::unordered_map<Shape, int>> shapeColorCount
nestedSet<int, Color, Shape>(shapeColorCount, 5, Color::RED, Shape::SQUARE); // This works
nestedSet(shapeColorCount, 5, Color::RED, Shape::SQUARE); // It can't figure out the types for the template
Is there a way to get this setup working correctly?
Yes, you can write the following functions:
template<typename Map, typename Value, typename FirstKey, typename ...RestKeys>
void nestedSet(Map& map, Value const& value, FirstKey const& key, RestKeys const&... rest_keys)
{
if constexpr(sizeof...(RestKeys) == 0)
map[key] = value;
else
nestedSet(map[key], value, rest_keys...);
}
template<typename Map, typename FirstKey, typename ...RestKeys>
auto& nestedGet(Map& map, FirstKey const& key, RestKeys const&... rest_keys)
{
if constexpr(sizeof...(RestKeys) == 0)
return map[key];
else
return nestedGet(map[key], rest_keys...);
}
Note that this solution doesn't depend on a particular unordered_map<Color, std::unordered_map<Shape, int>>
instantiation. It works for any instantiations of key and value types, and for any depth of nested unordered_map
s.
Here's a demo.
Also, if you don't have c++17, then you can rewrite the if constexpr
solution with an overloaded template that takes a single KeyType
parameter.