How to avoid "if" chains?
Asked Answered
J

51

277

Assuming I have this pseudo-code:

bool conditionA = executeStepA();
if (conditionA){
    bool conditionB = executeStepB();
    if (conditionB){
        bool conditionC = executeStepC();
        if (conditionC){
            ...
        }
    }
}

executeThisFunctionInAnyCase();

Functions executeStepX should be executed if and only if the previous succeed. In any case, the executeThisFunctionInAnyCase function should be called at the end. I'm a newbie in programming, so sorry for the very basic question: is there a way (in C/C++ for example) to avoid that long if chain producing that sort of "pyramid of code", at the expense of the code legibility?

I know that if we could skip the executeThisFunctionInAnyCase function call, the code could be simplified as:

bool conditionA = executeStepA();
if (!conditionA) return;
bool conditionB = executeStepB();
if (!conditionB) return;
bool conditionC = executeStepC();
if (!conditionC) return;

But the constraint is the executeThisFunctionInAnyCase function call. Could the break statement be used in some way?

Journal answered 26/6, 2014 at 12:25 Comment(31)
If the ellipsis is the only position where some code has to be added, you can collapse this into a single if statement: if( execA() && execB() && ... )Michael
@FrédéricHamidi wrong wrong wrong! Do not ever say that driving your program flow with exceptions is good! Exceptions are definitely NOT suited for this purpose, for too many reasons.Scrogan
@Piotr, I was spoiled by Python (which actually encourages this). I know exceptions are not supposed to be used for flow control in C++, but is it really flow control here? Couldn't a function returning false be considered as akin to an exceptional situation?Machinate
That depends on the semantics of a program. A false return can be pretty normal.Corrosive
I've edited the code above giving more info, this exclude the "if( execA() && execB() && ... )" possible solution.Journal
@dornhege: True, A false is a normal condition.Journal
I posted the code because it has been requested by some users, but my original question was more general, about nested if statements. Just to avoid somebody now saying "You could remove your temporary dir after...bla bla bla"Journal
@user3253359: the update shows that your "execute in any case" function is really just a cleanup action, corresponding to a C++ destructor. this changes the nature of the question for the second time. how about being more precise when asking.Churchly
@Baum: note that a scopeguard class can be trivially implemented using std::function. Andrei's stuff is more industrial strength library implementation. I'm not so sure that it's better then the simple trivial thing, though, because simplicity is important in itself, as I see it! ;-)Churchly
I've rolled back your question to its first revision. You should not change your question radically after you have received a certain number of questions (> 0), because that will invalidate all the answers given up to that moment and will create confusion. Open a new question instead.Fireside
Related, if not a duplicate: https://mcmap.net/q/110326/-good-c-style-when-checking-lots-of-return-values/10077Autochthonous
This is a case for a goto. (Or, you can create a new function and use the very related return.)Afire
Pretty much the same question was asked on Code Review. You might find the answers there helpful.Calumet
Some people seem so passive aggressive about this, is this a touchy subject? All i see is different opinions about using goto or not. Anyway I deleted my answer as to not upset anybody else, also because better answers have been posted. good luck.Concealment
@FrédéricHamidi I agree, this looks like a typical case of try-finally, however, this would only make sense if the failure is exceptional and not expected.Opus
You might look into monads (aka workflows in the MS world). Here's a nice intro tutorial: Railway Oriented Programming. There isn't support for this in C/C++ in the language, but there might be C++ libraries out there that you could use.Frock
@James: you're so right... I belive that answers should be edited based on question adjustments not the vice versa :) What's the main purpose here? Looking at some first answers I seen that I had to add an additional constraint: it's pseudo-code translated from real code, so please accept an OP mistake and then corrections: I told that I was a newbie..but seems to be not acceptable for SO.Journal
How is this not a duplicate nearly 6 years after Stack Overflow was launched?Ceruse
so many answers waiting for downvote... so little time & points to spend... [I've essentially found 3 good answers here & ~10 lousy, but still receiving upvotes - WHY OH WHY?!]Welterweight
I wish all “newbie programmers” would ask design questions like this.Chere
Would this question be better suited to Programmers?Discharge
@vaxquis That's the effect of question gaining some momentum. It gets answers, views, votes. Sometimes (or, some part of) SO crowd is like lemmings (perhaps of the Lemmings the game variety).Corrective
I think it's safe to say that this is a problem with no single "clean" solution. Some schemes that look good "on paper" will fail miserably when used in "real life" (especially when subjected to modification), others that seem repulsive from a "structure" POV turn out to be fairly robust in reality. Plus there are many different scenarios with different criteria (and different perceivers of those criteria). And the nature of the associated comments and formatting can make a big difference in how things work out.Consonantal
Why are you trying to avoid if chains in the first place?Herson
@ABCplus, what you're looking for is simply a block of code with the continue statement. (As a beginner, you just muddled-up break and continue.) It's utterly, utterly, utterly, utterly, utterly, utterly, utterly, utterly, utterly, utterly, utterly, utterly astounding that this question caused so much discussion.Hooked
Can you please provide an example as an answer? Or is there an answer that best fit what you mean?Journal
@ABC - well yes, I wrote a massive essay about it in my answer below: https://mcmap.net/q/108041/-how-to-avoid-quot-if-quot-chainsHooked
Is this a joke? I've seen how many different ways you can compute a Fibonacci series. This one is far better.Nomism
I have answered an almost identical question here.Tojo
How does it managed to get so many votes???Pekan
The C++ tag is listed on this question, yet I see only C specific solutions. I have little C++ knowledge, but doesn't C++ have exception handling (like try-throw-catch), which could be the appropriate solution for this problem using that language?Touchwood
F
494

You can use an && (logic AND):

if (executeStepA() && executeStepB() && executeStepC()){
    ...
}
executeThisFunctionInAnyCase();

this will satisfy both of your requirements:

  • executeStep<X>() should evaluate only if the previous one succeeded (this is called short circuit evaluation)
  • executeThisFunctionInAnyCase() will be executed in any case
Fireside answered 26/6, 2014 at 12:33 Comment(41)
Sorry, I've edited the code above giving more info, starting from the program I've made. I miss that infoJournal
@user3253359 I've rolled back that edit. See below your question as to the reason.Fireside
This is both semantically correct (indeed, we want ALL conditions to be true) as well as a very good programming technique (short circuit evaluation). Furthermore, this can be used in any complex situation where creating functions would mess up the code.Gytle
+1 indeed expert code. executeStepB won't run if the result of executeStepA is false! Finally I see proper solution.Emitter
This is easily the clearest and cleanest solution in the list of answers. Very nice.Talented
While correct, this might cause confusion. Junior developers might not know about the left-to-right evaluation.Bridwell
@RobAu: Then it will be good for the junior programmers to finally see code that relies on short-cut evaluation and may prompt them to reseach this topic which in turn would help them on their way to eventually become senior programmers. So clearly a win-win: decent code and somebody learned something from reading it.Carrion
This should be the top answerTouristy
@RobAu: This is not a hack taking advantage of some obscure syntax trick, it's highly idiomatic in nearly every programming language, to the point of being undebatably standard practice.Fuzz
The technique does not work very well when there are a number of local variables which would need to be communicated to/from the executeStep methods. It also spreads the code out is what can be an illogical way.Consonantal
This solution only applies if conditions are actually that simple function calls. In real code, those conditions may be 2-5 lines long (and themselves be combinations of many other && and ||) so there is no way you would want to join them into single if statement without screwing readability. And it is not always easy to move those conditions to outer functions, because they may be dependent on a lot of previously calculated local variables, which would create a terrible mess if you try to pass each one as individual argument.Sawicki
Actually from a software-engineer (or architects?) point-of-view, this technique is deemed pretty evil (I do not count myself to them, but as everybody I had a lecture about it as university). According to this lecture, one should avoid all kinds of side-effects in if-statements, because it can cause bugs. Of course, software-architects usually dislike C and C++.Tertius
Agreed that this is the fastest way.Claar
Does && create side effects, I would like to know.Keane
Sadly the short circuit evaluation is not existing on every programming language. For instance on VB.NET it will execute all methods in the if signature, even when the first method returns false, though it will not enter the body of the if statement, of course.Moresque
then again VB.NET is not a programming language, but a bad habit. Also @Aufziehvogel the && is no 'side-effect'. It's the way it has to behave. It actually builds on something very simple: Math formulas. You evaluate from left to right and if you encounter a bracket, you calculate everything inside the bracket and only use the result in your statement.Stunk
@modiX VB.NET does indeed have short circuit evaluation, if you use AndAlso or OrElse instead of just And and Or.Metcalf
While this answer scores an A for brevity, it also scores an F for maintainability & extensibility. If you need to add working variables, logging, or even the barest little bit of logic around the three function calls, this pattern breaks immediately.Sadiesadira
Add the logging in the functions, before return do log("<function name>:"+result); this is the best answer for the question at hand. if you are looking for more complicated multi line ifs, ask a separate question and I'll tell you to have the function return false else "return funcB();" and so on.Meld
Nah, it' not the best answer, your answer about logging is poor (what if the functions you are calling are in a third party library?) and you do not address the issues of local variables or additional logic. While quick, short, and common, this answer points to a technique that is in fact very brittle in real-world code,Sadiesadira
@JohnWu, you just wrap all the code that comes into a 'condition' in a (local) function. Doing logging inside an 'if statement' is what's really poor.Guthrun
I think you have just proven this approach is brittle, since you just added additional functions to support something rather basic. Adding additional functions is listed as a different answer.Sadiesadira
Am using this to do if(n<arr.length && arr.n==...) tests, glad it seems to be good practice.Exaggeration
This can be a nightmare to debug, though. What line did we fail on? On line 27 you say? Too bad there's eight friggin functions on line 27 that might have exploded...Galba
@Galba if (executeStepA() \n && executeStepB() \n && executeStepC()){Ericerica
I don't know about C++, but I'm pretty sure in Java if StepA was on 27, B on 28, and C on 29, it would report all of them on 29.Galba
@michaelb958,corsiKa - with C/C++ it's compiler-dependent and (in my experience) used to collapse all to line 29. Trying with gcc4.8.1, gdb does step to the separate lines, e.g. "next"ing through: #1 if( #2 f() > 0 #3 && g() > 0 #4 && h() > 0 #5 ) when all evaluate true the "next" order is (2,4,3,4,1), when line 3 evaluates false order is (2,4,3,1). A breakpoint on line 4 is hit on the initial (compiler glue) visit and not on the source-visible "h()" visit. So it's helpful but still not quite as easy to debug as separate statements.Happiness
@michaelb958 However clang3.5 does hide the "compiler glue" nicely - for the same cases of line 3 evaluating true or false gives "next" ordering (2,3,4) and (2,3) and breakpoints on those lines are hit in that sequence. So your advise could be suffixed with "and debug with clang-built code" :-)Happiness
This is unfortunately utterly horrible. Quite simply, never write smart-ass, tricky, code. It's completely unmaintainable. it's simply a non-starter. In any commercial situation, the manager or leader would simply say "That's cute. I direct you to the site code-golf if you have time on your hands. Give us a yell when you're back in reality." It's unfortunate that apparently the average SO reader voted this up. No wonder it's so hard to find programmers, at any price, who you can throw on a job and get results.Hooked
@JoeBlow maybe you're having trouble finding programmers because no-one wants to work for someone who thinks short-circuiting of logical AND is 'smart-ass tricky code'?Guthrun
That's funny :) ... but I'm sorry, it's just not simple, maintainable, documentable, changeable.Hooked
This is a clever solution! And yes - clever means bad in production code. It is NOT idomatic, IF should mean we check something, not we do some heavy lifting. Someone searching your Method for a position where the actual code is called, will easily skip-read over if-conditions, since they should contain side-effect free logic!Hamel
@JohnWu Yes, you can easily exceed the useful limits of this idiom. That's not even remotely relevant here, however, because the poster's asking about a situation that's well within those limits. When the scaffolding starts blowing the eyeball test (can you eyeball the control flow and side effects? no? fix the damn code!) there are other ways -- but they're good answers to a different question.Coequal
@Hamel So your preferred style is something like "file.AttemptOpen(name); if(file.IsOpen()) { }" over "if(file.Open(name)) { } "?Happiness
@JoeBlow - short-circuiting can certainly be abused (and a C++ developer who overloads operator&& should probably be quarantined), but I find restrained use, eg for probing optional pointers, concise and obvious, eg "if(bounding_box && bounding_box->width > val) { ... }"Happiness
@TomGoodfellow exactly! This is much clearer in my opinion, since the actual action is done in a stand-alone function call and the IF-Condition only checks logic and performs flow control! :-)Hamel
@jthill: I believe maintainability is almost always relevant.Sadiesadira
If you need access to the booleans, it wouldn't stay that easy though. Imagine those booleans actually being smart pointers, which implicitly can convert to bool, and that you might want to use later on.Callboard
I would alter naming slightly to improve readability; I think if (tryExecuteA() && tryExecuteB() && tryExecuteC()) would better express 'this function attempts something and returns boolean success or failure'.Sash
Why don't you go all the way and drop the last if. It is only there to indicate the air of booleanness. The code would then become even more ideomatic and to the point: \n executeStepA() \n && executeStepB() \n && executeStepC(); \n executeThisFunctionInAnyCase();Raggletaggle
Debugging this can be a pain - but that's an issue with the debugger, not with the solution.Branny
T
361

Just use an additional function to get your second version to work:

void foo()
{
  bool conditionA = executeStepA();
  if (!conditionA) return;

  bool conditionB = executeStepB();
  if (!conditionB) return;

  bool conditionC = executeStepC();
  if (!conditionC) return;
}

