c++ should nested switch statements be avoided?

Started by
17 comments, last by Shyr 8 years ago

(sword example)


I wouldn't make derived classes just to change values. I would have a data table of different kinds of swords, without any separate classes.
Advertisement

If they're the best solution to your problem, use them. Otherwise, use whatever is the best solution.

Switch statements are useful for many types of processing. Compilers are quite good at optimizing them in the general case, although obviously there are times where information you know can lead to better solutions. Internally the can be implemented as jump tables, if/else trees, branching trees, and sometimes even binary searches depending on the number and distribution of case statement values, which the compiler can intelligently choose between based on analysis.

Unless you are applying specific knowledge about the problem, such as knowing one option accounts for 80% of all the items and should be specially handled, switch statements can be a great way to go.

Nested switches can make sense if you have a bunch of processing based on data, and some sub-processing based on additional data to use them.

Consider:


switch(message.type) {
  case MSG_FOO:
    switch(message.subtype) {
      case FOO_TYPEA:
        ...
        break;
      case FOO_TYPEB:
        ... 
    }
 ...
}

There are other alternatives, of course. If you are dealing with code rather than data, inheritance and virtual functions are the typical route for this. And you can implement the methods the compiler uses like if/elseif/elseif trees, branching decision trees, and binary searches to function pointers yourself if you choose.

As for a program that intentionally uses all the levels of nesting the compiler allows, no, I've never seen a productive program attempt to do that. I'm sure there are obfuscated code contests where people have taken advantage of it, and I'm sure code generation tools have occasionally bumped in to that limit. In the ordinary case of things it is uncommon to reach compiler implementation limits.

(sword example)


I wouldn't make derived classes just to change values. I would have a data table of different kinds of swords, without any separate classes.

Of course not. I was merely giving an object-oriented example using C++. Were I to give a more practical demonstration, it would use an unnecessary amount of screen space for what was being conveyed.

Depends on your situation.

If you need a second switch, then you probably should review CAREFULY your design.

