6
\$\begingroup\$

Could you suggest ways of writing this code better?

bool success = true;
if(x == 0)
{
 if(do_up)
 run_do_up();
 else if(do_a)
 run_do_a();
 else if(do_n)
 run_do_n();
 else
 success = false;
}
else if(x == 1)
{
 if(do_n)
 run_do_n();
 else if(do_a)
 run_do_a();
 else
 success = false;
}
else//x == 2
{
 if(do_a)
 run_do_a();
 else if(do_n)
 run_do_n();
 else
 success = false;
}
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Mar 7, 2014 at 9:16
\$\endgroup\$
0

5 Answers 5

5
\$\begingroup\$

switch clause is usually used to clean this up, at least at the outer level:

switch(x){
 case 0:{
 if(do_up)
 run_do_up();
 else if(do_a)
 run_do_a();
 else if(do_n)
 run_do_n();
 else
 success = false;
 break;
 }
 case 1:{
 if(do_n)
 run_do_n();
 else if(do_a)
 run_do_a();
 else
 success = false;
 break;
 }
 default: {
 if(do_a)
 run_do_a();
 else if(do_n)
 run_do_n();
 else
 success = false;
 }
}

Alternatively, you can just realize you have 3 things to run an put in the logic:

if(x==0 && do_up)run_do_up(); //the only case this happens
else //case 1 does n first, other cases do a first
if(do_a && (x!=1 || (x==1 && !do_n)))run_do_a();
else
if(do_n && (x==1 || (x!=1 && !do_a)))run_do_n();
else success=false;

Check if I messed it up. The main question is: do you want from the flow to be apparent which one is checked first (in which case just use switch statement) or you want to avoid duplication of calls. You can also go take the hybrid way and just set flags in the main if block and then just conditionally call them at the end. Like this

 ...
 if(do_up) which_to_call=0;
 else if(do_a) which_to_call=1;
 else if(do_n) which_to_call=2;
 else which_to_call=-1; //error flag
answered Mar 7, 2014 at 10:53
\$\endgroup\$
1
  • 1
    \$\begingroup\$ +1 for the 3 things with logic in the if statement. I had that idea also but didn't put enough effort in thinking about the logic. Nice answer! \$\endgroup\$ Commented Mar 7, 2014 at 12:54
11
\$\begingroup\$

I think I'd encode this into something on the order of a state machine--a table holding the conditions and actions associated with each, and a tiny bit of code to walk through those to carry out the tests:

#include <iostream>
#include <functional>
#ifdef TEST
bool do_n, do_a, do_up;
bool success;
bool T = true;
void run_do_up() { std::cout << "did up\n"; }
void run_do_a() { std::cout << "did a\n"; }
void run_do_n() { std::cout << "did n\n"; }
#endif
struct conditional_action { 
 bool const &condition;
 std::function <void()> action;
 template <class F>
 conditional_action(bool const &b, F &a) : condition(b), action(a) {}
 conditional_action() : condition(T), action([&]{success = false; }) {}
};
conditional_action actions[4][3] { 
 { { do_up, run_do_up }, { do_a, run_do_a }, { do_n, run_do_n } },
 { { do_n, run_do_n }, { do_a, run_do_a } },
 { { do_a, run_do_a }, { do_n, run_do_n } }
};
void exec(size_t x) { 
 for (auto const &a : actions[x])
 if (a.condition) {
 a.action();
 return;
 }
}
#ifdef TEST
int main() { 
 do_n = true;
 exec(0);
 do_a = true;
 exec(2);
}
#endif

Depending on viewpoint, you might want to add a { T, [&]{success = false; }} to the end of each row in the actions table. That's what the default ctor will fill those entries with anyway, but there's a fair argument to be made that since it was explicit in the original logic, it should remain explicit here as well.

There is probably at least a little argument to be made that this is kind of a borderline case. A case where there were more conditions and actions would favor this technique more strongly. Whether really it makes sense when you have only three conditions and actions may be open to a little more question.

answered Mar 7, 2014 at 12:46
\$\endgroup\$
8
  • \$\begingroup\$ Isn't that to much for such a small task? \$\endgroup\$ Commented Mar 7, 2014 at 12:50
  • 2
    \$\begingroup\$ @exilit: Well, let's compare it to your answer. The combined total of exec, actions and conditional_action is exactly the same number of lines of code as you give in your answer. Your answer got that short, however, by breaking the original logic (do_up should not be considered when x==2). So, mine is not only much more scalable, but even for this small case is already the shortest code posted that actually works. It mostly looks longer because I added in demo/mock/test code. \$\endgroup\$ Commented Mar 7, 2014 at 13:07
  • \$\begingroup\$ I am not talking about loc. What I am talking about is the time and effort needed for the logic that sits behind. \$\endgroup\$ Commented Mar 7, 2014 at 13:43
  • 1
    \$\begingroup\$ @exilit: What time and effort? The effort of a single for loop or of a struct of two whole members? The effort to fully understand and verify something like if(do_a && (x!=1 || (x==1 && !do_n)))run_do_a(); alone exceeds the effort involved in writing and verifying my entire program. \$\endgroup\$ Commented Mar 7, 2014 at 14:24
  • 1
    \$\begingroup\$ @exilit: Maybe I just think in terms of larger, more complex problems, but I have to admit that I just don't see the difficulty or complexity. \$\endgroup\$ Commented Mar 7, 2014 at 15:07
4
\$\begingroup\$

If you write a few boolean returning wrapper functions:

bool cond_do_a(bool do) {
 if (do) run_do_a();
 return do;
}
bool cond_do_n(bool do) {
 if (do) run_do_n();
 return do;
}
bool cond_do_up(bool do) {
 if (do) run_do_up();
 return do;
}

Than you can express the code with a few simple logical or operators:

switch(x){
 case 0: success = cond_do_up(do_up) || cond_do_a(do_a) || cond_do_n(do_n);
 break;
 case 1: success = cond_do_n(do_n) || cond_do_a(do_a);
 break;
 case 2: success = cond_do_a(do_a) || cond_do_n(do_n);
 break;
 default:
 success = false;
}
answered Mar 7, 2014 at 21:14
\$\endgroup\$
2
\$\begingroup\$

You could try anything like this:

bool success = true;
if(x == 1)
{
 if(do_n)
 run_do_n();
 else if(do_a)
 run_do_a();
 else
 success = false;
}
else//x == 2 || x == 0
{
 if(do_up && x == 0)
 run_do_up();
 else if(do_a)
 run_do_a();
 else if(do_n)
 run_do_n();
 else
 success = false;
}
SylvainD
29.7k1 gold badge49 silver badges93 bronze badges
answered Mar 7, 2014 at 9:50
\$\endgroup\$
0
1
\$\begingroup\$

Put the maximum number of cases together in the same conditional. In this case, it means lumping values of x and do_...

bool success = true;
if (x == 0 && do_up) run_do_up();
else if (x == 1 && do_n) run_do_n();
else if (do_a) run_do_a();
else if (do_n) run_do_n();
else success = false;
answered Mar 7, 2014 at 16:16
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.