void bar()
{
  foo();
  executeThisFunctionInAnyCase();
}

Using either deeply nested ifs (your first variant) or the desire to break out of "part of a function" usually means you do need an extra function.

Trivial answered 26/6, 2014 at 13:24 Comment(30)
+1 finally someone posted a sane answer. This is the most correct, safe and readable way, in my opinion.Illomened
+1 This is a good illustration of the "Single Responsibility Principle". Function foo works its way through a sequence of related conditions and actions. Function bar is cleanly separated from the decisions. If we saw the details of the conditions and actions, it might turn out that foo is still doing too much, but for now this is a good solution.Gavelkind
The downside is that C doesn't have nested functions so if those 3 steps needed to use variables from bar you would be have to manually pass them to foo as parameters. If that is the case and if foo is only called once I would err towards using the goto version to avoid defining two tightly coupled functions that would end up not being very reusable.Pironi
Not sure of the short-circuiting syntax for C, but in C# foo() could be written as if (!executeStepA() || !executeStepB() || !executeStepC()) return Socinus
You should only make methods for things that are in fact seperate functionalities in your program, not just because you like your return statement so much. I would definitely reccomend short circuit evaluation through the && operator, since that can be applies anywhere without the need for meaningless methods.Gytle
@user1598390 Goto's are used all the time, especially in systems programming when you need to unwind a lot of set-up code.Gingili
adding on to lundin's comment: I would be thinking murder if I ever encountered most of these other answers in a codebase I was maintainingTailstock
@user1598390, unwinding is returning. In some cases where foo has called bar which has called baz which has called quux which has called xyzzy, you might find that xyzzy needs to return a result straight back to foo. Think how (in many languages) exceptions allow you to instantly jump back over many interleaved method calls from where the exception is thrown, to a catch block. Exceptions are really just a form of multi-level return statement.Gramicidin
@Gramicidin Then let's go back to GOTO programming.Dawnedawson
The best thing here is simplicity. Analyzing this code involves very little thinking; I can easily see at a glance what's intended and what's going to happen. +1Ingeminate
I'd rather go for Jefffrey's answer at the bottom if (executeStepA() && executeStepB() && executeStepC()) which looks smarter. Jumping and calling another func is rather slower than this one.Emitter
One cold even spare the additional function by replacing "foo/return" with "while(True)/break"Qoph
I can't believe how far I had to scroll to reach this answer. Thought I was going mad for a moment.Uprear
@Pironi In the case they need variables, you simply split the code into more, smaller functions.Illomened
@Lundin: Splitting into more functions is less advantageous if the functions are tightly coupled (not very reusable) and require tons of parameter passing.Pironi
@Pironi Nested functions then. They allow to get rid of too many parameters. I use them all the time and feeling good :)Touristy
@SargeBorsch: Yes, lambdas are the ultimate goto :)Pironi
@Pironi I didn't mention lambdas. They are more general than just nested functions.Touristy
The explicit boolans are useless here. Just do if(!executeStepA()) return. It would be much cleaner.Hyaline
@Hyaline If you put the method call inside the if check, then you have side effects inside an statement (assuming executeStepA has side effects, which I find to be reasonable). I rather like the separation of side effects from checks for which branch to follow, but I suppose that's somewhat a matter of personal preference. We could say that a single boolean variable would be sufficient, though.Ingeminate
I general (there are exceptions) I strenuously avoid using return from within the code body like that. It's very easy to modify a large method, adding more code at the end, and not realize that that code is being bypassed by the returns.Consonantal
@Trivial This is a perfect answer to a new programmer. Yet you might want to consider making your answer by general by mentioning that object-oriented programmers sometimes refactor the ifs into dispatch based on the type of a member variable (such as in the State Pattern or the Interpreter Pattern). Of course they are trading one kind of complexity for another, so they need to examine the design forces! (Smalltalk is full of that approach: I imagine the C++ community is not far behind.)Oswin
@jpmc26: I would also do it like quantdev wrote. Can you please explain the side-effects you are thinking of.Dorren
@HotLicks I'd argue that in that case your methods are too long. The code being skipped would be "executeThisFunctionInAnyCase", which this answer solves quite nicely.Coverup
@ChristianAmmer I'm saying that executeStepA presumably has side effects (modifies some state), just based on the name and usage. (It looks like a "try and return success" sort of pattern.) I'm not fond of changing state and checking some condition at the same time. I prefer my state modification to happen separately, so that it draws more attention. As I said, it's somewhat a matter of personal preference. Nothing to be debated so heavily.Ingeminate
@jpmc26: But if executeStepA modifies some state, this would also happen with the line if (!executeStepA()) return;. Thus if the variable conditionA is only used in the if then it's is really unnecessary.Dorren
@ChristianAmmer In terms of code logic, yes. In terms of readability, I disagree. Putting the call outside the if draws greater attention to it. Drawing greater attention to state modification is a good thing, in my opinion, since it should elicit more caution with modifying the code. This will be the third time I said it's a matter of preference, and honestly, I would hope the compiler would optimize it away. (No idea if it would, though.) I was merely trying to explain why the answerer might have preferred to write it that way. Is there an actual problem with it you're trying to address?Ingeminate
@jpmc26: Sorry, I misunderstood you. I thought you are thinking that possible side effects wouldn't take place if the function is called inside the if. I misunderstood you because I couldn't imagine you find it more readable. Initially I googled what are "side effects in C++" and tried to understand what you are meaning with "side effects inside an statement" (are you talking about something I should know and don't aware about it). Therefore my question and insisting for the correctnes of the function call inside the if.Dorren
@ChristianAmmer My apologies as well. Please forgive my rudeness and harshness. I've had a very stressful day, but that is, of course, no excuse for taking it out of you.Ingeminate
No problem, it was apperently necessary that I finally understood your point of view.Dorren
T
165

Old school C programmers use goto in this case. It is the one usage of goto that's actually encouraged by the Linux styleguide, it's called the centralized function exit:

int foo() {
    int result = /*some error code*/;
    if(!executeStepA()) goto cleanup;
    if(!executeStepB()) goto cleanup;
    if(!executeStepC()) goto cleanup;

    result = 0;
cleanup:
    executeThisFunctionInAnyCase();
    return result;
}

Some people work around using goto by wrapping the body into a loop and breaking from it, but effectively both approaches do the same thing. The goto approach is better if you need some other cleanup only if executeStepA() was successfull:

int foo() {
    int result = /*some error code*/;
    if(!executeStepA()) goto cleanupPart;
    if(!executeStepB()) goto cleanup;
    if(!executeStepC()) goto cleanup;

    result = 0;
cleanup:
    innerCleanup();
cleanupPart:
    executeThisFunctionInAnyCase();
    return result;
}

With the loop approach you would end up with two levels of loops in that case.

Tarttan answered 26/6, 2014 at 12:36 Comment(32)
+1. A lot of people see a goto and immediately think "This is terrible code" but it does have its valid uses.Sapphism
Except this is actually quite messy code, from a maintenance point-of-view. Particularly when there are multiple labels, where the code also turns harder to read. There are more elegant ways to achieve this: use functions.Illomened
-1 I see the goto and I don't even have to think to see that this is terrible code. I once had to maintain such, it's very nasty. The OP suggests a reasonable C language alternative at the end of the question, and I included that in my answer.Churchly
@Cheersandhth.-Alf: I prefer the bike shed painted blue.Insolvency
There's nothing wrong with this limited, self-contained use of goto. But beware, goto is a gateway drug and if you're not careful you will one day realize that no one else eats spaghetti with their feet, and you've been doing it for 15 years starting after you thought "I can just fix this otherwise nightmarish bug with a label ..."Foozle
I have written probably a hundred thousand lines of extremely clear, easy-to-maintain code in this style. The two key things to understand are (1) discipline! establish a clear guideline for the layout of every function and only violate it at extreme need, and (2) understand that what we are doing here is simulating exceptions in a language that does not have them. throw is in many ways worse than goto because with throw it's not even clear from the local context where you're going to end up! Use the same design considerations for goto-style control flow as you would for exceptions.Xuanxunit
+1. I'm not a big fan of single point of entry / single point of exit, and I hardly ever use gotos. But when a cleanup is absolutely needed, the goto can be the clearest and cleanest way of achieving that end.Gruff
-1 - The first example was nice and clean, but the second one with two labels is already starting to get messy, and a good example why goto-based code doesn't scale. If you are using gotos and then find that you need complex exit conditions with two or more labels, then it's probably time to refactor.Stew
As a rule of thumb, gotos that jump forward like this one are not as bad as gotos that jump backwards.Pironi
@this.lau_: For the second part you could define 3 separate labels and have the last two of them point to to the same spot. This lets you keep the execution and cleanup separate while still being a very simple pattern to read and write.Pironi
@this.lau_ If each failure requires cleanup of all the previous successes, then you'll find that refactoring also scales as the number of conditions -- and so, by that criterion, is also not a good solution. Personally, I favor this solution. It's well-known, been used for many decades, exists in much C code, and is as clear as any other solution and better than most.Yeo
RAII is respected with goto there is no reason to do cleanup functions, rather use RAII wrappers and blocks. The RAII wrapper's destructors will be called on block exitMier
Btw the Linux style guide is an amateur-level document, written by someone who clearly has no experience of writing such things. In addition, from a general C canon point-of-view, most of the contents from that document are disputed. So don't quote it in any serious debate as if it was some kind of C authority. If you want to cite coding standards as reference for a certain practice, refer to professional, widely-recognized coding standards such as MISRA-C or CERT C. (As it turns out, CERT C actually supports your answer)Illomened
CERT C reference on this topic. (I personally don't agree with them.)Illomened
-1 for the second example. While I'd accept the first, the second is getting far too messy.Galsworthy
@Illomened Interesting that you would call the Linux style guide an amateur-level document. Just took a look at the git-blame of that document, and it turns out that the part about centralized function exit is written by Linus Torvalds himself. Yes, it may be disputed. Much of what he says is disputed. But I would hesitate to call anything of that "amateur-level". After all, we are talking about a document at the base of the largest and most successful open source project (yes, at the base: a style-guide is one of the most influential documents in any project if it's honored).Tarttan
@cmaster Just read any other coding standard, then use your brain and make up your own opinion. As for the success of Linux, it has nothing to do with that document. It likely has nothing to do with the nature or quality of the source code at all, apart from it being free and available.Illomened
Haven't we learned anything from Apple's goto fail? At least put brackets to avoid that!Sukin
@Sukin Note that I put the goto on the same line as the if. This is precisely to safeguard against errors like goto fail. The danger lies in splitting the condition an the body in a way that allows either one to be deleted by accident. Whether you use braces or oneline if-statements to safeguard against this type of error is a matter of style imho. Note also that even braces don't save you from accidentally deleting the condition if you write the opening brace on its own line as mandated by many coding standards.Tarttan
-1 I'm not against GOTO (I've even fought on wiki to make GOTO article non-POV on the matter of GOTO use, since I do believe that correct uses of GOTO are OK), but I'm against this one particular solution. GOTO is unnecessary in this case. Using either wrapper function or short-circuit produces a) more readable code, b) shorter code, c) easier-to-maintain code.Welterweight
A significant problem with this is that it doesn't work in other languages such as Java.Consonantal
Sounds more like "a significant problem" with Java. This is precisely the solution I would use if I were writing C code, or C++ code in a hurry. (Otherwise, I'd use RAII in C++.) There's just no other way to write COM code and keep all the clean-up readable.Stalder
@cmaster Having the goto on the same line as the if doesn't help in a large project; your code is likely to be run through an automatic source formatter at some point, at which point the line breaks will more than likely be added.Truman
@CodyGray in Java, you'd use try { if (!executeStepA()) return; ... } finally { executeThisFunctionInAnyCase(); }, or using Java 1.7 or higher with AutoCloseable objects try (WhateverClass obj = new WhateverClass()) { if (!obj.executeStepA()) return; ... } with WhateverClass.close() defined to perform the required cleanup. The former syntax is basically equivalent to the gotos, the latter is roughly the same principle as RAII.Truman
If I'm not wrong, isn't this code almost the same as the one that created a big issue with SSL on iOS? nakedsecurity.sophos.com/2014/02/24/… Can create a big mess...Aristides
@Fire-Dragon-DoL Close, but not close enough. See magarciaisaia's comment and my answer to it.Tarttan
I can't believe it: this looks like the typical code pattern used in VB 6 / VBA for error management... :-)Chatelaine
I tend to liken "goto" to a stick of dynamite. There are lots of programmers who are so afraid of it that, when they come to a boulder in the middle of their chosen path, they build complex trestles out and around it when, for both code efficiency and readability, it really makes more sense to just blow it up. This stems, in my opinion, from CS professors who are too lazy to teach their students how to use goto properly, and so simply say that goto automatically means "bad code".Artifice
-1, goto definitely has its usage, but when there's a superior solution to this problem I.E: the accepted answer, there is simply no reason to use this approach.Monomania
Why is goto preferred here over creating a function cleanup()? I'm not asserting it's worse, just asking.Freestyle
@OllieFord You mean like saying return cleanup(); instead of goto cleanup;? Yes, it's possible, but it has some drawbacks: 1. cleanup() can't access local variables of its caller (unless it's an inner function (gnu extension) or C++11 lambda). The point of the cleanup code, however, is to cleanup local stuff. 2. Moving the cleanup code out of the function separates it further from the code that needs the cleanup, which is not good. I think, it's usually cleaner to factor out an inner part of the function including its own cleanup than trying to factor out the cleanup operation.Tarttan
@cmaster Okay, thanks. Though in this case, the 'cleanup' is just some function to always call. I just think I would have done if(!somefunc()) return alwaysfunc(); on each line, and return alwaysfunc(); at the end.Freestyle
S
135

This is a common situation and there are many common ways to deal with it. Here's my attempt at a canonical answer. Please comment if I missed anything and I'll keep this post up to date.

This is an Arrow

What you are discussing is known as the arrow anti-pattern. It is called an arrow because the chain of nested ifs form code blocks that expand farther and farther to the right and then back to the left, forming a visual arrow that "points" to the right side of the code editor pane.

Flatten the Arrow with the Guard

Some common ways of avoiding the Arrow are discussed here. The most common method is to use a guard pattern, in which the code handles the exception flows first and then handles the basic flow, e.g. instead of

if (ok)
{
    DoSomething();
}
else
{
    _log.Error("oops");
    return;
}

... you'd use....

if (!ok)
{
    _log.Error("oops");
    return;
} 
DoSomething(); //notice how this is already farther to the left than the example above

When there is a long series of guards this flattens the code considerably as all the guards appear all the way to the left and your ifs are not nested. In addition, you are visually pairing the logic condition with its associated error, which makes it far easier to tell what is going on:

Arrow:

ok = DoSomething1();
if (ok)
{
    ok = DoSomething2();
    if (ok)
    {
        ok = DoSomething3();
        if (!ok)
        {
            _log.Error("oops");  //Tip of the Arrow
            return;
        }
    }
    else
    {
       _log.Error("oops");
       return;
    }
}
else
{
    _log.Error("oops");
    return;
}

Guard:

ok = DoSomething1();
if (!ok)
{
    _log.Error("oops");
    return;
} 
ok = DoSomething2();
if (!ok)
{
    _log.Error("oops");
    return;
} 
ok = DoSomething3();
if (!ok)
{
    _log.Error("oops");
    return;
} 
ok = DoSomething4();
if (!ok)
{
    _log.Error("oops");
    return;
} 

This is objectively and quantifiably easier to read because

  1. The { and } characters for a given logic block are closer together
  2. The amount of mental context needed to understand a particular line is smaller
  3. The entirety of logic associated with an if condition is more likely to be on one page
  4. The need for the coder to scroll the page/eye track is greatly lessened

How to add common code at the end

The problem with the guard pattern is that it relies on what is called "opportunistic return" or "opportunistic exit." In other words, it breaks the pattern that each and every function should have exactly one point of exit. This is a problem for two reasons:

  1. It rubs some people the wrong way, e.g. people who learned to code on Pascal have learned that one function = one exit point.
  2. It does not provide a section of code that executes upon exit no matter what, which is the subject at hand.

Below I've provided some options for working around this limitation either by using language features or by avoiding the problem altogether.

Option 1. You can't do this: use finally

Unfortunately, as a c++ developer, you can't do this. But this is the number one answer for languages that contain a finally keyword, since this is exactly what it is for.

try
{
    if (!ok)
    {
        _log.Error("oops");
        return;
    } 
    DoSomething(); //notice how this is already farther to the left than the example above
}
finally
{
    DoSomethingNoMatterWhat();
}

Option 2. Avoid the issue: Restructure your functions

You can avoid the problem by breaking the code into two functions. This solution has the benefit of working for any language, and additionally it can reduce cyclomatic complexity, which is a proven way to reduce your defect rate, and improves the specificity of any automated unit tests.

Here's an example:

void OuterFunction()
{
    DoSomethingIfPossible();
    DoSomethingNoMatterWhat();
}

void DoSomethingIfPossible()
{
    if (!ok)
    {
        _log.Error("Oops");
        return;
    }
    DoSomething();
}

Option 3. Language trick: Use a fake loop

Another common trick I see is using while(true) and break, as shown in the other answers.

while(true)
{
     if (!ok) break;
     DoSomething();
     break;  //important
}
DoSomethingNoMatterWhat();

While this is less "honest" than using goto, it is less prone to being messed up when refactoring, as it clearly marks the boundaries of logic scope. A naive coder who cuts and pastes your labels or your goto statements can cause major problems! (And frankly the pattern is so common now I think it clearly communicates the intent, and is therefore not "dishonest" at all).

There are other variants of this options. For example, one could use switch instead of while. Any language construct with a break keyword would probably work.

Option 4. Leverage the object life cycle

One other approach leverages the object life cycle. Use a context object to carry around your parameters (something which our naive example suspiciously lacks) and dispose of it when you're done.

class MyContext
{
   ~MyContext()
   {
        DoSomethingNoMatterWhat();
   }
}

void MainMethod()
{
    MyContext myContext;
    ok = DoSomething(myContext);
    if (!ok)
    {
        _log.Error("Oops");
        return;
    }
    ok = DoSomethingElse(myContext);
    if (!ok)
    {
        _log.Error("Oops");
        return;
    }
    ok = DoSomethingMore(myContext);
    if (!ok)
    {
        _log.Error("Oops");
    }

    //DoSomethingNoMatterWhat will be called when myContext goes out of scope
}

Note: Be sure you understand the object life cycle of your language of choice. You need some sort of deterministic garbage collection for this to work, i.e. you have to know when the destructor will be called. In some languages you will need to use Dispose instead of a destructor.

Option 4.1. Leverage the object life cycle (wrapper pattern)

If you're going to use an object-oriented approach, may as well do it right. This option uses a class to "wrap" the resources that require cleanup, as well as its other operations.

class MyWrapper 
{
   bool DoSomething() {...};
   bool DoSomethingElse() {...}


   void ~MyWapper()
   {
        DoSomethingNoMatterWhat();
   }
}

void MainMethod()
{
    bool ok = myWrapper.DoSomething();
    if (!ok)
        _log.Error("Oops");
        return;
    }
    ok = myWrapper.DoSomethingElse();
    if (!ok)
       _log.Error("Oops");
        return;
    }
}
//DoSomethingNoMatterWhat will be called when myWrapper is destroyed

Again, be sure you understand your object life cycle.

Option 5. Language trick: Use short-circuit evaluation

Another technique is to take advantage of short-circuit evaluation.

if (DoSomething1() && DoSomething2() && DoSomething3())
{
    DoSomething4();
}
DoSomethingNoMatterWhat();

This solution takes advantage of the way the && operator works. When the left hand side of && evaluates to false, the right hand side is never evaluated.

This trick is most useful when compact code is required and when the code is not likely to see much maintenance, e.g you are implementing a well-known algorithm. For more general coding the structure of this code is too brittle; even a minor change to the logic could trigger a total rewrite.

Sadiesadira answered 27/6, 2014 at 0:10 Comment(13)
Finally? C++ does not have a finally clause. An object with a destructor is used in situations where you think you need a finally clause.Insignia
Edited to accommodate the two comments above.Sadiesadira
Maybe it's me, and maybe I'm just weird, but honestly I find nesting to be more readable than the guard pattern. Specifically, your first example, designated "arrow", is far clearer to me than the second "guard" example that you term "much nicer". 'Tis not nicer. My brain has no problem with nesting; programmers like hierarchy.Stalder
With trivial code (e.g. in my example) nested patterns are probably more comprehensible. With real world code (which could span several pages), the guard pattern is objectively easier to read because it will require less scrolling and less eye tracking, e.g. the average distance from { to } is shorter.Sadiesadira
I have seen nested pattern where the code wasn't visible on a 1920 x 1080 screen anymore... Try finding out what error handling code will be performed if the third action failed... I have used do { ... } while (0) instead so you don't need the final break (on the other hand, while (true) { ... } allows "continue" to start all over again.Whirlabout
Excellent answer. A couple of comments, though: you suggest using try ... finally without commenting that many languages (including both the languages tagged in the question) don't support this. Also, the fake loop can be made clearer by using a fake switch statement instead.Truman
Huh, I've never seen switch used that way. And yes I did comment on the lack of finally in c++, in two places actually, in the next section.Sadiesadira
Your last paragraph would be better stated referring to DoSomething2(), because bool ds1 = DoSomething1(); Log(ds1); if(ds1 && ... is what you're currently referring to as "completely rewrite".Righthander
Your option 4 is a memory leak in C++ actually (ignoring the minor syntax error). One doesn't use "new" in this kind of case, just say "MyContext myContext;".Levin
I thank you for attempting to sum up the possible solutions, but the question is clearly asked within the context of C and C++ and options 1, 4, and 4.1 are neither. I would advise you clean it up.Manet
A fair criticism. I've edited the post to emphasize c++ more. I can't help but think of this is a language-neutral problem, though.Sadiesadira
I fear the main reason this answer was voted up, is that it was long! :OHooked
Suggest replacing while(1){...break;} with a do{...}while(0);Moslem
C
61

Just do

if( executeStepA() && executeStepB() && executeStepC() )
{
    // ...
}
executeThisFunctionInAnyCase();

It's that simple.


Due to three edits that each has fundamentally changed the question (four if one counts the revision back to version #1), I include the code example I'm answering to:

bool conditionA = executeStepA();
if (conditionA){
    bool conditionB = executeStepB();
    if (conditionB){
        bool conditionC = executeStepC();
        if (conditionC){
            ...
        }
    }
}

executeThisFunctionInAnyCase();
Churchly answered 26/6, 2014 at 20:21 Comment(8)
I answered this (more concisely) in response to the first version of the question, and it got 20 upvotes before it was deleted by Bill the Lizard, after some commentary and editing by Darkness Races in Orbit.Churchly
@Cheersandhth-Alf: I can't believe it was mod-deleted. That's just too bad. (+1)Photoneutron
My +1 to your courage! (3 times does matter :D).Godewyn
It's completely important that newbie programmers learn about things like order-execution with multiple boolean "ands", how that is different in different languages etc. And this answer is a great pointer to that. But it's just a "non-starter" in normal commercial programming. It's not simple, it's not maintainable, it's not modifiable. Sure, if you're just writing some quick "cowboy code" for yourself, do this. But it just has sort of "no connection to everyday software engineering as practiced today." BTW sorry for the ridiculous "edit-confusion" you had to endure here, @cheers :)Hooked
@JoeBlow: Thank you for a great comment. While I would not use a C level approach in the first place I would have no problem maintaining this code, and I have maintained all kinds of umpteen-hundred-line-function C code (not created by me!). Therefore I do not see the problems you list as problems. In John Wu's answer there is alleged a problem about logging a function result for debugging, and I fail to see any problem about it, it's trivial to do as I see it. But precisely because I disagree and fail to understand, I think it's very important to have this view articulated. So thanks.Churchly
hey @cheers - glancing at your User Page, you're a famous systems programmer! (I was gonna say, I'd only do that when writing systems-programming-like code.) I'm just a humble applications programmer - we make apps with hotels and Facebook links and stuff :) Now ...Hooked
... You see the first "mega-line" of code (If(execute... etc).) To begin with: I need comments - lots of them - for every line of code. Every line of code has to be completely documented. (Don't even mention a whole function or function call!) So given that issue, there's a problem straight away with how to comment that "line" of code. If you glance at my answer, I edited in a footnote re this issue to try to explain what I mean.Hooked
@JoeBlow: I'm with Alf on this - I find the && list much clearer. I commonly break the conditions on to separate lines and add a trailing // explanation to each.... In the end, it's massively less code to look at, and once you understand how && works there's no ongoing mental effort. My impression is that most professional C++ programmers would be familiar with this, but as you say in different industries/projects the focus and experience differs.Pocketknife
M
35

There is actually a way to defer actions in C++: making use of an object's destructor.

Assuming that you have access to C++11:

class Defer {
public:
    Defer(std::function<void()> f): f_(std::move(f)) {}
    ~Defer() { if (f_) { f_(); } }

    void cancel() { f_ = std::function<void()>(); }

private:
    Defer(Defer const&) = delete;
    Defer& operator=(Defer const&) = delete;

    std::function<void()> f_;
}; // class Defer

And then using that utility:

int foo() {
    Defer const defer{&executeThisFunctionInAnyCase}; // or a lambda

    // ...

    if (!executeA()) { return 1; }

    // ...

    if (!executeB()) { return 2; }

    // ...

    if (!executeC()) { return 3; }

    // ...

    return 4;
} // foo
Manet answered 26/6, 2014 at 12:49 Comment(12)
I like this one, didn't knew this way of doing. Btw is mine proper or ugly? Can you tell me?Slugabed
@Mayerz: beauty is in the eye of the beholder :) The main issue with your is that it does not deal with executing some arbitrary code between the calls to executeA, executeB, ... That being said, A regular interface (such as yours) can be very useful to avoid copy-paste errors (such as the goto fail; by Apple...)Manet
This is just complete obfuscation to me. I really don't understand why so many C++ programmers like to solve a problem by tossing as many language features at it as possible, until the point where everyone has forgotten about what problem you were actually solving: they no longer care, because they are now so interested in using all those exotic language features. And from there on, you can keep yourself busy for days and weeks writing meta code, then maintaining the meta code, then write meta code for handling the meta code.Illomened
@Lundin: Well, I don't understand how one can be satisfied with brittle code that will break as soon as an early continue/break/return is introduced or an exception is thrown. On the other hand, this solution is resilient in the face of future maintenance, and only relies on the fact that destructors are executed during unwinding which is one of the most important feature of C++. Qualifying this of exotic while it is the underlying principle that powers all the Standard containers is amusing, to say the least.Manet
@MatthieuM. If you write the code as simple as much, with a simple "divide & conquer" approach that has been around & proven for the past 50 years or more, you can't go wrong. If I'm maintaining a very simple function, with just a few lines of code, which only does one single task, then I'd have to be quite daft to break the code during maintenance. Or are you suggesting that we should write monster functions with 100+ LOC, that perform multiple unrelated tasks? Using destructors isn't exotic: abusing them for the sake of doing something simple in a very complicated way, is exotic indeed.Illomened
To sum it up plainly: there is absolutely no reason whatsoever why you can't throw this whole Defer class out, then make 2 function calls: result = foo(); executeThisFunctionInAnyCase();. Sorry, but those 2 lines are 100% equivalent to all your meta-code.Illomened
@Lundin: The benefit of Matthieu's code is that executeThisFunctionInAnyCase(); will execute even if foo(); throws an exception. When writing exception-safe code, it is good practice to put all such cleanup functions in a destructor.Thermonuclear
@Thermonuclear Then don't throw any exception in foo(). And if you do, catch it. Problem solved. Fix bugs by fixing them, not by writing work-arounds.Illomened
@Lundin: Unfortunately, it's incredibly difficult to know whether foo() throws an exception, as it may call code written by 3rd parties, may be edited later, and is written by people with poor discipline. So, your response is really saying, "wrap everything that happens before a pseudo-finally block in a try-catch." But this makes for very unmaintainable code. The simplest guidelines I've ever encountered for writing code with exceptions was basically, A) all cleanup must be in a destructor and B) exceptions must not escape a destructor. Your proposal leads to more complex guidelines.Thermonuclear
@Thermonuclear No it is not incredibly difficult, because it is a function written by you. If you decide to include some questionable code which you have no idea what it does, then wrap it up in a function of its own and put all defensive programming measures inside that function. The bottom line is that you shouldn't have to be doing this, because any properly library should document what it throws, or even better: it should use no exceptions at all. If a library is crap, don't use it. At any rate, I think a discussion about how to best handle poorly written C++ libraries is quite off-topic.Illomened
@Lundin: the Defer class is a reusable little piece of code that lets you do any end-of-block cleanup, in an exception safe way. it's more commonly known as a scope guard. yes any use of a scope guard can be expressed in other more manual ways, just like any for loop can be expressed as a block and a while loop, which in turn can be expressed with if and goto, which can be expressed in assembly language if you want, or for those who are true masters, by altering the bits in memory via cosmic rays directed by butterfly effect of special short grunts and chants. but, why do that.Churchly
@Illomened But that's why C++ is fun. bro i herd u like meta codeHypsography
P
35

There's a nice technique which doesn't need an additional wrapper function with the return statements (the method prescribed by Itjax). It makes use of a do while(0) pseudo-loop. The while (0) ensures that it is actually not a loop but executed only once. However, the loop syntax allows the use of the break statement.

void foo()
{
  // ...
  do {
      if (!executeStepA())
          break;
      if (!executeStepB())
          break;
      if (!executeStepC())
          break;
  }
  while (0);
  // ...
}
Phatic answered 26/6, 2014 at 16:38 Comment(18)
that's a very nice observation... quite contradictory to the standard use of "continue".... :)Phatic
Using a function with multiple returns is similar, but more readable, in my opinion.Illomened
Yes, definitely it's more readable... however from an efficiency point of view the additional function call (parameter passing and return) overhead can be avoided with the do {} while (0) construct.Phatic
Multiple returns does not require parameter passing. you can return from void function.Laboured
Despite the fact that the wrapper function might not involve parameter passing or returning in this particular case, the function calling overhead (activation record allocation and deallocation in the process call stack) is still there to be considered.Phatic
You still are free to make the function inline. Anyway, this is a nice technique to know, because it helps in more than this problem.Pittman
@Illomened You have to take code locality into account, spreading code out over too many places has it's issues too.Mcclure
In my experience, this is a very unusual idiom. It would take me a while to figure out what the heck was going on, and that's a bad sign when I'm reviewing code. Given that it appears to have no advantages over the other, more common and therefore more readable approaches, I couldn't sign off on it.Stalder
@Phatic Replacing functions with this loops just for the sake of efficiency smells pre-mature optimization. Also, even if the compiler doesn't inline the function, which is likely, the overhead is minimal. You shouldn't let such insignificant performance considerations affect the way you design programs.Illomened
@Mcclure There are perhaps some extreme cases where too many functions would just add clutter. But in most cases, spreading code out in multiple functions is a good thing. Whenever trying to determine if something should be in a function of its own, using common sense will get you far.Illomened
inlining is done if needed/requested automatically by virtually any C/C++ compiler around; thus, I call "efficiency point of view" complete shenanigans in this case. /agree with LundinWelterweight
I agree with @CodyGray that this is a confusing idiom. do introduces a loop, yet this loop's body is not expected to execute multiple times. Very confusing. I'd find switch(1) { case 1: ... } less confusing: it's obvious even without reading to the bottom what it does.Truman
Switch-case is out of contention in this case because a function call or expression can't be an argument for case... I really don't know what's so confusing about a do-while(0) construct... IMHO it's a cheap and effective way to avoid if chains without the need for a wrapper function or exception throwing....Phatic
@Pittman not every language can inlineThereupon
Moreover, frankly speaking, it'd be interesting to know how often do people inline their functions?Phatic
Great, because it is basic. No problem of scope, no separation of code among multiple functions that would not exist otherwise (IMHO functions without a clear meaning are easily the start of problems). Maintainable even against a distract cut&paste is not risky (see the goto-answer hypothetical problem). Frankly it's way more readable then inline functions to me. A do-while(0) shouldn't be harder to read than a while(1)-breakIndebtedness
@Lohoris well, for what it matters, you can't say that every language has do-while neitherIndebtedness
It's utterly, utterly bizarre that anyone would find the basic technique here ("the continue statement") confusing or unusual. Umm ... continue means "leave the current block". It's one of the most fundamental things in programming. You might as well say "Hmm, I'm find the use of an 'if' statement confusing..." or say that about any other control statement. If one is essentially saying "oh, I don't like the 'continue' control statement" I guess that's fine. Totally bizarre.Hooked
S
19

You could also do this:

bool isOk = true;
std::vector<bool (*)(void)> funcs; //vector of function ptr

funcs.push_back(&executeStepA);
funcs.push_back(&executeStepB);
funcs.push_back(&executeStepC);
//...

//this will stop at the first false return
for (auto it = funcs.begin(); it != funcs.end() && isOk; ++it) 
    isOk = (*it)();
if (isOk)
 //doSomeStuff
executeThisFunctionInAnyCase();

This way you have a minimal linear growth size, +1 line per call, and it's easily maintenable.


EDIT: (Thanks @Unda) Not a big fan because you loose visibility IMO :

bool isOk = true;
auto funcs { //using c++11 initializer_list
    &executeStepA,
    &executeStepB,
    &executeStepC
};

for (auto it = funcs.begin(); it != funcs.end() && isOk; ++it) 
    isOk = (*it)();
if (isOk)
 //doSomeStuff
executeThisFunctionInAnyCase();
Slugabed answered 26/6, 2014 at 12:53 Comment(15)
I missed the suggested edit by accident, can you redo it or comment it?Slugabed
It was about the accidental function calls inside push_back(), but you fixed it anyway :)Parttime
@Parttime Ok thanks, I'm at work on IE6... so I have some trouble browsing.Slugabed
I'd use an initialize_list to set up your vector of function pointers.Sleazy
@Mayerz Exactly. One more thing to note : your loop won't stop if a call to one of the function fails.Sleazy
Yep I know, but it wouldnt change isOk result in factSlugabed
I'd love to know why this got a downvote. Assuming the execute steps are indeed symmetric, as they appear to be, then it's clean and maintainable.Mccurry
@Mccurry Thanks I started to wonder if label were better :)Slugabed
Though this might arguably look cleaner. It might be harder to understand for people, and is definitely harder to understand for the compiler!Nonrestrictive
Treating your functions more as data is often a good idea--once you've done it you will also notice subsequent refactorings. Even better is where each piece you are handling is an object reference, not just a function reference--this will give you even more ability to improve your code down the line.Georgenegeorges
Slightly over-engineered for a trivial case, but this technique definitively has a nice feature others don't: You can change the order of execution and [number of] the functions called at runtime, and that is nice :)Ringnecked
@Xocoatzin yes it's overkill for trivial case, but the example is trivial for the sake of clarity :) thanks for your feedbackSlugabed
Another downvote without comment, please at least explain whySlugabed
I would really prefer this one, even though performance wise it might not be optimal. Especially in case these are not just 3 methods, but say 40 or more this is the way to go!Butter
@Butter Thanks, this "data view" of function is, as you say, great when using a bunch of thoses.Slugabed
C
19

