Overuse loops to reduce if-nesting

Someday you need to do a series of if / else checks. In the old days, goto was a sandara tool for this.

Since goto is not-go in many code style guides, I sometimes use loops as a replacement, for example:

do { if (a) { doA(); break; } //... if (z) { doZ(); break; } }while(false); //all break jump here 

Is this a good approach? Is there a good C ++ template (e.g. using templates, inheritance, etc.) to implement this without too much overhead?

+5
source share
5 answers

In general, to write supported code, you should use the language tools the way they are intended. Using a loop, which should only be a loop for one iteration, is obviously a deflection not intended for the same set of code-style rules that are primarily unhappy with goto .

There are several tools in the language that make it easier for you to communicate with other programmers and your further self, which you intend to do here.

  • One of them is to pass this code into a separate method and use the return . Pretty funny early returns are also unhappy with some.
  • Another solution might be to use exceptions in which you are trying / catching. Whether this is appropriate for your purpose depends largely on the type of โ€œchecksโ€ you perform.
  • Finally, the if / else chain may not look very elegant, but, on the other hand, is a construct that leaves little room for misinterpretation.
+5
source

Since conditions seem to be unrelated, else if is an option:

 if (a) { doA(); } else if (b) { //... } else if (z) { doZ(); } 
+7
source

Using goto , using continue , using multiple break or using multiple return are all if they are abused, different spaghetti coding options, all of which can be used to create non-conditional branches. Some examples of abuse:

  • goto is considered very bad if it is used for anything other than a jump down, for example, for an error handler. (While this may be an acceptable use of goto , it still starts all the debate related to the loss of a dead horse - the so-called harmful debate, so I would avoid goto only for this reason.)

  • continue non-specifically jumps up, which is considered bad: one definition of spaghetti code is a code that jumps unconditionally, either up or down. This is especially bad when a few continue added to the same loop. In general, the existence of continue is a fairly definite sign of a loop that needs to be rewritten.

  • Multiple break and multiple return can be abused to break out of complex, nested loops, which makes the code difficult to read and maintain. In addition, the break method, as shown in the question, provides the use of a somewhat obscure do-while (false) loop.

Typically, all of these methods are considered bad practice because they can be easily abused. You just need to choose the one that is the least bad: in other words, the most read and least obscure.

I believe that multiple returns are the most readable form, since it can be mixed with some function result variable, which you might want to have anyway:

 result_t func () { if (a) { doA(); return RESULT_A; } ... // you'll need lots of if statements here to justify this program design if (z) { doZ(); return RESULT_Z; } return RESULT_NORMAL; } 

A note on the allocation of resources within a function.

If the above function should free some allocated resources, and it should always do this, you should do it with RAII, so that when a local variable goes out of scope, it cleans itself.

If in some cases it is required to free some allocated resources (for example, if an error occurs), you should not do this inside each if statement (invalid), and you should not implement the 1980s BASIC programmer "by mistake goto". Instead, consider adding a wrapper function outside the main function so that the program is maintained and minimizes clutter:

 result_t wrapper () { stuff = allocate(); // if needed result_t result = func(stuff); if(result == BAD) { deallocate(stuff); } return result; } 
+2
source

Your non-loop is a little offensive to your fellow programmers, but not terribly. You see this code in the implementation of macros and approval registrars.

With templates, of course, the possibilities for abuse expand geometrically. For example, you can write such a thing:

 void doA() { std::cout << "A" << std::endl; } void doB() { std::cout << "B" << std::endl; } void either_or() {} template<class T> void either_or(T&& t) { if (std::get<bool>(t)) { std::get<void(*)()>(t)(); } } template<class T, class...Ts> void either_or(T&& t, Ts&&... ts) { if (std::get<bool>(t)) { std::get<void(*)()>(t)(); } else { either_or(std::forward<Ts>(ts)...); } } auto main() -> int { using namespace std; bool a = false; bool b = true; either_or(make_tuple(a, &doA), make_tuple(b, &doB)); return 0; } 

What is an effective way (after the intervention of the optimizer) to perform the first action, for which the corresponding flag is true, is perhaps more offensive to those involved.

+1
source

IMHO, I do not see anything wrong with using goto . If you know how, where, and why to use goto , your code can be cleaner and more readable.

For example, Linux uses many goto . As @Bartek Banachewicz stated: "The fact that Linux code has gotos doesn't mean it's less bad."

goto not always bad; they have many valid uses, such as breaking a deep nested loop. Using goto in this case will make much cleaner code. This leads to spaghetti code only when abused or used where they should not be similar to C ++ programs.

Note. Unnecessary use of any loop, if-else, break, continue, and no, will result in messy and uncontrollable code. Even if we make this world an un-goto , there will be spaghetti code. It is in the face to make sure that he / she writes a cleaner, readable, optimal code.

The best design is to use exceptions in C ++ programs.

If you cannot use exceptions, then a different design can be used by a different method with several groups grouped together.

-1
source

Source: https://habr.com/ru/post/1234519/


All Articles