However, I recommend using switch cases for constant values that you probably expect. And a second nesting if you really need to operate on inner type (which is also constant and won't be change! Like... int 16/32/64 etc...).

In OOP design I do fully recommend avoiding switch statements. They are a pain in the ass when managing states.

When upgrading your code or adding a new feature, these statements easily become bottlenecks in the design since you need to add a new switch case and then look how to add the new method or function. When there is a big variety of objects. It gets even harder.

The sword example which was given by Shyr is probably the most amatuer way to program. and I did so when I just started.

The derived class is not a good example either because any unique sword has different data but same behavior. So you don't really need another class. Just reuse the base sword class.

The point of it, is to explit as much as you can the reuse of code, and avoid any big switch statmens.

(sword example)


I wouldn't make derived classes just to change values. I would have a data table of different kinds of swords, without any separate classes.


Of course not. I was merely giving an object-oriented example using C++. Were I to give a more practical demonstration, it would use an unnecessary amount of screen space for what was being conveyed.


I understand; although a newcomer won't! We have to be careful with what our demonstrations and examples teach to new programmers. In most of the derived class examples I see that are designed to show an idea to new programmers, it's REALLY often a case where an experienced programmer would say "Nooooo~, an experienced programmer would never do this!". The problem is that the newcomer will see the example and believe that it should be done that way. They don't yet know what good code vs. bad code looks like yet, and they don't know what all of the available options are for solving different problems.

I think that examples are ideal if they can be made so they are both understandable by new programmers and something that an experienced programmer would not object to. We can improve the entire programming community if we're careful with our examples to avoid misunderstanding.

The only time I recall having good reason to use a nested switch statement is in handling user input, where it's common practice to make pretty liberal use of them. This usually looks similar to the example prevented by frob, and is done for the same reason he described. Of course there are ways to prevent them in even that case, but it usually serves no purpose to do so.

Use your own judgement and try to strike a balance between readability and logical structure. Sometimes it just makes more sense to nest things, since whatever you're doing isn't really important enough to warrant its own function, and it makes more sense for it to be part of the current scope. Other times, it hurts readability. Don't blindly listen to know-it-alls who claim you should always do things one way or another way (except for me when I say you should ALWAYS COMMENT YOUR CODE :))

Here's one from the innards of my virtual machine for handling math operations (+, -, * and /) on an exploding number of combinations of different types. I don't think there is anything wrong with this personally, others might disagree.

template<class T> bool operation(State &state, const TypedValue &a, const TypedValue &b, TypedValue &c, Om::Value &res)
{
    switch(a.userType())
    {
        case Om::Type::Int:
        {
            switch(b.userType())
            {
                case Om::Type::Int: return op<T, int>(state, Om::Type::Int, a, b, c, res);
                case Om::Type::Float: return op<T, float>(state, Om::Type::Float, a, b, c, res);

                default: break;
            }

            break;
        }

        case Om::Type::Float:
        {
            switch(b.userType())
            {
                case Om::Type::Int: return op<T, float>(state, Om::Type::Float, a, b, c, res);
                case Om::Type::Float: return op<T, float>(state, Om::Type::Float, a, b, c, res);

                default: break;
            }

            break;
        }

        case Om::Type::String:
        {
            if(T::type == Addition && b.realType() == Om::Type::String)
            {
                c = TypedValue(Om::Type::String, state.allocate<StringEntity>(state.entity<StringEntity>(a.toUint()).text + state.entity<StringEntity>(b.toUint()).text));
                return true;
            }

            break;
        }

        default: break;
    }

    res = Om::ValueProxy::makeError(state, error(a.userType(), b.userType()));
    return false;
}
It is templated on structures like Add, Sub etc, so the same method can be used for all math operations via compile-time polymorphism.


I understand; although a newcomer won't! We have to be careful with what our demonstrations and examples teach to new programmers. In most of the derived class examples I see that are designed to show an idea to new programmers, it's REALLY often a case where an experienced programmer would say "Nooooo~, an experienced programmer would never do this!". The problem is that the newcomer will see the example and believe that it should be done that way. They don't yet know what good code vs. bad code looks like yet, and they don't know what all of the available options are for solving different problems.

I think that examples are ideal if they can be made so they are both understandable by new programmers and something that an experienced programmer would not object to. We can improve the entire programming community if we're careful with our examples to avoid misunderstanding.

When I was learning how to code I truly appreciated simpler examples as opposed to extremely complex ones that made it even more difficult for me to learn. Examples that cannot be understood by the one being taught are pointless because they cannot be absorbed by someone who has no clue what it means. And the purpose of an example is not to provide production code that will satisfy an "experienced programmer", but to illustrate an idea that will help the student understand a concept. Knowing how to code and knowing how to teach others are two very different concepts.

When I was first learning C++, an "experienced programmer" showed us his 300+ lines of production code as an example, asked us to modify them however we liked after a lengthy explanation of what could only sound like a foreign language to those who didn't know C++ at the time, and he became frustrated because we had no idea what to do or what anything did. I now understand that he was showing us a class and pointer logic. That could have been explained in about 10 lines of code. It would have saved us a lot of headache in the start, and maybe we would have been able to actually understand his code if we knew what the underlying concept was. Please remember that everyone has to start somewhere, and that somewhere should not be dictated by the teacher but by the student's current knowledge.

In any case, the original poster asked if he/she should use nested switch, and the answer to that question is no. Yes, you can. But just because you can doesn't mean you should or that it's a good practice. C++ allows the use of "goto" statements, but it is not recommended that you use it just because it's there.

This is because nested switch adds unnecessary complexity to a program and is difficult to maintain. When you write a switch statement, you must take into account the maintainability of the code. It will lead to much code duplication of the cases. Should you need to update your switch with say a new case, you would then have to go back to find and change every single place where you had that switch.

"Messy and difficult to read" code is certainly not clean code and should be avoided. If the OP is saying that his/her code is difficult to read, then we should suggest refactoring rather than ignoring this and justifying reasons to nest switch (make it even messier :) ). Code that is not clean is hard to maintain, and harder for team members who don't understand what your original intentions were (should you work with a team). If you find yourself using multiple switch statements, then you should refactor. If you are using C++ and interested in OOP as a whole, I recommend searching "design patterns" for alternative solutions as well.

Edit:

fixed typo

This topic is closed to new replies.

Advertisement