Mohit
Mohit

Reputation: 2110

C++ Utility to convert long switch statement into the concise function call which encapsulates the switch case ladder

I want to write a C++ utility which can do the following:

Current Workflow

int num;
std::string str;
switch(num){
    case 2:
        str = "two";
    break;
    case 5:
        str = "five";
    break;
    case 7:
        str = "seven";
    break;
}

I want to achieve this:

//key is always guaranteed to be int but value can be anything
DECLARE_SWITCH( {
    {2 , "two"},
    {5 , "five"},
    {7 , "seven"}
})

int num = 5;
std::string str;

match(num,str,ARR);
//here str == "five"

I want to write the DECLARE_SWITCH and match functions. There is no restriction on the language constructs - preprocessor MACRO, templates anything would do.However, it would be good if there is some simple solution or trick. I know about associative data structures but I do not want to use any data structure. This question is specifically about using switch case.

Upvotes: 2

Views: 584

Answers (3)

Surt
Surt

Reputation: 16099

Hmm your looking for the X macro

WARNING CONCEPT CODE NOT TESTET

#define LIST_OF_VARIABLES \
    X(2 , "two") \
    X(5 , "five") \
    X(7 , "seven")


template<class RetValue>
RetValue match(int key) {
    #define SWITCH(CASE, VALUE) case CASE : return VALUE; 

    #define X(PAIR) SWITCH(PAIR)

    // makes the actual switch case
    switch(key) {
      LIST_OF_VARIABLES 
      default: throw "fail";
    }
    #undef SWITCH
    #undef X
}    

int main()
    int num = 5;
    std::string str;

    str = match<std::string>(num);
    std::cout << str;
}

Upvotes: 0

kocica
kocica

Reputation: 6465

std::map / std::unordered_map is ideal for this usecase.

Maps are associative containers that store elements formed by a combination of a key value and a mapped value, following a specific order.

In your case you need key to be an int and mapped value std::string.

Here is an example

std::map<int, std::string> t;

t[1] = "one";
t[2] = "two";
t[3] = "three";

std::cout << "Value for '2': " << t[2] << std::endl;

for (auto& it : t)
{
    std::cout << it.first << ": " << it.second << std::endl;
}

for (auto& it : t)
{
    if (it.second == "one")
    {
        std::cout << "Value mapped to 'one' is: " << it.first << std::endl;
    }
}

Output

Value for '2': two
1: one
2: two
3: three
Value mapped to 'one' is: 1

In your case

std::map<int, std::string> DECLARE_SWITCH
{
    {2 , "two"},
    {5 , "five"},
    {7 , "seven"}
};

int num = 2;
std::string str = DECLARE_SWITCH[num];

std::cout << str << '\n';

Upvotes: 3

Artemy Vysotsky
Artemy Vysotsky

Reputation: 2734

With the map you can even make the code look very similar to your original sample

#include <map>
#include <string>
#include <iostream>

int main()
{
    std::map<int,std::string> DECLARE_SWITCH {
        {2 , "two"},
        {5 , "five"},
        {7 , "seven"}

    };

    int num = 5;
    std::string str = DECLARE_SWITCH[num];

    std::cout << str << '\n';
    return 0;
}

Note that operator [] will insert new entry to map if it is not present. To avoid such behavior you will have to use find

#include <map>
#include <string>
#include <iostream>

std::string match(int number, const std::map<int,std::string>& DECLARE_SWITCH )
{
    auto q = DECLARE_SWITCH.find(number);
    if (q==DECLARE_SWITCH.end()) {
        return "";
    }
    else
        return q->second;
}

int main()
{
    const std::map<int,std::string> DECLARE_SWITCH {
        {2 , "two"},
        {5 , "five"},
        {7 , "seven"}
    }; //now we can declare map as const

    int num = 5;
    std::string str = match(num,DECLARE_SWITCH);

    std::cout << str << '\n';
    return 0;
}

Upvotes: 3

Related Questions