1

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.

aydwi
  • 25
  • 5
  • I'm not sure I understand. You want to return the `string` corresponding to the key which can be either `Colors` or `Days`, is that right? – DeiDei Jul 26 '22 at 20:28
  • @DeiDei Yes, that is exactly correct – aydwi Jul 26 '22 at 20:36
  • compiler does not know that you have only one map for each enum type. You need somehow provide it with information which map contains which type. You can do it by overloading extractValue for each type or use some template specializations. But it is impossible to have only generic function. – Sandro Jul 26 '22 at 20:52

3 Answers3

2

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];
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Just a dumb question: isnt `constexpr` assumed if the `else` branch refers to an `if constexpr` ? Do you need to repeat `if constexpr` ? – paolo Jul 26 '22 at 20:43
  • @paolo No, it is not assumed, and yes, you need to repeat it on each `if`. See [Do I need to put constexpr after else-if?](https://stackoverflow.com/questions/52356341/) – Remy Lebeau Jul 26 '22 at 20:50
1

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.

DeiDei
  • 10,205
  • 6
  • 55
  • 80
1

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;
}
Sandro
  • 2,707
  • 2
  • 20
  • 30