Sometimes, an if statement can be rather complicated or long, so for the sake of readability it is better to extract complicated calls before the if.
e.g. this:
if (SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall())
{
// do stuff
}
into this
bool b1 = SomeComplicatedFunctionCall();
bool b2 = OtherComplicatedFunctionCall();
if (b1 || b2)
{
//do stuff
}
(provided example is not that bad, it's just for illustration... imagine other calls with multiple arguments, etc.)
But with this extraction I lost the short circuit evaluation (SCE).
Do I really lose SCE every time? Is there some scenario where the compiler is allowed to "optimize it" and still provide SCE?
Are there ways of keeping the improved readability of the second snippet without losing SCE?
One natural solution would look like this:
bool b1 = SomeCondition();
bool b2 = b1 || SomeOtherCondition();
bool b3 = b2 || SomeThirdCondition();
// any other condition
bool bn = bn_1 || SomeFinalCondition();
if (bn)
{
// do stuff
}
This has the benefits of being easy to understand, being applicable to all cases and having short circuit behaviour.
This was my initial solution: A good pattern in method calls and for-loop bodies is the following:
if (!SomeComplicatedFunctionCall())
return; // or continue
if (!SomeOtherComplicatedFunctionCall())
return; // or continue
// do stuff
One gets the same nice performance benefits of shortcircuit evaluation, but the code looks more readable.
I tend to break down conditions onto multiple lines, i.e.:
if( SomeComplicatedFunctionCall()
|| OtherComplicatedFunctionCall()
) {
Even when dealing with multiple operators (&&) you just need to advance indention with each pair of brackets. SCE still kicks in - no need to use variables. Writing code this way made it much more readible to me for years already. More complex example:
if( one()
||( two()> 1337
&&( three()== 'foo'
|| four()
)
)
|| five()!= 3.1415
) {
If you have long chains of conditions and what to keep some of the short-circuiting, then you could use temporary variables to combine multiple conditions. Taking your example it would be possible to do e.g.
bool b = SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall();
if (b && some_other_expression) { ... }
If you have a C++11 capable compiler you could use lambda expressions to combine expressions into functions, similar to the above:
auto e = []()
{
return SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall();
};
if (e() && some_other_expression) { ... }
1) Yes, you no longer have SCE. Otherwise, you would have that
bool b1 = SomeComplicatedFunctionCall();
bool b2 = OtherComplicatedFunctionCall();
works one way or the other depending if there is an if statement later. Way too complex.
2) This is opinion based, but for reasonably complex expressions you can do:
if (SomeComplicatedFunctionCall()
|| OtherComplicatedFunctionCall()) {
If it ways too complex, the obvious solution is to create a function that evaluates the expression and call it.
You can also use:
bool b = someComplicatedStuff();
b = b || otherComplicatedStuff(); // it has to be: b = b || ...; b |= ...; is bitwise OR and SCE is not working then
and SCE will work.
But it's not much more readable than for example:
if (
someComplicatedStuff()
||
otherComplicatedStuff()
)
1) Do I really lose SCE every time? Is compiler is some scenario allowed to "optimize it" and still provide SCE?
I don't think such optimization is allowed; especially OtherComplicatedFunctionCall() might have some side effects.
2) What is the best practice in such situation? Is it only possibility (when I want SCE) to have all I need directly inside if and "just format it to be as readable as possible" ?
I prefer to refactor it into one function or one variable with a descriptive name; which will preserve both short circuit evaluation and readability:
bool getSomeResult() {
return SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall();
}
...
if (getSomeResult())
{
//do stuff
}
And as we implement getSomeResult() based on SomeComplicatedFunctionCall() and OtherComplicatedFunctionCall(), we could decompose them recursively if they're still complicated.
1) Do I really lose SCE every time? Is compiler is some scenario
allowed to "optimize it" and still provide SCE?
No you don't, but it's applied differently:
if (SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall())
{
// do stuff
}
Here, the compiler won't even run OtherComplicatedFunctionCall() if SomeComplicatedFunctionCall() returns true.
bool b1 = SomeComplicatedFunctionCall();
bool b2 = OtherComplicatedFunctionCall();
if (b1 || b2)
{
//do stuff
}
Here, both functions will run because they have to be stored into b1 and b2. Ff b1 == true then b2 won't be evaluated (SCE). But OtherComplicatedFunctionCall() has been run already.
If b2 is used nowhere else the compiler might be smart enough to inline the function call inside the if if the function has no observable side-effects.
2) What is the best practice in such situation? Is it only possibility
(when I want SCE) to have all I need directly inside if and "just
format it to be as readable as possible" ?
That depends.
Do you need OtherComplicatedFunctionCall() to run because of side-effects or the performance hit of the function is minimal then you should use the second approach for readability. Otherwise, stick to SCE through the first approach.
Another possibility that short circuits and has the conditions in one place:
bool (* conditions [])()= {&a, &b, ...}; // list of conditions
bool conditionsHold = true;
for(int i= 0; i < sizeOf(conditions); i ++){
if (!conditions[i]()){;
conditionsHold = false;
break;
}
}
//conditionsHold is true if all conditions were met, otherwise false
You could put the loop into a function and let the function accept a list of conditions and output a boolean value.
Very strange: you are talking about readability when nobody mentions the usage of comment within the code:
if (somecomplicated_function() || // let me explain what this function does
someother_function()) // this function does something else
...
In top of that, I always preceed my functions with some comments, about the function itself, about its input and output, and sometimes I put an example, as you can see here:
/*---------------------------*/
/*! interpolates between values
* #param[in] X_axis : contains X-values
* #param[in] Y_axis : contains Y-values
* #param[in] value : X-value, input to the interpolation process
* #return[out] : the interpolated value
* #example : interpolate([2,0],[3,2],2.4) -> 0.8
*/
int interpolate(std::vector<int>& X_axis, std::vector<int>& Y_axis, int value)
Obviously the formatting to use for your comments may depend on your development environment (Visual studio, JavaDoc under Eclipse, ...)
As far as SCE is concerned, I assume by this you mean the following:
bool b1;
b1 = somecomplicated_function(); // let me explain what this function does
bool b2 = false;
if (!b1) { // SCE : if first function call is already true,
// no need to spend resources executing second function.
b2 = someother_function(); // this function does something else
}
if (b1 || b2) {
...
}
Readability is necessary if you work in a company and your code will be read by someone else. If you write a program for yourself, it is up to you if you want to sacrifice performance for the sake of comprehensible code.
Related
Am reading through (the) C++ Core guidelines and encountered this rule: "Don’t declare a variable until you have a value to initialize it with" https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es22-dont-declare-a-variable-until-you-have-a-value-to-initialize-it-with
It describes the following code as bad:
SomeLargeType var;
if (cond) // some non-trivial condition
Set(&var);
else if (cond2 || !cond3) {
var = Set2(3.14);
}
else {
var = 0;
for (auto& e : something)
var += e;
}
Unfortunately this point fails to describe a way to how to solve this exact issue. Sometimes you just have to initialize a large object differently depending on a condition.
The only circumvent that comes to my mind is something like:
SomeLargeType * var;
if (cond) // some non-trivial condition
var = new SomeLargeType(123);
else if (cond2 || !cond3) {
var = new SomeLargeType(3.14);
}
However even if I use a smartpointer, this feels somehow unnecessary/unsafe and most of all, worse than the initial way.
What is the optimal solution?
You can use a function. Also, don't use bare pointers with ownership (I assume that there's a guideline for this too). Example:
std::unique_ptr<SomeLargeType>
make_something(bool cond, bool cond23)
{
if (cond)
return std::make_unique<SomeLargeType>(123);
else if (cond23)
return std::make_unique<SomeLargeType>(3.14);
else
return nullptr;
}
// usage
std::unique_ptr<SomeLargeType> var = make_something(cond, cond2 || !cond3);
If there's no way for this function to be reusable, then a lambda might be appropriate, as shown by Sopel
First, there is no undefined behaviour in the original sample code: all possible conditions are taken care of (by the final else block). However, there is potential undefined behaviour in the answers so far given, as they replace the 'good' code in the aforementioned else block with, effectively, no initialisation and the return of a nullptr, which may later be the subject of an attempted dereference.
Also, there is no real need here to complicate matters by replacing the instance variable with a pointer (and this also changes the nature/logic of the code).
Using a lambda (as suggested in the link provided by StaceyGirl) is certainly a good way to go (probably the best, but that may be subjective). However, to keep the logic the same as the original code, one can apply the lambda to the object, rather than to a pointer, as so:
SomeLargeType var = [&]() {
if (cond) { // some non-trivial condition
SomeLargeType v1;
Set(&v1);
return v1;
}
else if (cond2 || !cond3) {
SomeLargeType v2 = Set2(3.14);
return v2;
}
else {
SomeLargeType v3 = 0;
for (auto& e : something) var += e;
return v3;
}
}();
Here, unlike in the original code (where a default constructor is first called, then one of three others), a constructor for SomeLargeObject will only be called once¹ and there will be no undefined behaviour. It is this initial call to the (potentially very expensive) default constructor that, I presume, is the reason for this being cited as an example of "bad code."
¹ If there are any doubts about how often the constructors are called, I can provide a complete MCVE (with some minor modifications to avoid the undefined for (auto& e : something) line), if such is requested.
An immediately invoked lambda can be used as an alternative to a named function.
SomeLargeType* var = [&]() {
if (cond) // some non-trivial condition
return new SomeLargeType(123);
else if (cond2 || !cond3)
return new SomeLargeType(3.14);
else
return nullptr;
}();
Note that some types may not provide a default constructor, in which case it's impossible to have a lazy initialization without using a boxing type.
This can also improve performance when the type is not trivially constructible.
https://godbolt.org/z/_V8t2T
This is probably a really obvious question but I want to be sure about it before going on with my development.
I have a templated class something like:
template<TypeA var1, TypeB var2>
class myClass
{
//Attributes
...
//Methods
...
void checkHit(vector<Real>* path, vector<bool>* active)
{
for(int i = 0; i < N; i++)
{
//Some process
bool hit = ((var1 == Up) || (var1 == Down));
}
//...
bool flag = ((var2 == Left) || (var2 == Right));
}
}
If I create and object like
myClass obj<Up, Right>(...);
When executing the method
obj.checkHit( ... );
Will the program just check for bool hit = ((var1 == Up) and (var2 == Right) ?? In other words, will the compiler throw away the code that doesn't has to do with the template-parameters passed? What I mean is if the resulting code after compiling will be something like
template<TypeA var1, TypeB var2>
class myClass
{
//Attributes
...
//Methods
...
void checkHit(vector<Real>* path, vector<bool>* active)
{
for(int i = 0; i < N; i++)
{
//Some process
bool hit = ((var1 == Up)); //Like if I have never put "|| (var1 == Down));"
}
//...
bool flag = ((var2 == Right));
}
}
Hope the example is clear enough, I just simplified (a lot) the real code but it should be roughly equivalent. If it isn't clear just tell and I will gladly improve it.
I'm assuming your TypeA and TypeB are either enumerations or some sort of class/struct with those values defined?
That being the case, the compiler isn't going to "throw out" code here. Your conditional checks to see what the template parameters are, so if they don't match what is in the conditionals, you'll end up with a call that would basically do this:
bool hit = (0 == 1 || 1 == 2);
Depending on your optimization settings, the compiler may realize that this will always be false and just set it to false (also assuming you do something useful in the function so the compiler doesn't just turn it into a noop).
You also should pass your vectors by reference (or constant reference), and I would avoid using vector<bool>.
The example is not really clear enough, there is no "code that doesn't has to do with the template-parameters passed" because nothing depends on hit or flag, so a decent optimizer would turn checkHit into a completely empty function!
The compiler can evaluate the ((var1 == TypeA::Up) || (var2 == TypeB::Down)) condition at compile-time and a decent optimizing compiler will propagate the result of that condition to the rest of the function, removing any dead code that will never be run due due to the value of that condition.
Also, do you really want to pass these arguments by value?
void checkHit(vector<Real> path, vector<bool> active)
the Program will NOT throw away any part of the code.Template classes are only used to generalize the usage of a class .once a typeA and Type B are chosen they will all be taken as the Type A and Type B throughout the code and the rest of the code will execute in the normal way that it would without any templates
do{...} while(0);
the usage of do{}while(0); in my coding is used because, i do not want to use long if else nested conditional statements. I eventually give an break at the time of failure and move out of the loop, with a assurance that my function would have been traversed at least 1 time.
Now, the problem comes with the code warning tools, I am getting a warning at the usage of do{...}while(0);
the usage of nested if(){} else{} is less readable, high complex. and lets the code to be having dead code.
if i exclude nested if(){} else{} and do{} while(0); , do we left part with some other way to make code readable with understandable logic;
if(status_of_funcA_ok != funcA())
{ //failure}
else if (status_of_funcB_ok != funcB())
{//failure}
else if (status_of_funcC_ok != funcC())
else
{//Great}
do{
if(status_of_funcA_ok != funcA())
break;
if (status_of_funcB_ok != funcB())
break;
if (status_of_funcC_ok != funcC())
break;
}while(0);
Move the complete logic of the do while{0} loop to a function, and replace the break with return. And call the function, instead of the loop.
You will not have to worry about the beauty.
The compiler also doesn't have to complain about the do while{0}.
All the more, by adding a bit of modularity, the program might be a little more readable.
In any case, before doing this, it would be nice to check whether your compiler is in an extremely pedantic mode, and you might want to turn that off. That might take the warning away.
ss.
PS: You don't seem to need a return value for the function, but you could have that to get a clue of which function was successful.
I am using this pattern too, for those who wonder, here's an abstract example:
do // while(0) for break
{
state1 = 0;
if (cond1())
{
if (cond2())
break;
state1 = opA();
}
if (cond3() || state1 && state1->cond4())
break;
...
Triumph(state1, ...);
// often here: return
}
Failure(state1, ...);
I consider this valid in the following circumstances:
you have a long-ish sequence (say, >~half a dozen of conditions)
the conditions are complex, and you use / build up significant state, so you can't
isolate the elements into functions
you are in an exception-unfriendly environment, or your break-ing code path is
not actually an exception
What you can do about it:
Silence the warning. It is just a warning, after all; and I don't see a "typical mistake" (like typing 0 instead of your condition) that would be caught by this warning.
[edit] Now, that was silly. the typical mistake that you catch with the warning is e.g. while (a1!=a1) instead of while (a1!=a2).[/edit]
Break into functions, move state to a class
this would transform above code to:
struct Garbler
{
State1 state1;
bool Step1()
{
state1 = 0;
if (cond1())
{
if (cond2())
return false;
state1 = opA();
}
return true;
}
bool Step2()
{
return cond3() || state1 && state1->cond4();
}
..
void Run()
{
if (Step1() && Step2() && ... && Step23())
Triumph(state1, ...);
else
Failure(state1, ...);
}
}
This is arguably less readable, worse is that you pull apart the sequence, which might lead to a very questionable class (where members may be called only in a certain order).
Scopeguards
This may allow to transform the breaks into early returns, which are more acceptable:
state1 = 0;
ScopeGuard gFailure = MakeGuard(&Failure, ByRef(state1), ...);
if (cond1())
{
if (cond2())
return;
state1 = opA();
}
if (cond3() || state1 && state1->cond4())
return;
// everything went ok, we can dismiss the scopeguard
gFailure.Dismiss();
Triumph(state1, ...);
They can be more elegantly written in C++0x, preserve the flow, but the solution isn't that flexible either, e.g. when Failure() cannot be isolated easily into a single function.
Nested nested if-else statements can become quite unreadable, but I think using do {..} while(0); as a replacement would be much worse. It is very unconventional and anybody else reading it would not really associate it with if-else statements.
There are a few things you can do to make nested if-else statements more readable. A few suggestions are:
optimize your logic - sometimes you can do away with a lot of if clauses when you 'refactor' your logic ex. grouping identical items.
use switch() - switch is generally more readable compared to if-else statements. You can associate an enum to each case and you can switch this.
encapsulate complicated logic with functions
You can use goto instead of do {} while(0) and break. This is not readable and not good practice either though. I think for each specific case there is a better way to avoid deep if/else structures. For example, sometimes using function calls can help:
for example instead of:
if(status_of_funcA_ok != funcA())
{ //failure}
else if (status_of_funcB_ok != funcB())
{//failure}
else if (status_of_funcC_ok != funcC())
else
{//Great}
you can write:
if (check_funcs() == 0) {
great();
}
int check_funcs() {
if (status_of_funcA_ok != funcA())
return -1;
if (if(status_of_funcB_ok != funcB()))
return -2;
if (if(status_of_funcC_ok != funcC()))
return -3;
return 0; /* great */
}
Sometimes, you can use exit().
Also, in c++ you can use throw() and try/catch:
try {
/* */
throw (this error);
/* */
throw (that error);
} catch (this error) {
} catch (that error) {
}
If there are more conditions to check avoid using if{} else{},
best practice is to Replace if else conditions with switch case
I'm working in C++ enviroment and:
a) We are forbidden to use exceptions
b) It is application/data server code that evaluates lot of requests of different kinds
I have simple class encapsulating result of server operation that is also used internally for lot of functions there.
class OpResult
{
.....
bool succeeded();
bool failed(); ....
... data error/result message ...
};
As I try to have all functions small and simple, lot of blocks like this are arising:
....
OpResult result = some_(mostly check)function(....);
if (result.failed())
return result;
...
The question is, is it bad practise to make macro looking like this and use it everywhere?
#define RETURN_IF_FAILED(call) \
{ \
OpResult result = call; \
if (result.failed()) \
return result; \
}
I understand that someone can call it nasty, but is there a better way?
What other way of handling results and avoiding lot of bloat code would you suggest?
It's a trade off. You are trading code size for obfuscation of the logic. I prefer to preserve the logic as visible.
I dislike macros of this type because they break Intellisense (on Windows), and debugging of the program logic. Try putting a breakpoint on all 10 return statements in your function - not the check, just the return. Try stepping through the code that's in the macro.
The worst thing about this is that once you accept this it's hard to argue against the 30-line monster macros that some programmers LOVE to use for commonly-seen mini-tasks because they 'clarify things'. I've seen code where different exception types were handled this way by four cascading macros, resulting in 4 lines in the source file, with the macros actually expanding to > 100 real lines. Now, are you reducing code bloat? No. It's impossible to tell easily with macros.
Another general argument against macros, even if not obviously applicable here, is the ability to nest them with hard to decipher results, or to pass in arguments that result in weird but compilable arguments e.g. the use of ++x in a macros that uses the argument twice. I always know where I stand with the code, and I can't say that about a macro.
EDIT: One comment I should add is that if you really do repeat this error check logic over and over, perhaps there are refactoring opportunities in the code. Not a guarantee but a better way of code bloat reduction if it does apply. Look for repeated sequences of calls and encapsulate common sequences in their own function, rather than addressing how each call is handled in isolation.
Actually, I prefer slightly other solution. The thing is that the result of inner call is not necessarily a valid result of an outer call. For example, inner failure may be "file not found", but the outer one "configuration not available". Therefore my suggestion is to recreate the OpResult (potentially packing the "inner" OpResult into it for better debugging). This all goes to the direction of "InnerException" in .NET.
technically, in my case the macro looks like
#define RETURN_IF_FAILED(call, outerresult) \
{ \
OpResult innerresult = call; \
if (innerresult.failed()) \
{ \
outerresult.setInner(innerresult); \
return outerresult; \
} \
}
This solution requires however some memory management etc.
Some purist argue that having no explicit returns hinders the readability of the code. In my opinion however having explicit RETURN as a part of the macro name is enough to prevent confusion for any skilled and attentive developer.
My opinion is that such macros don't obfuscate the program logic, but on the contrary make it cleaner. With such a macro, you declare your intent in a clear and concise way, while the other way seems to be overly verbose and therefore error-prone. Making the maintainers parse in mind the same construct OpResult r = call(); if (r.failed) return r is wasting of their time.
An alternative approach without early returns is applying to each code line the pattern like CHECKEDCALL(r, call) with #define CHECKEDCALL(r, call) do { if (r.succeeded) r = call; } while(false). This is in my eyes much much worse and definitely error-prone, as people tend to forget about adding CHECKEDCALL() when adding more code.
Having a popular need to do checked returns (or everything) with macros seems to be a slight sign of missing language feature for me.
As long as the macro definition sits in an implementation file and is undefined as soon as unnecessary, I wouldn't be horrified.
// something.cpp
#define RETURN_IF_FAILED() /* ... */
void f1 () { /* ... */ }
void f2 () { /* ... */ }
#undef RETURN_IF_FAILED
However, I would only use this after having ruled out all non-macro solutions.
After 10 years, I'm going to answer my own question to my satisfaction, if only I had a time machine ...
I encountered a similar situation many times in new projects. Even when exceptions were allowed, I don't want to always use them for "normal fails".
I eventually discovered a way to write these kind of statements.
For generic Result that includes message, I use this:
class Result
{
public:
enum class Enum
{
Undefined,
Meaningless,
Success,
Fail,
};
static constexpr Enum Undefined = Enum::Undefined;
static constexpr Enum Meaningless = Enum::Meaningless;
static constexpr Enum Success = Enum::Success;
static constexpr Enum Fail = Enum::Fail;
Result() = default;
Result(Enum result) : result(result) {}
Result(const LocalisedString& message) : result(Fail), message(message) {}
Result(Enum result, const LocalisedString& message) : result(result), message(message) {}
bool isDefined() const { return this->result != Undefined; }
bool succeeded() const { assert(this->result != Undefined); return this->result == Success; }
bool isMeaningless() const { assert(this->result != Undefined); return this->result == Enum::Meaningless; }
bool failed() const { assert(this->result != Undefined); return this->result == Fail; }
const LocalisedString& getMessage() const { return this->message; }
private:
Enum result = Undefined;
LocalisedString message;
};
And then, I have a special helper class in this form, (similar for other return types)
class Failed
{
public:
Failed(Result&& result) : result(std::move(result)) {}
explicit operator bool() const { return this->result.failed(); }
operator Result() { return this->result; }
const LocalisedString& getMessage() const { return this->result.getMessage(); }
Result result;
};
When these are combined, I can write code like this:
if (Failed result = trySomething())
showError(result.getMessage().str());
Isn't it beutiful?
I agree with Steve's POV.
I first thought, at least reduce the macro to
#define RETURN_IF_FAILED(result) if(result.failed()) return result;
but then it occurred to me this already is a one-liner, so there really is little benefit in the macro.
I think, basically, you have to make a trade off between write-ability and readability. The macro is definitely easier to write. It is, however, an open question whether it is also is easier to read. The latter is quite a subjective judgment to make. Still, using macros objectively does obfuscate code.
Ultimately, the underlying problem is that you must not use exceptions. You haven't said what the reasons for that decision are, but I surely hope they are worth the problems this causes.
Could be done with C++0x lambdas.
template<typename F> inline OpResult if_failed(OpResult a, F f) {
if (a.failed())
return a;
else
return f();
};
OpResult something() {
int mah_var = 0;
OpResult x = do_something();
return if_failed(x, [&]() -> OpResult {
std::cout << mah_var;
return f;
});
};
If you're clever and desperate, you could make the same kind of trick work with regular objects.
In my opinion, hiding a return statement in a macro is a bad idea. The 'code obfucation' (I like that term..! ) reaches the highest possible level. My usual solution to such problems is to aggregate the function execution at one place and control the result in the following manner (assuming you have 5 nullary functions):
std::array<std::function<OpResult ()>, 5> tFunctions = {
f1, f2, f3, f4, f5
};
auto tFirstFailed = std::find_if(tFunctions.begin(), tFunctions.end(),
[] (std::function<OpResult ()>& pFunc) -> bool {
return pFunc().failed();
});
if (tFirstFailed != tFunctions.end()) {
// tFirstFailed is the first function which failed...
}
Is there any information in result which is actually useful if the call fails?
If not, then
static const error_result = something;
if ( call().failed() ) return error_result;
would suffice.
In a situation where a variable could have two different values, and you do something if its one, something differnent if its the other, would you just do:
if(myVariable == FIRST_POSSIBLE_VALUE) { ... }
else { ... }
or would you do:
if(myVariable == FIRST_POSSIBLE_VALUE) { ... }
else if (myVariable == SECOND_POSSIBLE_VALUE) { ... }
for clarity, in a situation where a reader wouldn't necessarily be able to tell that they do the same thing (but the else if does a "needless" expression)?
So what would you do?
Thanks!
EDIT: There is actually about a lot more different options for something like this: ternary operator, if-else, if-elseif, if-elseif-else, -if-else(with assert), switch. Each one has its place, but its hard to decide..
I always prefer just plain else when there is no other possible state of the variable(ie, checking for null and all that). I may add a comment saying what the variable is if it isn't the first conditional, but that is only in cases where its like
if(color==red){
....
}else{ //our theme only allows for red and yellow, so the color must be yellow.
....
}
Also, this saves some time for the processor cause it won't have to check a useless variable(or worse in OOP, where checking that variable can take quite a few dereferences, function calls, and memory reads)
I never do something like
if(file.is_open==1){
....
}else if(file.is_open==0){
....
}
as is_open is a boolean, it is pointless to specify that because the only option left is 0, also this can save a little bit of typing when you must refactor your code to use is_open() instead, as now you only must change one line instead of two.
and 'else if' statements I think should be turned to switches if there is more than 1 'else if', unless of course the language makes it impossible(such as how C can't handle strings in switches)
Else is a default. Meaning that there are either a large number of possibilities for the data, or that it is unexpected data.
I go by the basic rule: If there is a parameter that can be met, use else if, and if there isn't, use else. I normally use else's for errors.
I only use the if-else for boolean checks, that means that if the expression doesn't match there only can be the else. Or i want to take everything with the else: think of it like default.
If you want to check enumeration or something, you should try check this via switch statement, if possible in your language.
In Java it's not possible to use a switch for Strings. So you could use something like this:
if(string.equals("foo")) {
// first case
} else if(string.equals("bar")) {
// second case
} else {
throw IllegalArgumentException(" ... ");
// or log it
}
If you're not sure that your check can't be extended, you should if you can provide an default way.
Isn't this what assert was made for?
if (condition1) { ... }
else { assert(condition2); }
This can be expanded for three-state logic, too.
if (condition1) { ... }
elsif (condition2) { ... }
else { assert(condition3); }
Using assert makes your code readable, easy to maintain, and clear. That being said, asserts and comments are almost interchangeable.
Sometimes the condition of the else statement is very obvious. For example
if(user.IsNew) { } else { /*in this case user.IsNew != true*/ }
But in some other cases the else isn't that obvious and it is better to clarify the else condition. This is also more future proof in case some other possible conditions are added.
Furthermore you are able to insert an exception in the (last) else to inform about unimplemented cases. This can be very useful when the for example backend and frontend are separated and somebody adds a new value to an enumerator (or when using text keys a new key is introduced) you will receive an error when the new value is first used. When not using if else if you won't see what happened and that could make debugging pretty hard.
if(user.SelectedStyle == Styles.Red) {
} else if(user.SelectedStyle == Styles.Basic) {
} else {
throw new Exception("Not implemented");
}
In the case above a new Style (for example Style.Blue) will cause your application to throw an exception.
It's really a matter of style and your own mental view of the world. The glass is BOTH half empty and half full, but you can get the darnedest arguments going about it.
If the boolean tests are all of the same type, a switch statement is best.
If not, I'd recommend leaving out the additional test but insert a comment about the operational meaning of falling through into that last statement. See Gertjan's comment, above.
When your input can be clearly separate into distinct cases, I feel it is mostly nicer to explicitly state what those cases are, for example if you are expecting 'n' to be a number between 0 and 100, and you have 3 cases:
if (n >= 0 && n < 30) {
case1();
} else if (n >=30 && n < 70) {
case2();
} else if (n >=70 && n < 100) {
case3();
}
in some situations the 'else' case is good for error checking
} else {
error("n should be between 0 and 100");
}
if your data is checked for erroneous values earlier, then there may be a case to use else for the final case, to provide a small performance improvement in languages like C:
} else { // (n >= 70 && n < 100)
case3();
}
but this is only necessary due to some languages inability to express the domain of a function, in languages where the domain can be expressed clearly, the optimiser should add this performance benefit for you, allowing you to be specific in your code, and making it easier to add more cases later
... of course this is an art, not a science, and in some cases you can't follow the hard-and-fast rule, I frequently find myself writing code like:
if (p == NULL) {
doSomething();
} else {
doSomethingElse();
}
...justified by the fact that is very obvious and implicit from the first if condition what the else is used for.
else was invented and is used for good reason. Where you use it should be dictated by the logic that you trying to achieve, not a contrived sense of style.
Some argue that it is more self-documenting by using explicit conditions in an else if; however, this may lead to gaps in logic with default or catch all conditions.
Also, some say that it is easier to modify in the future. This argument is bunk. Using design patterns and writing modular code is something that is easier to modify in the future, writing one line shouldn't qualify for these kinds of statements.
It depends on the situation. Do you only want to take action if certain criteria are met, or is there a special case for one value and another set of logic for any other value?