For me do{...}while(0)
is fine. If you don't want to see the do{...}while(0)
, you can define alternative keywords for them.
Example:
SomeUtilities.hpp:
#define BEGIN_TEST do{
#define END_TEST }while(0);
SomeSourceFile.cpp:
BEGIN_TEST
if(!condition1) break;
if(!condition2) break;
if(!condition3) break;
if(!condition4) break;
if(!condition5) break;
//processing code here
END_TEST
I think the compiler will remove the unneccessary while(0)
condition in do{...}while(0)
in binary version and convert the breaks into unconditional jump. You can check it's assembly language version to be sure.
Using goto
also produces cleaner code and it is straightforward with the condition-then-jump logic. You can do the following:
{
if(!condition1) goto end_blahblah;
if(!condition2) goto end_blahblah;
if(!condition3) goto end_blahblah;
if(!condition4) goto end_blahblah;
if(!condition5) goto end_blahblah;
//processing code here
}end_blah_blah:; //use appropriate label here to describe...
// ...the whole code inside the block.
Note the label is placed after the closing }
. This is the avoid one possible problem in goto
that is accidentally placing a code in between because you didn't see the label. It is now like do{...}while(0)
without condition code.
To make this code cleaner and more comprehensible, you can do this:
SomeUtilities.hpp:
#define BEGIN_TEST {
#define END_TEST(_test_label_) }_test_label_:;
#define FAILED(_test_label_) goto _test_label_
SomeSourceFile.cpp:
BEGIN_TEST
if(!condition1) FAILED(NormalizeData);
if(!condition2) FAILED(NormalizeData);
if(!condition3) FAILED(NormalizeData);
if(!condition4) FAILED(NormalizeData);
if(!condition5) FAILED(NormalizeData);
END_TEST(NormalizeData)
With this, you can do nested blocks and specify where you want to exit/jump-out.
BEGIN_TEST
if(!condition1) FAILED(NormalizeData);
if(!condition2) FAILED(NormalizeData);
BEGIN_TEST
if(!conditionAA) FAILED(DecryptBlah);
if(!conditionBB) FAILED(NormalizeData); //Jump out to the outmost block
if(!conditionCC) FAILED(DecryptBlah);
// --We can now decrypt and do other stuffs.
END_TEST(DecryptBlah)
if(!condition3) FAILED(NormalizeData);
if(!condition4) FAILED(NormalizeData);
// --other code here
BEGIN_TEST
if(!conditionA) FAILED(TrimSpaces);
if(!conditionB) FAILED(TrimSpaces);
if(!conditionC) FAILED(NormalizeData); //Jump out to the outmost block
if(!conditionD) FAILED(TrimSpaces);
// --We can now trim completely or do other stuffs.
END_TEST(TrimSpaces)
// --Other code here...
if(!condition5) FAILED(NormalizeData);
//Ok, we got here. We can now process what we need to process.
END_TEST(NormalizeData)
Spaghetti code is not the fault of goto
, it's the fault of the programmer. You can still produce spaghetti code without using goto
.