I have a few disjoint maps which map unique types to strings. For example:
enum class Colors {RED, GREEN};
enum class Days {MON, SUN};
std::map <Colors, std::string> map1{
{Colors::RED, "red"},
{Colors::GREEN, "green"},
};
std::map <Days, std::string> map2{
{Days::MON, "mon"},
{Days::SUN, "sun"},
};
Given a key, I want to be able to extract its value transparently from the corresponding map (there is exactly one map for a type).
I considered a wrapper function like so
template <typename T>
auto extractValue(T k){
// Somehow use type T to switch maps and return value corresponding to k
}
I'm however not sure how/if this can be achieved. If this is not possible, what could be an alternate approach where I can exploit the fact that there is exactly one map per type.
I'm using C 20.
Edit: I can achieve this by writing overloads of extractValue
for each type, but I'm looking for a more "generic" approach.
CodePudding user response:
In C 17 and later, you can use if constexpr
, eg:
template <typename T>
auto extractValue(T k){
if constexpr (std::is_same_v<T, Colors>) {
return map1[k];
}
else if constexpr (std::is_same_v<T, Days>) {
return map2[k];
}
return std::string{};
}
Otherwise, you can use template specialization, eg:
template <typename T>
auto extractValue(T k){
return std::string{};
}
template <>
auto extractValue<Colors>(Colors k){
return map1[k];
}
template <>
auto extractValue<Days>(Days k){
return map2[k];
}
Or, you could just simply overload the function, eg:
auto extractValue(Colors k){
return map1[k];
}
auto extractValue(Days k){
return map2[k];
}
CodePudding user response:
You can overload function extractValue() for each enum type:
auto extractValue(Colors color) {
return map1[color];
}
auto extractValue(Days day) {
return map2[day];
}
int main() {
std::cout << extractValue(Days::MON) << '\n';
std::cout << extractValue(Colors::RED) << '\n';
return 0;
}
CodePudding user response:
Given the parameters of your question, I could recommend two options. The first one with C 17's if constexpr
makes everything quite optimal:
template <typename T>
auto extractValue(T k) {
if constexpr (std::is_same_v<T, Colors>) {
return map1[k];
}
else if constexpr (std::is_same_v<T, Days>) {
return map2[k];
}
throw new ArgumentException("Unrecognized type");
}
This is however not that extendible (if at some point you add a new type), even though the exception is thrown and you know you have to add a new if
clause, it isn't always apparent in production code.
I would recommend just sticking to function overloads and ditch the template.
string extractValue(std::map<Colors, string> map, Colors key) {
return map[key];
}
and so on... You can still call the overloads from a precursor function template in which you don't know the exact types.
Or better yet, just template the key type:
string extractValue<T>(std::map<T, string> map, T key) {
return map[key];
}
This all depends on the complexity of your functions though. So pick your best choice.