Would this work? I think this is equivalent with your code.

bool condition = true; // using only one boolean variable
if (condition) condition = executeStepA();
if (condition) condition = executeStepB();
if (condition) condition = executeStepC();
...
executeThisFunctionInAnyCase();
Cookstove answered 27/6, 2014 at 9:6 Comment(3)
I usually call the variable ok when using the same variable like this.Graphomotor
I would be very interested in knowing why the downvotes. What's going wrong here?Journal
compare ur answer with short circuit approach for cyclomatic complexity.Aristotle
M
15

Assuming the desired code is as I currently see it:

bool conditionA = executeStepA();
if (conditionA){
    bool conditionB = executeStepB();
    if (conditionB){
        bool conditionC = executeStepC();
        if (conditionC){
            ...
        }
    }
}    
executeThisFunctionInAnyCase();

I would say that the correct approach, in that it's the simplest to read and easiest to maintain, would have fewer levels of indentation, which is (currently) the stated purpose of the question.

// Pre-declare the variables for the conditions
bool conditionA = false;
bool conditionB = false;
bool conditionC = false;

// Execute each step only if the pre-conditions are met
conditionA = executeStepA();
if (conditionA)
    conditionB = executeStepB();
if (conditionB)
    conditionC = executeStepC();
if (conditionC) {
    ...
}

