3

I have a switch statement where each case has unique code, and some code that is shared between all cases except the default label. Is there a good way to have a shared command between the different case labels?

Edit: code example

switch (c)
{
    case '+':
        command.type = ADD;
        commands.push_back(command);
        break;
    case '-':
        command.type = SUB;
        commands.push_back(command);
        break;
    case '>':
        command.type = INC;
        commands.push_back(command);
        break;
    case '<':
        command.type = DEC;
        commands.push_back(command);
        break;
    case '.':
        command.type = PUT;
        commands.push_back(command);
        break;
    case ',':
        command.type = GET;
        commands.push_back(command);
        break;
    default: break;
iammilind
  • 68,093
  • 33
  • 169
  • 336
Debrugger
  • 92
  • 1
  • 9

5 Answers5

12

Keep a std::map from char to whatever type command.type is.
Let's call it command_table.

Then:

switch (c)
{
    case '+':
    case '-':
    case '>':
    case '<':
    case '.':
    case ',':
        command.type = command_table[c];
        commands.push_back(command);
        break;
    default: break;
}

Or, shorter and with the added benefit that it's harder to forget a case:

auto it = command_table.find(c);
if (it != command_table.end())
{
    command.type = it.second;
    commands.push_back(command);
}
molbdnilo
  • 64,751
  • 3
  • 43
  • 82
6
  • Set a flag to true
  • in the default case of the switch, set the flag to false
  • run the common code if the flag is true.

Something like the following:

bool MyPushBackFlag = true;
switch (c)
{
    case '+':
        command.type = ADD;
        break;
    case '-':
        command.type = SUB;
        break;
    case '>':
        command.type = INC;
        break;
    case '<':
        command.type = DEC;
        break;
    case '.':
        command.type = PUT;
        break;
    case ',':
        command.type = GET;
        break;
    default: MyPushBackFlag = false; break;
}

if (MyPushBackFlag)
     commands.push_back(command);
stefaanv
  • 14,072
  • 2
  • 31
  • 53
  • 4
    `if( flag == true )` I hate this :( – Slava Apr 21 '17 at 14:56
  • @Slava For us it is clear that `if(MyPushBackFlag)` would work, but for OP :( – Khalil Khalaf Apr 21 '17 at 14:57
  • @Slava you hating it does not mean that it is bad. – Khalil Khalaf Apr 21 '17 at 15:02
  • 1
    @KyleKhalaf unnecessary verbose code is bad, while `if( pointer != nullptr )` instead of `if( pointer )` could add readability, comparing to `true` inside `if` is useless. You may write `if ( ( flag == true ) == true )` too as well, why not add more comparison to `true`? – Slava Apr 21 '17 at 15:04
  • @Slava it is just your preference. The confusion for a programmer when seeing `if(flag == true)`, in any programming language, is neglect. My preference is that logic is better than hands on experience with the syntax. I will teach my kids logic before syntax. And OP (student) needed to learn some logic here – Khalil Khalaf Apr 21 '17 at 15:11
  • @KyleKhalaf even if confusion is neglect does not make that code any good, that verbosity does not add any value. – Slava Apr 21 '17 at 15:14
  • @Slava: For a variable with a name like "flag", I'd consider it a corner case. I like code to actually resemble the way I'd read it in my mind using English sentences. For example, I'd never say to myself *"if the pointer, then"* or *"if the vector's empty is true, then"*. I'd say *"if the pointer isn't null, then"* and *"if the vector is empty, then"*. That's why I prefer `if (pointer != nullptr`)` and `if (v.empty())`. Now, as far as "flag" is concerned, I *do* say to myself *"if the flag is true, then..."*, so I don't mind the `== true` here. – Christian Hackl Apr 21 '17 at 15:16
  • @Slava: Which brings me to the thought that the name itself is bad. The variable should be called `IsPushBackNecessary` (or something like that). That name would describe its intent, not its implementation as a boolean flag. And then, the `== true` would actually look bad, and `if (IsPushBackNecessary)` would be clearly better. – Christian Hackl Apr 21 '17 at 15:17
2

sometimes refactoring the code just increases complexity... :)

#include <vector>
#include <array>
#include <iostream>

enum CommandType {
    ADD, SUB, INC, DEC, PUT, GET
};

struct Command {
    CommandType type;
};

std::vector<Command> commands;

using mapping = std::pair<char, CommandType>;

template<class T, class Iter, class Func>
bool dispatch(T &&t, Iter first, Iter last, Func &&f) {
    auto i = std::find_if(first, last, [&t](auto &&pair) { return std::get<0>(pair) == t; });
    if (i == last) {
        return false;
    }
    f(std::get<1>(*i));
    return true;
}

template<class T, std::size_t N, class Func>
bool dispatch(char t, std::array<mapping, N> const &range, Func &&f) {
    return dispatch(t, range.begin(), range.end(), std::forward<Func>(f));
}

bool my_switch(char c) {

    return dispatch(c,
                    std::array<mapping, 6> {{
                                                    {'+', ADD},
                                                    {'-', SUB},
                                                    {'>', INC},
                                                    {'<', DEC},
                                                    {'.', PUT},
                                                    {',', GET}
                                            }}, [](auto type) {
                Command command{};
                command.type = type;
                commands.push_back(command);
                std::cout << "dispatched: " << command.type << std::endl;
            })
           or [](char c) { 
        std::cout << "invalid option " << c << std::endl;
        return false;
    }(c);
}


int main() {
    my_switch('+');
    my_switch('<');
    my_switch('U');
}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
1

I have a switch statement where each case has unique code, and some code that is shared between all cases except the default label.

For your particular example case, where the only difference between different cases is in data, rather than execution, a map is probably more appropriate (see molbdnilo's answer).

In general, where a map is not appropriate (i.e. when the code paths differ in their execution), you could use this primitive control structure, that is seldom used, goto:

switch( c )
{
    case '+': command.type = ADD; break;
    case '-': command.type = SUB; break;
    case '>': command.type = INC; break;
    case '<': command.type = DEC; break;
    case '.': command.type = PUT; break;
    case ',': command.type = GET; break;
    default:
        goto no_match; // or return from function if appropriate
}
commands.push_back( command );
no_match:
//...

This is clearer and easier to read than a flag variable - although that is just my opinion.

Community
  • 1
  • 1
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • 1
    @ Whoever downvoted, how did you find the answer unhelpful? Do you have a suggestion to improve it? – eerorika Apr 21 '17 at 15:31
  • I didn't downvote, but the first thing I thought after having read your answer was this: http://stackoverflow.com/a/379259/1133284 . – Brutus Apr 21 '17 at 15:59
  • Although the solution will work it's far from idiomatic and involves using a language feature that is below bottom on most peoples list. I'm just not seeing any value here. – Captain Obvlious Apr 21 '17 at 16:49
  • 3
    Using `goto` in this case is simpler, cleaner and more efficient than the alternatives. But we rather jump through ridiculous hoops to avoid it because we are so afraid of it? – x4u Apr 22 '17 at 12:36
0

We can have an effect of goto without actually using it.

#define GOTO_END(...) { __VA_ARGS__; } if(false)
#define END {}
switch(c)
{
  case '+': GOTO_END(command.type = ADD)
  case '-': GOTO_END(command.type = SUB) // if `c == '-'` then jump to END
  case '>': GOTO_END(command.type = INC)
  case '<': GOTO_END(command.type = DEC)
  case '.': GOTO_END(command.type = PUT)
  case ',': GOTO_END(command.type = GET)
            END // <--- must not be forgotten
            commands.push_back(command);
  default:  break;        
}
iammilind
  • 68,093
  • 33
  • 169
  • 336