Is there any design pattern to avoid the nested switch case?

I have seen similar threads but am not sure how exactly to apply the solutions to my case. My problem is that I have a set of usecases to say "A", "B", "C". There are certain commands I need to execute when the input (2 usecases is the input) is any 2 of the listed cases, for example:

switch(input1)
{
case A:
break;
case B:
break;
case C:
break;
}

      

inside each case, I will need to check input 2, so the final code might look like

switch(input1)
{
case A:
{
switch(input2):
case B:
break;
case c:
break;
}
case B:
{
switch(input2):
case A:
break;
case c:
break;
}
....

}

      

I was thinking to use the (pair, command) card and remove these switch cases, but is there an alternative better solution or design problem to solve this problem?

+3


source to share


6 answers


If performance isn't a big issue, then a function pointer map might be one solution.

Assuming that the label A

, B

, C

... - small integral values less 255

.

  • Install the card first

    #define KEY(a,b)  ( (a<<8) | b )
    
    std::map<int, function_pointer_type>  dispatcher =
    {
        { KEY(A,B), ab_handler},
        { KEY(A,C), ac_handler},
        { KEY(B,C), bc_handler},
        //etc
    };
    
          

  • Use a map to call the appropriate handler for each set of inputs:

     dispatcher[KEY(input1,input2)] (/* args */);
    
          

Note that you need to configure the dispatcher with every possible pair of inputs. Also, if the pair KEY(A,B)

and KEY(B,A)

are the same, then you can write a named function invoke

to handle this case to ensure uniform usage for the rest of the code.



 void invoke(int input1, int input2, /* args */)
 {
     if (dispatcher.find(KEY(input1, input2)) != dispatcher.end() )
           dispatcher[KEY(input1,input2)] (/* args */);
     else
           dispatcher[KEY(input2,input1)] (/* args */);
 }

      

then use it like:

 invoke(input1, input2, /* args */);
 invoke(input2, input1, /* args */);  //still okay!

      

Hope it helps.

+8


source


In your case, how to split two switches into two functions



bool processInput2(char input2)
{
  switch(input2)
  {
   case 'A':
   {  
      // blah
   }
    break;
}

bool processInput1(char input1)
{
  switch(input1)
  {
   case 'A':
      processInput2(input2);
      break;
}

      

+2


source


One possibility is to split the code into one function for each nested case, so your example will have 6 functions:

void process_A_A() { ... }
void process_A_B() { ... }
void process_B_A() { ... }
void process_B_B() { ... }
void process_C_A() { ... }
void process_C_B() { ... }

      

Then put them into an array in initialization for a very fast (constant time) runtime lookup:

typedef std::function<void(void)> Function;  // or: void (*)(void)
Function f[Input1Count][Input2Count];
f[A][A] = &process_A_A;
f[A][B] = &process_A_B;
...

      

To call the corresponding function, write:

f[input1][input2]();

      

Note that, using the C ++ 11 type std::function

, functions do not have to be classic function pointers; they can also be lambda functions or functor objects.

You can also leave some parts blank or assign the same function multiple times. When you decide to keep some of the entries empty (so nothing should be done in that case), check the function object before calling it:

if (f[input1][input2])
    f[input1][input2]();

      

+1


source


You can always do something like:

switch ( 256 * input1 + input2 ) {
case 256 * 'A' + 'B':
    //  ...
    break;
//  ...
};

      

But honestly, in this case, I would find nested switches easier to understand, assuming that switch

is the correct answer to your problem. For character input this is common, but there are other alternatives such as std::map<std::pair<char, char>, Action const*>

where Action

is the virtual base class and every action on the map is a static instance of the derived class. This has the advantage that each action is a separate object (which may not be an advantage depending on what you are doing in the action), and if the map is populated dynamically (for example, in the constructor Action

) you can add actions without changing the source code. parser (but you may not need this flexibility).

0


source


Suggested answers with map or table of pointers to handle functions ok. But I see two drawbacks: 1) Slight performance degradation compared to manual nested switches. 2) The processing methods of the case are not entirely self-descriptive. I mean, you have to mention each descriptor method twice - in your "definition" and in the place where you start the map.

I see two alternatives: 1) Generate source code. Automatically generate nested keys from any view. Well ... this is a very good option for generating optimal code if you don't mind adding code generation for such a small task. 2) Using preprocessor hacks. Not the most elegant, but a pretty interesting way to make it work.

First, we declare X-Macro for our enum:

#define ELEMENTS(processor) \
processor(firstElement)     \
processor(secondElement)    \
processor(thirdElement)

      

We can use it to declare the enum itself:

#define ENUM_PROCESSOR(arg) arg,

enum class
{
    ELEMENTS(ENUM_PROCESSOR)
};

#undef ENUM_PROCESSOR
Now we can add method that uses macros to generate nested switches:

void processElements(const Elements element1,
                     const Elements element2)
{
    // These macros are useful to trick the preprocessor to allow recursive macro calls
    // https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms
    #define EMPTY(...)
    #define DEFER(...) __VA_ARGS__ EMPTY()
    #define EXPAND(...) __VA_ARGS__
    #define ELEMENTS_INT() ELEMENTS

    #define PROCESS_NESTED_ENUM_VALUE(value)                                         \
    case Elements::value:                                                            \
    {                                                                                \
        process<Value1, Elements::value>();                                          \
        break;                                                                       \
    }

    #define PROCESS_ENUM_VALUE(value)                                                \
    case Elements::value:                                                            \
    {                                                                                \
        constexpr Elements Value1 = Elements::value;                                 \
        switch (element2)                                                            \
        {                                                                            \
            DEFER(ELEMENTS_INT)()(PROCESS_NESTED_ENUM_VALUE)                         \
        };                                                                           \
                                                                                     \
        break;                                                                       \
    }

    switch (element1)
    {
        EXPAND(ELEMENTS(PROCESS_ENUM_VALUE));
    };

    #undef EMPTY
    #undef DEFER
    #undef EXPAND

    #undef ELEMENT_TYPES_INT
    #undef PROCESS_ENUM_VALUE
    #undef PROCESS_NESTED_ENUM_VALUE
}

      

A lot of effort goes into wrestling the preprocessor for recursively extending ELEMENTS here. The basic idea is well described here .

We now declare our handlers as a specialized template function:

template <Elements Element1, Elements Element2>
void process();

template<>
void process<Elements::firstElement, Elements::firstElement>()
{
    //some code 1;
}

...

      

0


source


Why not use if thugs?

if (input1 == A && input2 == B) {
} else if (input1==A && input2 = C) {
} ...

      

That's kind of what you mean.

-1


source







All Articles