// Unconditionally execute the 'cleanup' part.
executeThisFunctionInAnyCase();

This avoids any need for gotos, exceptions, dummy while loops, or other difficult constructs and simply gets on with the simple job at hand.

Mccurry answered 26/6, 2014 at 17:48 Comment(3)
When using loops, its usually acceptable to use return and break to jump out of the loop without needing to introduce extra "flag" variables. In this case, using a goto would be similarly innocuou - remember that you are trading extra goto complexity for extra mutable variable complexity.Pironi
@Pironi The variables were in the original question. There is no extra complexity here. There were assumptions made about the question (e.g. that the variables are need in the redacted code) so they were retained. If they are not needed then the code can be simplified, but given the incomplete nature of the question there is no other assumption which can validly be made.Mccurry
Very useful approach, esp. for use by a self professed newbie, it provides a cleaner solution with no drawbacks. I note it also not does not rely on the steps having the same signature or even being functions rather than blocks. I can see this being used as a first pass refactor even where a more sophisticated approach is valid.Overturf
F
13

Could break statement be used in some way?

Maybe not the best solution but you can put your statements in a do .. while (0) loop and use break statements instead of return.

Fibered answered 26/6, 2014 at 12:27 Comment(7)
It wasn't I who downvoted it, but this would be an abuse of a looping construct for something where the effect is what's currently wanted but would inevitably result in pain. Probably for the next developer who has to maintain it in 2 years time after you've moved on to another project.Mccurry
@Mccurry using do .. while (0) for macro definitions is also abusing loops but it is considered OK.Fibered
Perhaps, but there are cleaner ways to achieve it.Mccurry
@Mccurry the only purpose of my answer is to answer Could break statement be used in some way and the answer is yes, the first words in my answer suggests this may be not the solution to use.Fibered
Nope, you're still not going to taunt me into downvoting you :P Sorry, that's a flippant reply; all I was saying was why someone else might have downvoted.Mccurry
This answer should just be a commentTenant
Might be good to mention in the answer that it is possible, but not a good idea. For macros, it is a known idiom, and since macros are typically small, it is easy to see what it is used for. If it occurs in code directly, its more likely to be confusing. Even made it to thedailywtf.com in at least one case.Offside
S
13

You could put all the if conditions, formatted as you want it in a function of their own, the on return execute the executeThisFunctionInAnyCase() function.

From the base example in the OP, the condition testing and execution can be split off as such;

void InitialSteps()
{
  bool conditionA = executeStepA();
  if (!conditionA)
    return;
  bool conditionB = executeStepB();
  if (!conditionB)
    return;
  bool conditionC = executeStepC();
  if (!conditionC)
    return;
}

And then called as such;

InitialSteps();
executeThisFunctionInAnyCase();

If C++11 lambdas are available (there was no C++11 tag in the OP, but they may still be an option), then we can forgo the seperate function and wrap this up into a lambda.

// Capture by reference (variable access may be required)
auto initialSteps = [&]() {
  // any additional code
  bool conditionA = executeStepA();
  if (!conditionA)
    return;
  // any additional code
  bool conditionB = executeStepB();
  if (!conditionB)
    return;
  // any additional code
  bool conditionC = executeStepC();
  if (!conditionC)
    return;
};

initialSteps();
executeThisFunctionInAnyCase();
Suter answered 26/6, 2014 at 12:32 Comment(0)
S
11

If you dislike goto and dislike do { } while (0); loops and like to use C++ you can also use a temporary lambda to have the same effect.

[&]() { // create a capture all lambda
  if (!executeStepA()) { return; }
  if (!executeStepB()) { return; }
  if (!executeStepC()) { return; }
}(); // and immediately call it

executeThisFunctionInAnyCase();
Staunch answered 29/6, 2014 at 9:23 Comment(2)
if you dislike goto && you dislike do { } while (0) && you like C++ ... Sorry, couldn't resist, but that last condition fails because the question is tagged c as well as c++Mccurry
@Mccurry it's always difficult to please everybody. In my opinion there is no such thing as C/C++ you usually code in either one of those and the usage of the other is frowned.Staunch
S
10

The chains of IF/ELSE in your code is not the language issue, but the design of your program. If you're able to re-factor or re-write your program I'd like to suggest that you look in Design Patterns (http://sourcemaking.com/design_patterns) to find a better solution.

Usually, when you see a lot of IF's & else's in your code , it is an opportunity to implement the Strategy Design Pattern (http://sourcemaking.com/design_patterns/strategy/c-sharp-dot-net) or maybe a combination of other patterns.

I'm sure there're alternatives to write a long list of if/else , but I doubt they will change anything except that the chain will look pretty to you (However, the beauty is in the eye of the beholder still applies to code too:-) ) . You should be concerned about things like (in 6 months when I have a new condition and I don't remember anything about this code , will I be able to add it easily? Or what if the chain changes, how quickly and error-free will I be implement it)

Sympathizer answered 26/6, 2014 at 20:34 Comment(0)
C
9

Have your execute functions throw an exception if they fail instead of returning false. Then your calling code could look like this:

try {
    executeStepA();
    executeStepB();
    executeStepC();
}
catch (...)

Of course I'm assuming that in your original example the execution step would only return false in the case of an error occuring inside the step?

Commissary answered 26/6, 2014 at 16:2 Comment(1)
using exception to control flow is often consider as bad practice and smelly codeDeal
S
9

A lot of good answers already, but most of them seem to tradeoff on some (admittedly very little) of the flexibility. A common approach which doesn't require this tradeoff is adding a status/keep-going variable. The price is, of course, one extra value to keep track of:

bool ok = true;
bool conditionA = executeStepA();
// ... possibly edit conditionA, or just ok &= executeStepA();
ok &= conditionA;

if (ok) {
    bool conditionB = executeStepB();
    // ... possibly do more stuff
    ok &= conditionB;
}
if (ok) {
    bool conditionC = executeStepC();
    ok &= conditionC;
}
if (ok && additionalCondition) {
    // ...
}

executeThisFunctionInAnyCase();
// can now also:
return ok;
Squat answered 27/6, 2014 at 10:55 Comment(3)
Why ok &= conditionX; and not simply ok = conditionX;?Journal
@user3253359 In many cases, yes, you can do that. This is a conceptual demo; in working code we'd try to simplify it as much as possibleSquat
+1 One of the few clean and maintainable answers which works in c, as stipulated in the question.Mccurry
H
9

You just do this..

coverConditions();
executeThisFunctionInAnyCase();

function coverConditions()
 {
 bool conditionA = executeStepA();
 if (!conditionA) return;
 bool conditionB = executeStepB();
 if (!conditionB) return;
 bool conditionC = executeStepC();
 if (!conditionC) return;
 }

99 times of 100, this is the only way to do it.

Never, ever, ever try to do something "tricky" in computer code.


By the way, I'm pretty sure the following is the actual solution you had in mind...

The continue statement is critical in algorithmic programming. (Much as, the goto statement is critical in algorithmic programming.)

In many programming languages you can do this:

-(void)_testKode
    {
    NSLog(@"code a");
    NSLog(@"code b");
    NSLog(@"code c\n");
    
    int x = 69;
    
    {
    
    if ( x == 13 )
        {
        NSLog(@"code d---\n");
        continue;
        }
    
    if ( x == 69 )
        {
        NSLog(@"code e---\n");
        continue;
        }
    
    if ( x == 13 )
        {
        NSLog(@"code f---\n");
        continue;
        }
    
    }
    
    NSLog(@"code g");
    }

(Note first of all: naked blocks like that example are a critical and important part of writing beautiful code, particularly if you are dealing with "algorithmic" programming.)

Again, that's exactly what you had in your head, right? And that's the beautiful way to write it, so you have good instincts.

However, tragically, in the current version of objective-c (Aside - I don't know about Swift, sorry) there is a risible feature where it checks if the enclosing block is a loop.

enter image description here

Here's how you get around that...

-(void)_testKode
    {
    NSLog(@"code a");
    NSLog(@"code b");
    NSLog(@"code c\n");
    
    int x = 69;
    
    do{
    
    if ( x == 13 )
        {
        NSLog(@"code d---\n");
        continue;
        }
    
    if ( x == 69 )
        {
        NSLog(@"code e---\n");
        continue;
        }
    
    if ( x == 13 )
        {
        NSLog(@"code f---\n");
        continue;
        }
    
    }while(false);
    
    NSLog(@"code g");
    }

So don't forget that ..

do { } while(false);

just means "do this block once".

ie, there is utterly no difference between writing do{}while(false); and simply writing {} .

This now works perfectly as you wanted...here's the output...

enter image description here

So, it's possible that's how you see the algorithm in your head. You should always try to write what's in your head. ( Particularly if you are not sober, because that's when the pretty comes out! :) )

In "algorithmic" projects where this happens a lot, in objective-c, we always have a macro like...

#define RUNONCE while(false)

... so then you can do this ...

-(void)_testKode
    {
    NSLog(@"code a");
    int x = 69;
    
    do{
    if ( x == 13 )
        {
        NSLog(@"code d---\n");
        continue;
        }
    if ( x == 69 )
        {
        NSLog(@"code e---\n");
        continue;
        }
    if ( x == 13 )
        {
        NSLog(@"code f---\n");
        continue;
        }
    }RUNONCE
    
    NSLog(@"code g");
    }

There are two points:

a, even though it's stupid that objective-c checks the type of block a continue statement is in, it's troubling to "fight that". So it's a tough decision.

b, there's the question should you indent, in the example, that block? I lose sleep over questions like that, so I can't advise.

Hope it helps.

Hooked answered 5/7, 2014 at 16:51 Comment(4)
You missed the second best voted answer.Aplenty
Did you put the bounty to get more rep? :)Governance
Instead of putting all those comments in the if, you could also use more descriptive function names and put the comments in the functions.Pisistratus
Yuck. I'll take the 1 line solution which reads concisely with short-circuit evaluation (which has been in languages for over 20 years and is well known) over this mess any day. I think we can both agree we are happy not working with each-other.Severen
R
7

In C++ (the question is tagged both C and C++), if you can't change the functions to use exceptions, you still can use the exception mechanism if you write a little helper function like

struct function_failed {};
void attempt(bool retval)
{
  if (!retval)
    throw function_failed(); // or a more specific exception class
}

Then your code could read as follows:

try
{
  attempt(executeStepA());
  attempt(executeStepB());
  attempt(executeStepC());
}
catch (function_failed)
{
  // -- this block intentionally left empty --
}

executeThisFunctionInAnyCase();

If you're into fancy syntax, you could instead make it work via explicit cast:

struct function_failed {};
struct attempt
{
  attempt(bool retval)
  {
    if (!retval)
      throw function_failed();
  }
};

Then you can write your code as

try
{
  (attempt) executeStepA();
  (attempt) executeStepB();
  (attempt) executeStepC();
}
catch (function_failed)
{
  // -- this block intentionally left empty --
}

executeThisFunctionInAnyCase();
Ruebenrueda answered 26/6, 2014 at 19:51 Comment(4)
Refactoring value checks into exceptions is not necessarily a good way to go, there is considerable overhead unwinding exceptions.Sadiesadira
-1 Using exceptions for normal flow in C++ like this is poor programming practice. In C++ exceptions should be reserved for exceptional circumstances.Galsworthy
From the question text (emphasis by me): "Functions executeStepX should be executed if and only if the previous succeed." In other words, the return value is used to indicate failure. That is, this is error handling (and one would hope that failures are exceptional). Error handling is exactly what exceptions were invented for.Ruebenrueda
Nope. Firstly, exceptions were created to allow error propagation, not error handling; secondly, "Functions executeStepX should be executed if and only if the previous succeed." doesn't mean that boolean false returned by previous function indicates an obviously exceptional/erroneous case. Your statement is thus non sequitur. Error handling and flow sanitization can be implemented by many different means, exceptions are a tool allowing error propagation and out-of-place error handling, and excel at that.Welterweight
T
7

For C++11 and beyond, a nice approach might be to implement a scope exit system similar to D's scope(exit) mechanism.

One possible way to implement it is using C++11 lambdas and some helper macros:

template<typename F> struct ScopeExit 
{
    ScopeExit(F f) : fn(f) { }
    ~ScopeExit() 
    { 
         fn();
    }

    F fn;
};

template<typename F> ScopeExit<F> MakeScopeExit(F f) { return ScopeExit<F>(f); };

#define STR_APPEND2_HELPER(x, y) x##y
#define STR_APPEND2(x, y) STR_APPEND2_HELPER(x, y)

#define SCOPE_EXIT(code)\
    auto STR_APPEND2(scope_exit_, __LINE__) = MakeScopeExit([&](){ code })

This will allow you to return early from the function and ensure whatever cleanup code you define is always executed upon scope exit:

SCOPE_EXIT(
    delete pointerA;
    delete pointerB;
    close(fileC); );

if (!executeStepA())
    return;

if (!executeStepB())
    return;

if (!executeStepC())
    return;

The macros are really just decoration. MakeScopeExit() can be used directly.

Tenrec answered 28/6, 2014 at 19:50 Comment(3)
There is no need for macros to make this work. And [=] is usually wrong for a scoped lambda.Sennacherib
Yes, the macros are just for decoration and can be thrown away. But wouldn't you say that capture by value is the safest "generic" approach?Tenrec
no: if your lambda will not persist beyond the current scope where the lambda is created, use [&]: it is safe, and minimally surprising. Capture by value only when the lambda (or copies) could survive longer than the scope at the point of declaration...Sennacherib
U
7

Why is nobody giving the simplest solution ? :D

If all your functions have the same signature then you can do it this way (for C language):

bool (*step[])() = {
    &executeStepA,
    &executeStepB,
    &executeStepC,
    ... 
};

for (int i = 0; i < numberOfSteps; i++) {
    bool condition = step[i]();

    if (!condition) {
        break;
    }
}

executeThisFunctionInAnyCase();

For a clean C++ solution, you should create an interface class that contains an execute method and wraps your steps in objects.
Then, the solution above will look like this:

Step *steps[] = {
    stepA,
    stepB,
    stepC,
    ... 
};

for (int i = 0; i < numberOfSteps; i++) {
    Step *step = steps[i];

    if (!step->execute()) {
        break;
    }
}

executeThisFunctionInAnyCase();
Ukulele answered 10/7, 2014 at 9:1 Comment(0)
G
6

Assuming you don't need individual condition variables, inverting the tests and using the else-falthrough as the "ok" path would allow you do get a more vertical set of if/else statements:

bool failed = false;

// keep going if we don't fail
if (failed = !executeStepA())      {}
else if (failed = !executeStepB()) {}
else if (failed = !executeStepC()) {}
else if (failed = !executeStepD()) {}

runThisFunctionInAnyCase();

Omitting the failed variable makes the code a bit too obscure IMO.

Declaring the variables inside is fine, no worry about = vs ==.

// keep going if we don't fail
if (bool failA = !executeStepA())      {}
else if (bool failB = !executeStepB()) {}
else if (bool failC = !executeStepC()) {}
else if (bool failD = !executeStepD()) {}
else {
     // success !
}

runThisFunctionInAnyCase();

This is obscure, but compact:

// keep going if we don't fail
if (!executeStepA())      {}
else if (!executeStepB()) {}
else if (!executeStepC()) {}
else if (!executeStepD()) {}
else { /* success */ }

runThisFunctionInAnyCase();
Graphomotor answered 27/6, 2014 at 19:52 Comment(0)
A
6

As Rommik mentioned, you could apply a design pattern for this, but I would use the Decorator pattern rather than Strategy since you are wanting to chain calls. If the code is simple, then I would go with one of the nicely structured answers to prevent nesting. However, if it is complex or requires dynamic chaining, then the Decorator pattern is a good choice. Here is a yUML class diagram:

yUML class diagram

Here is a sample LinqPad C# program:

void Main()
{
    IOperation step = new StepC();
    step = new StepB(step);
    step = new StepA(step);
    step.Next();
}

public interface IOperation 
{
    bool Next();
}

public class StepA : IOperation
{
    private IOperation _chain;
    public StepA(IOperation chain=null)
    {
        _chain = chain;
    }

    public bool Next() 
    {
        bool localResult = false;
        //do work
        //...
        // set localResult to success of this work
        // just for this example, hard coding to true
        localResult = true;
        Console.WriteLine("Step A success={0}", localResult);

        //then call next in chain and return
        return (localResult && _chain != null) 
            ? _chain.Next() 
            : true;
    }
}

public class StepB : IOperation
{
    private IOperation _chain;
    public StepB(IOperation chain=null)
    {
        _chain = chain;
    }

    public bool Next() 
    {   
        bool localResult = false;

        //do work
        //...
        // set localResult to success of this work
        // just for this example, hard coding to false, 
            // to show breaking out of the chain
        localResult = false;
        Console.WriteLine("Step B success={0}", localResult);

        //then call next in chain and return
        return (localResult && _chain != null) 
            ? _chain.Next() 
            : true;
    }
}

public class StepC : IOperation
{
    private IOperation _chain;
    public StepC(IOperation chain=null)
    {
        _chain = chain;
    }

    public bool Next() 
    {
        bool localResult = false;
        //do work
        //...
        // set localResult to success of this work
        // just for this example, hard coding to true
        localResult = true;
        Console.WriteLine("Step C success={0}", localResult);
        //then call next in chain and return
        return (localResult && _chain != null) 
            ? _chain.Next() 
            : true;
    }
}

The best book to read on design patterns, IMHO, is Head First Design Patterns.

Annabell answered 28/6, 2014 at 18:47 Comment(2)
What is the benefit of this over something like Jefffrey's answer?Nannienanning
far more change resilient, when the requirements change this approach is simpler to manage without a lot of domain knowledge. Especially when you consider how deep and long some sections of nested ifs can get. It can all get very fragile and thus high risk to work with. Don't get me wrong some optimization scenarios may result in you ripping this out and going back to ifs but 99% of the time this is fine. But the point is when you get to that level you don't care about maintainability you need the performance.Aggiornamento
F
6

If your code is as simple as your example and your language supports short-circuit evaluations, you could try this:

StepA() && StepB() && StepC() && StepD();
DoAlways();

If you are passing arguments to your functions and getting back other results so that your code cannot be written in the previous fashion, many of the other answers would be better suited to the problem.

Footworn answered 1/7, 2014 at 13:46 Comment(2)
In fact I edited my question to better explain the topic, but it was rejected to do not invalidate most answers. :\Journal
I'm a new user in SO, and a newbie programmer. 2 questions then: is there the risk that an another question like the one you said will be marked as duplicated because of THIS question? Another point is: how could a newbie SO user/programmer choose the best answer between all there (almost good I suppose..)?Journal
I
5

Several answers hinted at a pattern that I saw and used many times, especially in network programming. In network stacks there is often a long sequence of requests, any of which can fail and will stop the process.

The common pattern was to use do { } while (false);

I used a macro for the while(false) to make it do { } once; The common pattern was:

do
{
    bool conditionA = executeStepA();
    if (! conditionA) break;
    bool conditionB = executeStepB();
    if (! conditionB) break;
    // etc.
} while (false);

This pattern was relatively easy to read, and allowed objects to be used that would properly destruct and also avoided multiple returns making stepping and debugging a bit easier.

Insignia answered 27/6, 2014 at 15:11 Comment(0)
P
5

This looks like a state machine, which is handy because you can easily implement it with a state-pattern.

In Java it would look something like this:

interface StepState{
public StepState performStep();
}

An implementation would work as follows:

class StepA implements StepState{ 
    public StepState performStep()
     {
         performAction();
         if(condition) return new StepB()
         else return null;
     }
}

And so on. Then you can substitute the big if condition with:

Step toDo = new StepA();
while(toDo != null)
      toDo = toDo.performStep();
executeThisFunctionInAnyCase();
Plumlee answered 30/6, 2014 at 17:10 Comment(0)
S
4

Because you also have [...block of code...] between executions, I guess you have memory allocation or object initializations. In this way you have to care about cleaning all you already initialized at exit, and also clean it if you will meet problem and any of functions will return false.

In this case, best what I had in my experience (when I worked with CryptoAPI) was creating small classes, in constructor you initialize your data, in destructor you uninitialize it. Each next function class have to be child of previous function class. If something went wrong - throw exception.

class CondA
{
public:
    CondA() { 
        if (!executeStepA()) 
            throw int(1);
        [Initialize data]
    }
    ~CondA() {        
        [Clean data]
    }
    A* _a;
};

class CondB : public CondA
{
public:
    CondB() { 
        if (!executeStepB()) 
            throw int(2);
        [Initialize data]
    }
    ~CondB() {        
        [Clean data]
    }
    B* _b;
};

class CondC : public CondB
{
public:
    CondC() { 
        if (!executeStepC()) 
            throw int(3);
        [Initialize data]
    }
    ~CondC() {        
        [Clean data]
    }
    C* _c;
};

And then in your code you just need to call:

shared_ptr<CondC> C(nullptr);
try{
    C = make_shared<CondC>();
}
catch(int& e)
{
    //do something
}
if (C != nullptr)
{
   C->a;//work with
   C->b;//work with
   C->c;//work with
}
executeThisFunctionInAnyCase();

I guess it is best solution if every call of ConditionX initialize something, allocs memory and etc. Best to be sure everything will be cleaned.

Sillsby answered 26/6, 2014 at 13:10 Comment(0)
R
4

It's seems like you want to do all your call from a single block. As other have proposed it, you should used either a while loop and leave using break or a new function that you can leave with return (may be cleaner).

I personally banish goto, even for function exit. They are harder to spot when debugging.

An elegant alternative that should work for your workflow is to build a function array and iterate on this one.

const int STEP_ARRAY_COUNT = 3;
bool (*stepsArray[])() = {
   executeStepA, executeStepB, executeStepC
};

for (int i=0; i<STEP_ARRAY_COUNT; ++i) {
    if (!stepsArray[i]()) {
        break;
    }
}

executeThisFunctionInAnyCase();
Remodel answered 26/6, 2014 at 15:54 Comment(2)
Fortunately, the debugger spots them for you. If you're debugging and not single-stepping through code, you're doing it wrong.Stalder
I don't understand what you mean, neither why I couldn't use single-stepping ?Remodel
C
4

Here's a trick I've used on several occasions, in both C-whatever and Java:

do {
    if (!condition1) break;
    doSomething();
    if (!condition2) break;
    doSomethingElse()
    if (!condition3) break;
    doSomethingAgain();
    if (!condition4) break;
    doYetAnotherThing();
} while(FALSE);  // Or until(TRUE) or whatever your language likes

I prefer it over nested ifs for the clarity of it, especially when properly formatted with clear comments for each condition.

Consonantal answered 27/6, 2014 at 17:33 Comment(1)
In Java I would just solve this with returns and a finally block.Dredger
C
4

To improve on Mathieu's C++11 answer and avoid the runtime cost incurred through the use of std::function, I would suggest to use the following

template<typename functor>
class deferred final
{
public:
    template<typename functor2>
    explicit deferred(functor2&& f) : f(std::forward<functor2>(f)) {}
    ~deferred() { this->f(); }

private:
    functor f;
};

template<typename functor>
auto defer(functor&& f) -> deferred<typename std::decay<functor>::type>
{
    return deferred<typename std::decay<functor>::type>(std::forward<functor>(f));
}

This simple template class will accept any functor that can be called without any parameters, and does so without any dynamic memory allocations and therefore better conforms to C++'s goal of abstraction without unnecessary overhead. The additional function template is there to simplify use by template parameter deduction (which is not available for class template parameters)

Usage example:

auto guard = defer(executeThisFunctionInAnyCase);
bool conditionA = executeStepA();
if (!conditionA) return;
bool conditionB = executeStepB();
if (!conditionB) return;
bool conditionC = executeStepC();
if (!conditionC) return;

Just as Mathieu's answer this solution is fully exception safe, and executeThisFunctionInAnyCase will be called in all cases. Should executeThisFunctionInAnyCase itself throw, destructors are implicitly marked noexceptand therefore a call to std::terminate would be issued instead of causing an exception to be thrown during stack unwinding.

Connelly answered 7/7, 2014 at 17:28 Comment(2)
+1 I was looking for this answer so I wouldn't have to post it. You should perfect-forward functor in deferred'd constructor, no need to force a move.Sennacherib
@Yakk changed the constructor to a forwarding constructorConnelly
Z
3

an interesting way is to work with exceptions.

try
{
    executeStepA();//function throws an exception on error
    ......
}
catch(...)
{
    //some error handling
}
finally
{
    executeThisFunctionInAnyCase();
}

If you write such code you are going somehow in the wrong direction. I wont see it as "the problem" to have such code, but to have such a messy "architecture".

Tip: discuss those cases with a seasoned developer which you trust ;-)

Zennas answered 2/7, 2014 at 11:49 Comment(1)
I think this idea cannot replace every if-chain. Anyway, in many cases, this is a very good approach!Ellerey
B
3

Another approach - do - while loop, even though it was mentioned before there was no example of it which would show how it looks like:

do
{
    if (!executeStepA()) break;
    if (!executeStepB()) break;
    if (!executeStepC()) break;
    ...

    break; // skip the do-while condition :)
}
while (0);

executeThisFunctionInAnyCase();

(Well there's already an answer with while loop but do - while loop does not redundantly check for true (at the start) but instead at the end xD (this can be skipped, though)).

Brigitte answered 7/7, 2014 at 22:0 Comment(7)
Hey Zaffy - this answer has a massive explanation of the do{}while(false) approach. https://mcmap.net/q/108041/-how-to-avoid-quot-if-quot-chains Two other answers also mentioned it.Hooked
It's a fascinating question whether it's more elegant to use CONTINUE or BREAK in this situation!Hooked
Hey @JoeBlow I saw all the answers... just wanted to show instead of talking about it :)Brigitte
My first answer here I said "Nobody has mentioned THIS ..." and immediately someone kindly pointed out it was the 2nd top answer :)Hooked
@JoeBlow Eh, you're right. I'll try to fix that. I feel like ... xD Thank you anyway, next time i'll pay attetion a little more :)Brigitte
I wouldn't worry! I just changed and edit all the other stuff on the page, so it looked like mine was first! :) (Just joking.) The overwhelming fact is, as you pointed out, it's really bizarre that on such a simple obvious question [the answer is just what you say here] there has been so much talk about it and essentially 'totally wrong' answers. It's totally bizarre.Hooked
just FWIW if you read through my answer, we usually make a macro like "ONCE" (ie while(false) ) for just this situation.Hooked
V
2

In certain special situations a virtual inheritance tree and virtual method calls may handle your decision tree logic.

objectp -> DoTheRightStep();

I have met situations, when this worked like a magic wand. Of course this makes sense if your ConditionX can be consistently translated into "object Is A" Conditions.

Velites answered 29/6, 2014 at 17:1 Comment(0)
N
2

You can use a "switch statement"

switch(x)
{
  case 1:
    //code fires if x == 1
    break;
  case 2:
    //code fires if x == 2
    break;

  ...

  default:
    //code fires if x does not match any case
}

is equivalent to:

if (x==1)
{
  //code fires if x == 1
}
else if (x==2)
{
  //code fires if x == 2
}

...

else
{
  //code fires if x does not match any of the if's above
}

However, I would argue that it's not necessary to avoid if-else-chains. One limitation of switch statements is that they only test for exact equality; that is you can't test for "case x<3" --- in C++ that throws an error & in C it may work, but behave unexpected ways, which is worse than throwing an error, because your program will malfunction in unexpected ways.

Nadene answered 30/6, 2014 at 15:18 Comment(1)
This doesn't address the original question in any way.Mccurry
H
2

Just a side note; when an if scope always causes a return (or break in a loop), then don't use an else statement. This can save you a lot of indentation overall.

Heads answered 2/7, 2014 at 12:59 Comment(0)
E
2

An easy solution is using a condition boolean variable, and the same one can be reused over and over again in order to check all the results of the steps being executed in sequence:

    bool cond = executeStepA();
    if(cond) cond = executeStepB();
    if(cond) cond = executeStepC();
    if(cond) cond = executeStepD();

    executeThisFunctionInAnyCase();

Not that it was not necessary to do this beforehand: bool cond = true; ... and then followed by if(cond) cond = executeStepA(); The cond variable can be immediately assigned to the result of executeStepA(), therefore making the code even shorter and simpler to read.

Another more peculiar but fun approach would be this (some might think that this is a good candidate for the IOCCC though, but still):

    !executeStepA() ? 0 :
      !executeStepB() ? 0 :
      !executeStepC() ? 0 :
      !executeStepD() ? 0 : 1 ;

    executeThisFunctionInAnyCase();

The result is exactly the same as if we did what the OP posted, i.e.:

    if(executeStepA()){
        if(executeStepB()){
            if(executeStepC()){
                if(executeStepD()){
                }
            }
        }
    }

    executeThisFunctionInAnyCase();
Emu answered 13/7, 2014 at 1:25 Comment(4)
This duplicates Krumia's answer.Mccurry
Actually, this approach uses 1 less line of code for the same result.Emu
Perhaps it does, but then it also assumes that the OP did not wish to use cond1, cond2 and cond3 later on for some reason outside of the fragment we'd been shown.Mccurry
As you wish. I have added another approach in order to make this post more different :-) Feel free to comment on that one.Emu
H
2

An alternative solution would be to define an idiom through macro hacks.

 #define block for(int block = 0; !block; block++)

Now, a "block" can be exited with break, in the same way as for(;;) and while() loops. Example:

int main(void) {

    block {
       if (conditionA) {
          // Do stuff A...
          break; 
       }
       if (conditionB) {
          // Do stuff B...
          break; 
       }
       if (conditionC) {
          // Do stuff C...
          break; 
       }
       else {
         // Do default stuff...
       }
    } /* End of "block" statement */
    /* --->   The "break" sentences jump here */

    return 0;
} 

In despite of the "for(;;)" construction, the "block" statement is executed just once.
This "blocks" are able to be exited with break sentences.
Hence, the chains of if else if else if... sentences are avoided.
At most, one lastly else can hang at the end of the "block", to handle "default" cases.

This technique is intended to avoid the typical and ugly do { ... } while(0) method.
In the macro block it is defined a variable also named block defined in such a way that exactly 1 iteration of for is executed. According to the substitution rules for macros, the identifier block inside the definition of the macro block is not recursively replaced, therefore block becomes an identifier inaccesible to the programmer, but internally works well to control de "hidden" for(;;) loop.

Moreover: these "blocks" can be nested, since the hidden variable int block would have different scopes.

Hetaera answered 13/7, 2014 at 5:41 Comment(1)
I personally like programming this way. You can often use macros to make effective, well, control structures that do not exist in the language.Hooked
C
1

What about just moving the conditional stuff to the else as in:

if (!(conditionA = executeStepA()){}
else if (!(conditionB = executeStepB()){}
else if (!(conditionC = executeStepC()){}
else if (!(conditionD = executeStepD()){}

This does solve the indentation problem.

Creosote answered 26/6, 2014 at 16:47 Comment(6)
That's simply not indenting, rather than "solving" the apparent problem of complexity.Mccurry
And it's horribly difficult to read -- the intent is very unclear.Planetary
Also, assignment inside conditions is bad practice. Good compilers warn against this, to prevent bugs where you type = when == was intended.Illomened
well that is what the extra brackets are for.Creosote
A little commenting solves the intent part, otherwise I think this solves the indent part nicely, which was part of OP's question, no?Graphomotor
You could use ; instead of {}Brigitte
R
1

As @Jefffrey said, you can use the conditional short-circuit feature in almost every language, I personally dislike conditional statements with more than 2 condition (more than a single && or ||), just a matter of style. This code does the same (and probably would compile the same) and it looks a bit cleaner to me. You don't need curly braces, breaks, returns, functions, lambdas (only c++11), objects, etc. as long as every function in executeStepX() returns a value that can be cast to true if the next statement is to be executed or false otherwise.

if (executeStepA())
 if (executeStepB())
  if (executeStepC())
   //...
    if (executeStepN()); // <-- note the ';'

executeThisFunctionInAnyCase();

Any time any of the functions return false, none of the next functions are called.

I liked the answer of @Mayerz, as you can vary the functions that are to be called (and their order) in runtime. This kind of feels like the observer pattern where you have a group of subscribers (functions, objects, whatever) that are called and executed whenever a given arbitrary condition is met. This might be an over-kill in many cases, so use it wisely :)

Ringnecked answered 2/7, 2014 at 8:56 Comment(0)
H
1

After reading all the answers, I want to provide one new approach, which could be quite clear and readable in the right circumstances: A State-Pattern.

If you pack all Methods (executeStepX) into an Object-class, it can have an Attribute getState()

class ExecutionChain
{
    public:
        enum State
        {
          Start,
          Step1Done,
          Step2Done,
          Step3Done,
          Step4Done,
          FinalDone,
        };
        State getState() const;

        void executeStep1();
        void executeStep2();
        void executeStep3();
        void executeStep4();
        void executeFinalStep();
    private:
        State _state;
};

This would allow you to flatten your execution code to this:

void execute
{
    ExecutionChain chain;

    chain.executeStep1();

    if ( chain.getState() == Step1Done )
    {
        chain.executeStep2();
    }

    if ( chain.getState() == Step2Done )
    {
        chain.executeStep3();
    }

    if ( chain.getState() == Step3Done )
    {
        chain.executeStep4();
    }

    chain.executeFinalStep();
}

This way it is easily readable, easy to debug, you have a clear flow control and can also insert new more complex behaviors (e.g. execute Special Step only if at least Step2 is executed)...

My problem with other approaches like ok = execute(); and if (execute()) are that your code should be clear and readable like a flow-diagram of what is happening. In the flow diagram you would have two steps: 1. execute 2. a decision based on the result

So you shouldn't hide your important heavy-lifting methods inside of if-statements or similar, they should stand on their own!

Hamel answered 8/7, 2014 at 11:40 Comment(0)
S
1
[&]{
  bool conditionA = executeStepA();
  if (!conditionA) return; // break
  bool conditionB = executeStepB();
  if (!conditionB) return; // break
  bool conditionC = executeStepC();
  if (!conditionC) return; // break
}();
executeThisFunctionInAnyCase();

We create an anonymous lambda function with implicit reference capture, and run it. The code within it runs immediately.

When it wants to stop, it simply returns.

Then, after it runs, we run executeThisFunctionInAnyCase.

return within the lambda is a break to end of block. Any other kind of flow control just works.

Exceptions are left alone -- if you want to catch them, do it explicitly. Be careful about running executeThisFunctionInAnyCase if exceptions are thrown -- you generally do not want to run executeThisFunctionInAnyCase if it can throw an exception in an exception handler, as that results in a mess (which mess will depend on the language).

A nice property of such capture based inline functions is you can refactor existing code in place. If your function gets really long, breaking it down into component parts is a good idea.

A variant of this that works in more languages is:

bool working = executeStepA();
working = working && executeStepB();
working = working && executeStepC();
executeThisFunctionInAnyCase();

where you write individual lines that each short-circuit. Code can be injected between these lines, giving you multiple "in any case", or you can do if(working) { /* code */ } between execution steps to include code that should run if and only if you haven't already bailed out.

A good solution to this problem should be robust in the face of adding new flow control.

In C++, a better solution is throwing together a quick scope_guard class:

#ifndef SCOPE_GUARD_H_INCLUDED_
#define SCOPE_GUARD_H_INCLUDED_
template<typename F>
struct scope_guard_t {
  F f;
  ~scope_guard_t() { f(); }
};
template<typename F>
scope_guard_t<F> scope_guard( F&& f ) { return {std::forward<F>(f)}; }
#endif

then in the code in question:

auto scope = scope_guard( executeThisFunctionInAnyCase );
bool conditionA = executeStepA();
if (!conditionA) return;
bool conditionB = executeStepB();
if (!conditionB) return;
bool conditionC = executeStepC();
if (!conditionC) return;

and the destructor of scope automaticlaly runs executeThisFunctionInAnyCase. You can inject more and more such "at end of scope" (giving each a different name) whenever you create a non-RAII resource that needs cleaning up. It can also take lambdas, so you can manipulate local variables.

Fancier scope guards can support aborting the call in the destructor (with a bool guard), block/allow copy and move, and support type-erased "portable" scope-guards that can be returned from inner contexts.

Sennacherib answered 8/7, 2014 at 15:11 Comment(0)
C
1

Very simple.

if ((bool conditionA = executeStepA()) && 
    (bool conditionB = executeStepB()) &&
    (bool conditionC = executeStepC())) {
   ...
}
executeThisFunctionInAnyCase();

This will preserve the boolean variables conditionA, conditionB and conditionC as well.

Cogswell answered 14/7, 2014 at 8:40 Comment(0)
F
0

Don't. Sometimes you need the complexity. The trick is how you do it. Having the "what you do when the condition exists" may take up some room, making the if statement tree appear larger than it really is. So instead of doing things if a condition is set, just set a variable to a specific value for that case( enumeration or number, like 10,014. After the if tree, then have a case statement, and for that specific value, do whatever you would have done in the if tree. It will lighten up the tree. if x1 if x2 if x3 Var1:=100016; endif endif end if case var=100016 do case 100016 things...

Fong answered 27/6, 2014 at 15:41 Comment(0)
T
0
while(executeStepA() && executeStepB() && executeStepC() && 0);
executeThisFunctionInAnyCase();

executeThisFunctionInAnyCase() had to be executed in any case even if the other functions do not complete.

The while statement:

while(executeStepA() && executeStepB() && executeStepC() && 0)

will execute all the functions and will not loop as its a definite false statement. This can also be made to retry a certain times before quitting.

Toms answered 7/7, 2014 at 16:49 Comment(4)
Where I work, "clever" is an insult, only one step less severe than "fragile" or "brittle". I agree, this code is "clever".Mccurry
the while is also completely redundant, just a short circuit expression will do;Nobleminded
@ClickRick, If you think about compiler optimizations; while statement works differently than you think ...Toms
Optimizations will cancel the while loop?Toms
R
0

Fake loops already got mentioned, but I didn't see the following trick in the answers given so far: You can use a do { /* ... */ } while( evaulates_to_zero() ); to implement a two-way early-out breaker. Using break terminates the loop without going through evaluating the condition statement, whereas a continue will evaulate the condition statement.

You can use that if you have two kinds of finalization, where one path must do a little more work than the other:

#include <stdio.h>
#include <ctype.h>

int finalize(char ch)
{
    fprintf(stdout, "read a character: %c\n", (char)toupper(ch));

    return 0;
}

int main(int argc, char *argv[])
{
    int ch;
    do {
        ch = fgetc(stdin);
        if( isdigit(ch) ) {
            fprintf(stderr, "read a digit (%c): aborting!\n", (char)ch);
            break;
        }
        if( isalpha(ch) ) {
            continue;
        }
        fprintf(stdout, "thank you\n");
    } while( finalize(ch) );

    return 0;
}

Executing this gives the following session protocol:

dw@narfi ~/misc/test/fakeloopbreak $ ./fakeloopbreak 
-
thank you
read a character: -

dw@narfi ~/misc/test/fakeloopbreak $ ./fakeloopbreak 
a
read a character: A

dw@narfi ~/misc/test/fakeloopbreak $ ./fakeloopbreak 
1
read a digit (1): aborting!
Rebbecarebbecca answered 9/7, 2014 at 0:2 Comment(8)
Daten, what's up. You've made an excellent point that you can use break versus continue, depending on whether you want the little thingy on the end run. {However, this is appalling code-golf programming with no room in the real world! It's totally inconceivable you'd do this in ordinary code for making airplanes fly or whatever. ;-)Hooked
@JoeBlow: Maybe not airplanes, but you can find hundreds of this constructs in the firmware of a device that in the long term might end up in medical products (it's aimed at medical research so far). Usually those are part of hardware configuration transactions, where the successful completion of the transaction doesn't require rollback, while transaction aborts needs cleanup and the detection of a hardware failure must put the device into a fail-safe state, without tearing down too much configuration state, so that the issue can be debugged / logged to diagnostics by the panic code.Rebbecarebbecca
yeah it's too tricky. it's tough because there'd be no corporate value in the code, and it will be difficult for others to work on it (who don't have your deep systems programming background). what you're saying is that: in some cases you want to run a routine (perhaps "cleanup"). so have a routine "cleanup" and where you want to run it, call it. (and have a huge comment "in this case need to run clean up" have dozens of lines of comments explaining why you need to call it in certain case, and why not in others.)...Hooked
specifically, in code where lives are at risk i try to avoid single-paths. so you'd have like specifically TWO routines say yesCleanup and noCleanup, or perhaps even better finalCleanUpMode( cleanup options, booleans, etc); so you always, absolutely, have to call that, there's no mistake. And if the paths are tricky or threaded or whatever, I guess you'd have some sort of flag or something that you set along the lines "clean up not done yet!" and then clear that when it is done (either way) and watch for it not being cleared with a daemonHooked
the problem with being a really good programmer like yourself is, one has to program for idiots. code has to be so staggeringly simple, childishly stupid, really, really dumbed-down, to the point where the stupidest programmer who comes along after you can instantly understand every thing. it's a tough tension in programming.Hooked
@JoeBlow: Good thing that this device has purely diagnostically purposes and a hardware failure would just result in invalid diagnostic data :). Personally I'd prefer to use a language with proper cleanup language constructs, but we're stuck with C so far. Also the code doesn't use continue, break and the while( ...(),0 ) directly. There are some macros rollback, abort and finally that nicely wrap it up (finally uses GCC anonymous, nested functions to encapsulate the rollback code).Rebbecarebbecca
I can only say that your code is "too clever", which is a complement :) Note -- i THINK the kid asking here was asking about objective-C in an iOS milieu. Note -- in terms of GMTA, please read my long post on here, where among the other 10,000s of words of listening to myself talk, I explain our macro thoughts on this (in the iOS milieu). Cheers!!Hooked
@JoeBlow: Your comment seems to miss the link to the actual post.Rebbecarebbecca
B
0

Well, 50+ answers so far and nobody has mentioned what I usually do in this situation! (i.e. an operation that consists of several steps, but it would be overkill to use a state machine or a function pointer table):

if ( !executeStepA() )
{
    // error handling for "A" failing
}
else if ( !executeStepB() )
{
    // error handling for "B" failing
}
else if ( !executeStepC() )
{
    // error handling for "C" failing
}
else
{
    // all steps succeeded!
}

executeThisFunctionInAnyCase();

Advantages:

  • Does not end up with huge indent level
  • Error handling code (optional) is on the lines just after the call to the function that failed

Disadvantages:

  • Can get ugly if you have a step that's not just wrapped up in a single function call
  • Gets ugly if any flow is required other than "execute steps in order, aborting if one fails"
Barrage answered 11/7, 2014 at 6:50 Comment(2)
The problem with this approach is that the various conditions A, B and C might be completely unrelated, which then makes the code hard to understand. Long if...else if statements should preferably only be used when the conditions are related to each other.Illomened
Disagree, there's no need for any of the steps to be related in order for this structure to work. The only thing in common is the need to skip to the end of the if...else chain in case of failure.Barrage
R
0

Why using OOP? in pseudocode:

abstract class Abstraction():
   function executeStepA(){...};
   function executeStepB(){...};   
   function executeStepC(){...};
   function executeThisFunctionInAnyCase(){....}
   abstract function execute():

class A(Abstraction){
   function execute(){
      executeStepA();
      executeStepB();
      executeStepC();
   }
}
 class B(Abstraction){
   function execute(){
      executeStepA();
      executeStepB();
   }
}
class C(Abstraction){
     function execute(){
       executeStepA();
     }
}

this way your if's dissapear

item.execute();
item.executeThisFunctionInAnyCase();

Usually, ifs can be avoided using OOP.

Ripple answered 14/7, 2014 at 8:34 Comment(0)
S
0

I think C++23's monadic operations for optional would do well, though the functions would have to be changed a bit.

The and_then() method does the break or call next function operation, and chaining the method allows calling the functions one by one, till one of them returns false.

To give a quick & dirty example:

#include <iostream>
#include <optional>
#include <cstdlib>

using namespace std;

optional<bool> func1() {
    cout << "func1\n";

    if (rand() % 2)
        return true;
    else
        return nullopt;
}

optional<bool> func2(optional<bool> v) {
    cout << "func2\n";

    if (rand() % 2)
        return true;
    else
        return nullopt;
}

optional<bool> func3(optional<bool> v) {
    cout << "func3\n";

    if (rand() % 2)
        return true;
    else
        return nullopt;
}

void func4() {
    cout << "func4\n";
}

int main() {
    srand(time(NULL));

    func1()
      .and_then(func2)
      .and_then(func3);

    func4();

    return 0;
}
Steeplebush answered 5/8, 2022 at 16:56 Comment(0)
C
-1

Regarding your current code example, essentially a question #2,

[...block of code...]
bool conditionA = executeStepA();    
if (conditionA){
    [...block of code...]
    bool conditionB = executeStepB();
    if (conditionB){
        [...block of code...]
        bool conditionC = executeStepC();
        if (conditionC){
            ...other checks again...
        }
    }
}

executeThisFunctionInAnyCase();

Except for storing the function results in variables this is typical C code.

If the boolean function results signal failure then a C++ way would be to use exceptions, and code this up as

struct Finals{ ~Finals() { executeThisFunctionInAnyCase(); } };

Finals finals;
// [...block of code...]
executeStepA();
// [...block of code...]
executeStepB();
// [...block of code...]
executeStepC();
//...other checks again...

However, the details can vary greatly depending on the real problem.

When I need such general final actions, instead of defining a custom struct on the spot, I often use a general scope guard class. Scope guards were invented by Petru Marginean for C++98, then using the temporary lifetime extension trick. In C++11 a general scope guard class can be trivially implemented based on client code supplying a lambda expression.

At the end of the question you suggest a good C way to do this, namely by using a break statement:

for( ;; ) // As a block one can 'break' out of.
{
    // [...block of code...]
    if( !executeStepA() ) { break; }
    // [...block of code...]
    if( !executeStepB() ) { break; }
    // [...block of code...]
    if( !executeStepC() ) { break; }
    //...other checks again...
    break;
}
executeThisFunctionInAnyCase();

Alternatively, for C, refactor the code in the block as a separate function and use return instead of break. That’s both more clear and more general, in that it supports nested loops or switches. However, you asked about break.

Compared with the C++ exception based approach this relies on the programmer remembering to check each function result, and do the proper thing, both of which are automated in C++.

Churchly answered 26/6, 2014 at 13:24 Comment(14)
In case anyone wonders, for(;;) is used in spite of indicating a general loop, because Visual C++ has a habit of emitting sillywarnings for constant expressions used as conditions.Churchly
-1. This is far uglier than a goto formulation. It has the exact same effect as the goto formulation, with one big difference: The goto formulation is honest. This is dishonest. There is no looping. The loop exists for the sole reason of avoiding a goto.Gruff
why don't you use while(true) instead of for?Emitter
@sean: see the very first comment. but additionally, also while(true) has the connotations of a general loop. the do loop is more commonly used as an artifical block construct, in particular for establishing a local scope in a macro, but the same applies.Churchly
@CareyGregory: Dafid Hammen has only cited an emotional response to something unfamiliar to him. There is a big difference between this code and goto based code, namely that the execution is guaranteed to continue under the bottom of the for block. That's called structured programming. It was invented by Edsger Dijsktra, who wrote the classic article in 1968 about why goto creates hell, called "Go to statement considered harmful". I cited this information in an earlier comment that was deleted. The SO mods on the side of goto.Churchly
I've seen this exact same scheme used elsewhere just to avoid a goto. It is not at all unfamiliar to me. The general consensus during code review was that even though there was a rule in place against using gotos, a goto formulation was the preferred implementation here. Every rule can be broken with a waiver. Those waiver logs are important. Using dishonest coding practices to avoid going through a waiver process is far worse than being honest and asking for a waiver because some nominally banned coding practice (e.g., goto) happens to be the best solution in some particular case.Gruff
@DavidHammen: "dishonest" is a loaded word. this code is up-front honest. add a comment at top if you find the keyword misleading. i'm sorry that you have this emotional response. i think it is a valid reason for you (and possibly co-workers) to not use the construct, because with such feelings you'd be less productive. which is what counts. so then i would suggest a refactoring rather than goto. e.g. define a function, using return rather than break (or, switch over to C++). do read up on dijkstra. ;-)Churchly
Oh please. Any programmer worth anything nowadays knows what structured programming is. With regard to Djikstra, do you mean this Djikstra? Please don't fall into the trap of believing that I am terribly dogmatical about [the go to statement]. I have the uncomfortable feeling that others are making a religion out of it, as if the conceptual problems of programming could be solved by a single trick, by a simple form of coding discipline!Gruff
I've used the 3rd way shown above a number of times, in C-whatever and Java. For many cases it's the best structure one can settle on -- straightforward, efficient, and reasonably unlikely to produce bugs, though it's not strictly "structured".Consonantal
@Cheersandhth.-Alf Considering that I've worked on code written before Dijsktra's article, I find it a bit amusing to have it cited back to me. Trust me, I've seen the disasters that promiscuous goto use can produce (and worse, promiscuous use of FORTRAN's assigned goto, the worst programming construct ever devised). But my and Hammen's reasoning for the -1 is not emotional. Using a loop for something that is not in fact a loop is highly misleading. If you're going to use gotos then do so in a way that can be justified in a code review. Disguising them as loops is a terrible practice.Esteban
To be fair, the question explicitly asks also for a solution that uses break and when I read that sentence I thought to answer this aspect of the question in particular with suggesting a loop and even a degenerated switch, but found that a sufficient answer already exists. I don't think the loop is so bad. Of course the short-cut evaluation is more concise and the goto expresses the intention more directly but I know some stupid code style guides forbid the use of goto and in such a environment the loop is far from the worst replacement.Carrion
The struct finals micro RAII is quite unique as an answer here and not too ugly. And RAII is cheap and pretty much a corner stone of C++.Staunch
What makes for(;;) { break; } different from do { break; } while(0)Keane
@SalmanA: two ways. first, the latter can easily produce a sillywarning, while the former is the language's designed syntax for a loop with internal exit. secondly, the latter is associated with use as an artifical block construct, especially in macros, while the former is not. these two differences point in different directions as to what to choose: should one minimize problem of not-real warnings that mask real warnings, or should one minimize problem of convention-based expectations. since human behavior is much more easily adjustable than compiler behavior i chose the former. alrdy dscssedChurchly
H
-1

Given the function:

string trySomething ()
{
    if (condition_1)
    {
        do_1();
        ..
            if (condition_k)
            {
                do_K();

                return doSomething();
            }
            else
            {
                return "Error k";
            }
        ..
    }
    else
    {
        return "Error 1";
    }
}

We can get rid of the syntactical nesting, by reversing the validation process:

string trySomething ()
{
    if (!condition_1)
    {
        return "Error 1";
    }

    do_1();

    ..

    if (!condition_k)
    {
        return "Error k";
    }

    do_K();

    return doSomething ();
}
Harwell answered 1/7, 2014 at 19:13 Comment(1)
Probably because it is almost a duplicate of the answer that says "this is an arrow".Simplism
I
-1

In my opinion function pointers are the best way to go through this.

This approach was mentioned before, but I'd like to go even deeper into the pros of using such approach against an arrowing type of code.

From my experience, this sort of if chains happen in an initialization part of a certain action of the program. The program needs to be sure that everything is peachy before attempting to start.

In often cases in many of the do stuff functions some things might get allocated , or ownership could be changed. You will want to reverse the process if you fail.

Let's say you have the following 3 functions :

bool loadResources()
{
   return attemptToLoadResources();
}
bool getGlobalMutex()
{
   return attemptToGetGlobalMutex();
}
bool startInfernalMachine()
{
   return attemptToStartInfernalMachine();
}

The prototype for all of the functions will be:

typdef bool (*initializerFunc)(void);

So as mentioned above, you will add into a vector using push_back the pointers and just run them in the order. However, if your program fails at the startInfernalMachine , you will need to manually return the mutex and unload resources . If you do this in your RunAllways function, you will have a baad time.

But wait! functors are quite awesome (sometimes) and you can just change the prototype to the following :

typdef bool (*initializerFunc)(bool);

Why ? Well, the new functions will now look something like :

bool loadResources(bool bLoad)
{
   if (bLoad)
     return attemptToLoadResources();
   else
     return attemptToUnloadResources();
}
bool getGlobalMutex(bool bGet)
{
  if (bGet)
    return attemptToGetGlobalMutex();
  else
    return releaseGlobalMutex();
}
...

So now,the whole of the code, will look something like :

vector<initializerFunc> funcs;
funcs.push_back(&loadResources);
funcs.push_back(&getGlobalMutex);
funcs.push_back(&startInfernalMachine);
// yeah, i know, i don't use iterators
int lastIdx;
for (int i=0;i<funcs.size();i++)
{
   if (funcs[i](true))
      lastIdx=i;
   else 
      break;
}
// time to check if everything is peachy
if (lastIdx!=funcs.size()-1)
{
   // sad face, undo
   for (int i=lastIdx;i>=0;i++)
      funcs[i](false);
}

So it's definately a step forward to autoclean your project, and get past this phase. However, implementation is a bit awkward, since you will need to use this pushback mechanism over and over again. If you have only 1 such place, let's say it's ok, but if you have it 10 places, with an oscilating number of functions... not so fun.

Fortunately, there's another mechanism that will allow you to make a even better abstraction : variadic functions. After all, there's a varying number of functions you need to go thorough. A variadic function would look something like this :

bool variadicInitialization(int nFuncs,...)
{
    bool rez;
    int lastIdx;
    initializerFunccur;
    vector<initializerFunc> reverse;
    va_list vl;
    va_start(vl,nFuncs);
    for (int i=0;i<nFuncs;i++)
    {
        cur = va_arg(vl,initializerFunc);
        reverse.push_back(cur);
        rez= cur(true);
        if (rez)
            lastIdx=i;
        if (!rez)
            break;
    }
    va_end(vl);

    if (!rez)
    {

        for (int i=lastIdx;i>=0;i--)
        {
            reverse[i](false);
        }
    }
    return rez;
}

And now your code will be reduced (anywhere in the application) to this :

bool success = variadicInitialization(&loadResources,&getGlobalMutex,&startInfernalMachine);
doSomethingAllways();

So this way you can do all those nasty if lists with just one function call, and be sure that when the function exits you will not have any residues from initializations.

Your fellow team mates will be really grateful for making 100 lines of code possible in just 1.

BUT WAIT! There's more! One of the main traits of the arrow-type code is that you need to have a specific order! And that specific order needs to be the same in the whole application (multithreading deadlock avoidance rule no 1 : always take mutexes in the same order throughout the whole application) What if one of the newcomers, just makes the functions in a random order ? Even worse, what if you are asked to expose this to java or C# ? (yeah, cross platform is a pain)

Fortunately there's an approach for this. In bullet points , this is what I would suggest :

  • create an enum , starting from the first resource to the last

  • define a pair which takes a value from the enum and pairs it to the function pointer

  • put these pairs in a vector (I know, I just defined the use of a map :) , but I always go vector for small numbers)

  • change the variadic macro from taking function pointers to integers (which are easily exposed in java or C# ;) ) )

  • in the variadic function, sort those integers

  • when running, run the function assigned to that integer.

At the end, your code will ensure the following :

  • one line of code for initialization, no matter how many stuff needs to be ok

  • enforcing of the order of calling : you cannot call startInfernalMachine before loadResources unless you (the architect) decides to allow this

  • complete cleanup if something fails along the way (considering you made deinitialization properly)

  • changing the order of the initialization in the whole application means only changing the order in the enum

Ilene answered 11/7, 2014 at 12:53 Comment(0)
J
-1

Conditions can be simplified if conditions are moved under individual steps, here's a c# pseudo code,

the idea is to use a choreography instead of a central orchestration.

void Main()
{
    Request request = new Request();
    Response response = null;

    // enlist all the processors
    var processors = new List<IProcessor>() {new StepA() };

    var factory = new ProcessorFactory(processors);

    // execute as a choreography rather as a central orchestration.
    var processor = factory.Get(request, response);
    while (processor != null)
    {
        processor.Handle(request, out response);
        processor = factory.Get(request, response); 
    }

    // final result...
    //response
}

public class Request
{
}

public class Response
{
}

public interface IProcessor
{
    bool CanProcess(Request request, Response response);
    bool Handle(Request request, out Response response);
}

public interface IProcessorFactory
{
    IProcessor Get(Request request, Response response);
}   

public class ProcessorFactory : IProcessorFactory
{
    private readonly IEnumerable<IProcessor> processors;

    public ProcessorFactory(IEnumerable<IProcessor> processors)
    {
        this.processors = processors;
    }

    public IProcessor Get(Request request, Response response)
    {
        // this is an iterator
        var matchingProcessors = processors.Where(x => x.CanProcess(request, response)).ToArray();

        if (!matchingProcessors.Any())
        {
            return null;
        }

        return matchingProcessors[0];
    }
}

// Individual request processors, you will have many of these...
public class StepA: IProcessor
{
    public bool CanProcess(Request request, Response response)
    {
        // Validate wether this can be processed -- if condition here
        return false;
    }

    public bool Handle(Request request, out Response response)
    {
        response = null;
        return false;
    }
}
Jessamine answered 14/7, 2014 at 4:22 Comment(1)
What is the reason for neg? is the answer incorrect or completely off from the original question? or someone just don't like it?Jessamine

© 2022 - 2024 — McMap. All rights reserved.