I've run into this situation a few times now, where I'll have a logic structure that looks something like this.
switch(someInteger)
{
case 1:
if(conditionA) {
if(conditionB) {
func1();
} else {
func2();
}
} else {
func3();
}
break;
case 2:
if(conditionA) {
if(conditionB) {
func4();
} else {
func5();
}
} else {
func6();
}
break;
case 2:
//ditto
case 3:
//ditto
case someObnoxiouslyHighNumber:
//ditto
}
The structure in each case is identical, the only differences are the functions being called. But now I'm duplicating my logic, and that just feels dirty. I feel like there's a more elegant way to handle these situations, but I've yet to come across one.
Any ideas on how to refactor this?
EDIT: Changed the structure of the example a bit to emphasize the problem more.
I'm not sure, but I'd be tempted to try it this way:
if someInteger == 1 and conditionA and conditionB then func1
else if someInteger == 1 and conditionA and !conditionB then func2
else if someInteger == 1 and !conditionA then func3
else if someInteger == 2 and conditionA and conditionB then func4
else if someInteger == 2 and conditionA and !conditionB then func5
else if someInteger == 2 and !conditionA then func6
else error "Uncharted territory"
Related
I want to use different signals from different IR remotes to control a wheeled robot.
The robot is the Smart robot car kit v1.0 from Elegoo.
I used the infrared_remote_control_car.ino file from the disc with it.
I just added the #define JVC and the operators at the end.
The code looks like this:
#include <IRremote.h>
int receiverpin = 12;
int in1=9;
int in2=8;
int in3=7;
int in4=6;
int ENA=10;
int ENB=5;
int ABS=130;
unsigned long RED;
#define A 16736925
#define B 16754775
#define X 16712445
#define C 16720605
#define D 16761405
#define JVCfront 49816
#define JVCback 49688
#define JVCright 49704
#define JVCleft 49832
#define JVCmenu 49900
#define JVC3ok 49724
#define JVCstop 49856
IRrecv irrecv(receiverpin);
decode_results results;
void _mForward()
{
digitalWrite(ENA,HIGH);
digitalWrite(ENB,HIGH);
digitalWrite(in1,LOW);
digitalWrite(in2,HIGH);
digitalWrite(in3,LOW);
digitalWrite(in4,HIGH);
Serial.println("go forward!");
}
void _mBack()
{
digitalWrite(ENA,HIGH);
digitalWrite(ENB,HIGH);
digitalWrite(in1,HIGH);
digitalWrite(in2,LOW);
digitalWrite(in3,HIGH);
digitalWrite(in4,LOW);
Serial.println("go back!");
}
void _mleft()
{
analogWrite(ENA,ABS);
analogWrite(ENB,ABS);
digitalWrite(in1,LOW);
digitalWrite(in2,HIGH);
digitalWrite(in3,HIGH);
digitalWrite(in4,LOW);
Serial.println("go left!");
}
void _mright()
{
analogWrite(ENA,ABS);
analogWrite(ENB,ABS);
digitalWrite(in1,HIGH);
digitalWrite(in2,LOW);
digitalWrite(in3,LOW);
digitalWrite(in4,HIGH);
Serial.println("go right!");
}
void _mStop()
{
digitalWrite(ENA,LOW);
digitalWrite(ENB,LOW);
Serial.println("STOP!");
}
void setup() {
pinMode(in1,OUTPUT);
pinMode(in2,OUTPUT);
pinMode(in3,OUTPUT);
pinMode(in4,OUTPUT);
pinMode(ENA,OUTPUT);
pinMode(ENB,OUTPUT);
pinMode(receiverpin,INPUT);
Serial.begin(9600);
_mStop();
irrecv.enableIRIn();
}
void loop() {
if (irrecv.decode(&results))
{
RED=results.value;
Serial.println(RED);
irrecv.resume();
delay(150);
if(RED==(A || JVCfront))
{
_mForward();
}
else if(RED==(B or JVCback))
{
_mBack();
}
else if(RED==(C or JVCleft))
{
_mleft();
}
else if(RED==(D or JVCright))
{
_mright();
}
else if(RED==(X or JVCstop or JVCmenu or JVC3ok))
{
_mStop();
}
}
}
I tried different ways I saw on the internet for the OR operator as you can see.
Actually, the robot is always going forward.
Does the Serial.println(RED) always print out the expected value?
Your if-elseblock itself does not seem to be problematic at first glance. It doesn't matter if you use || or or. They are equivalent.
The problem is the way you are checking the value:
Therefore that you have parenthesis around your OR statement, you create a bool-value. if(RED==(A || JVCfront) translates to:
is A set or is JVCfront set; meaning, are they != 0 (yes they both are, so this expression is true
is RED == true (no it is not, because true represents 1 in integer)
so the code in that block is not being executed
If you want to solve it with an if-else, you need to:
if(RED == A or RED == JVCfront)
{
_mForward();
}
Anyway, I would suggest a switch-case statement:
switch(RED):
{
case A:
{
//intentional fallthrough
}
case JVCfront:
{
_mForward();
break;
}
case B:
{
//intentional fallthrough
}
case JVCback:
{
_mBack();
break;
}
case C:
{
//intentional fallthrough
}
case JVCleft:
{
_mleft();
break;
}
case D:
{
//intentional fallthrough
}
case JVCright:
{
_mright();
break;
}
case X:
{
//intentional fallthrough
}
case JVCstop:
{
//intentional fallthrough
}
case JVCmenu:
{
//intentional fallthrough
}
case JVC3ok:
{
_mStop();
break;
}
default:
{
_mStop();
}
}
I personally find this kind of code much easier to read, and more understandable.
Please note: If you dont put a break into a case, it automatically executes the next case as well. This is why I wrote //intentional fallthrough in this part of the code. P.e. when case A is executed, nothing happens. It just falls through into case JVCFront and executes everything there.
I'm trying to implement a very basic clone of redis in C++. So when I get the queries, I need to parse those. Right now I am doing this:
void Query::buildQuery(){
std::string query_type = lower(args[0]);
if(query_type == "get"){ //do something }
else if(query_type == "set"){ //do something }
else if(query_type == "getbit"){ //do something }
else if(query_type == "setbit"){ //do something }
else if(query_type == "zadd"){ //do something }
else if(query_type == "zcard"){ //do something }
else if(query_type == "zcount"){ //do something }
else if(query_type == "zrange"){ //do something }
else if(query_type == "save"){ //do something }
else { throw(QueryException("Invalid query type")); }
}
Is there any other, shorter way to do this? I don't want to include any other library than the STL.
If those do_somethings can be extracted into separate methods, then you could create a pre-initialized hash map (unordered_map) from string to pointer to member function and do something like
(this->*queryHandlers[query_type])();
You'll have to choose between lots of functions and one large function, though.
If you're running on an Intel/AMD processor and feeling 'brave' you might like to take a look at these implementations of strcmp, strlen, etc that use SSE instructions. That'd be quicker.
As for the general structure, you could turn "set" & "do something" into an class which has a test method and a do-something method. Have an array of those, and iterate across it calling the test method passing query_type. The test method for the object would automatically call the do-something method if it matches the string.
Without if...else if, you can do this by switch statement. Like:
void Query::buildQuery(){
std::string query_type = lower(args[0]);
switch(str2int(query_type) ){
case str2int("set"):// do something
break;
case str2int("getbit"):// do something
break;
................
..........
default:
throw(QueryException("Invalid query type"));
}
}
According to Serhiy where str2int is like:
constexpr unsigned int str2int(const char* str, int h = 0)
{
return !str[h] ? 5381 : (str2int(str, h+1)*33) ^ str[h];
}
how can i exit from nested while() or for() without goto?
for example if i use three loops like below in a function:
void myfun(){
for (;;)
{
while( true )
{
for (;;)
{
//what is the exit code of all loop() from here?
}
}
}
}
using break; only can exit from one loop,
but how can i exit all loops ?
the loops can be limited by counter or unlimited.
I personally would rewrite the code so that you don't have a nested loop in the first place. Something like this:
bool myFun2
{
for (;;)
{
if(something) return true;
}
// If the loop isn't "forever", return false here?
}
bool myFun1()
{
while( true )
{
if (myFun2()) return true;
}
// return false here if needed.
}
void myfun()
{
for (;;)
{
if(myFun1()) break;
}
}
This becomes much easier to follow than trying to figure out which conditions some exitLoop variable gets set, for example.
You can't, you need another break at while context or change yours loops usign a variable as a exit flag:
bool exit = false;
for (;;){
while (!exit){
for (;;){
exit = true;
break;
}
}
if (exit) break;
}
An so on for as many loop do you have in your code
If you want to jump out of the function that is leave the function then you should use return. However if you want to just jump off the nested loops & not out of the function then you can throw an exception. This method will help you from breaking the code into several functions as some have done. However exceptions are meant for library designers & we should avoid using them too much. Personally speaking using goto is the best thing in this case but as you asked against it, hence I'm saying so. Well then your code will look like this :-
void myfun()
{
try
{
for (;;)
{
while( true )
{
for (;;)
{
if (/*some condition*/)
throw false;
}
}
}
}
catch (bool)
{
cout<<"caught";
}
// do stuffs if your code is successful that is you don't break out
}
I've come across a situation where I have a bunch of "systems" that need to be initialized in sequence, with the next system only being initialized if all of the proceeding systems initialized successfully.
This has led me to a whole slew of nested if - else statements. Here's some pseudo-code for visualization.
bool mainInit () {
if (!system1Init ()) {
reportError (); // some error reporting function
}
else {
if (!system2Init ()) {
reportError ();
}
else {
if (!system3Init ()) {
// ... and so on
I find that this starts to look like a mess when you get even a handful of levels to it.
Now I thought of using a switch statement instead, starting at the first case and falling through to the other cases on success, only breaking if there's an error.
bool mainInit () {
switch (1) {
case 1:
if (!system1Init ()) {
reportError ();
break;
}
case 2:
if (!system2Init ())
reportError ();
break;
}
// ....
}
Now, I like this a lot better. I find it much easier to read, especially with some decent comments, but I'm fairly new to programming.
So, my question is: Seeing how this is not how switch statements are traditionally used(at least from what I've seen), is something like this acceptable, or would this be considered bad form?
Being new to programming, I'm trying not to develop too many bad habits that might frustrate and make things more difficult for other programmers down the road.
I did a search, but most of what I found had to do with replacing chains of if - else if statements, not replacing nested ones.
Reference all of the systems in an array, for example an std::vector<mySystem*>, and loop over them sequentially, breaking off on the first fail. This way your entire code is reduced to less than 5 lines of code, even for 500+ systems.
The suggested switch hack is an evil example of XY problem solving: your real problem is that you don't have the array of systems, and are using named variables, thus eliminating all options to more flexibly use all systems, like in a loop.
Assuming that all your system#Init() calls are known at compile time, you can very easily put them in a table and then iterate over that table.
typedef (*system_init)(void);
system_init initialization_functions[] =
{
system1Init,
system2Init,
system3Init,
...
systemNInit
};
bool mainInit()
{
for(size_t idx(0); idx < sizeof(initialization_functions) / sizeof(initialization_functions[0]); ++idx)
{
if(!initialization_functions[idx]())
{
ReportError();
return false;
}
}
return true;
}
However, your existing code looks incorrect since the first mainInit() only calls system1Init() and then exits. Probably not what you wanted in the first place.
if(!system1Init())
{
ReportError();
return false;
}
// if you add an else, the system2Init() does not get called
// even if system1Init() succeeds
if(!system2Init())
{
ReportError();
return false;
}
[...]
return true;
Would the switch answer your problem? Not as it was written. That is, if you wanted to call the mainInit() function with a counter, it could be useful. Drupal uses that mechanism:
bool mainInit(int idx)
{
bool r(true);
switch(idx)
{
case 1:
r = system1Init();
break;
case 2:
r = system2Init();
break;
[...]
}
if(!r)
{
ReportError();
}
return r
}
Note that the table mechanism works the same way as the switch. As long as all the code is found in the systemNInit() functions (and it should be), the switch does not add anything, so you could do something like this too:
bool mainInit(int idx)
{
if(idx < 0 || idx >= sizeof(initialization_functions) / sizeof(initialization_functions[0]))
{
throw std::range_error("index out of bounds");
}
if(!initialization_functions[idx]())
{
ReportError();
return false;
}
return true;
}
Calling the mainInit() with an index can be helpful in case you want to "de-initialize" properly:
int main()
{
for(size_t idx(0); idx < ...; ++idx)
{
if(!mainInit(idx))
{
while(idx > 0)
{
--idx;
mainDeinit(idx);
}
exit(1);
}
}
...app do something here...
}
Use custom exceptions with clear error messages and add a try-catch-report-die around the code in main(). Exceptions are there to specifically make your case look good by making "bad path" implicit.
void initX() { ...; throw std::invalid_argument_exception("..."); }
int main() {
try {
init1(); init2(); ... run();
return 0;
} catch (std::exception const& e) {
log(e.what()); exit 42;
}
}
I'd do it this way:
bool mainInit () {
if (!system1Init ()) {
return(false);
}
if (!system2Init ()) {
return(false);
}
if (!system3Init ()) {
return(false);
}
//...
return(true);
}
//...
if(!mainInit()) {
reportError();
}
I would like to transform my if() conditional to switch() from this:
if($configuration['application'][$applicationName]['subdomain'] == true){
foreach($configuration['language'] as $language){
if($language['abbreviation'].'.'.$configuration['application'][$applicationName]['domain'] == $_SERVER['HTTP_HOST']){
$_SESSION['language'] = $language['abbreviation'];
}
}
// If no subdomain detected and redirection is enabled, set default language
if(!isset($_SESSION['language'])){
$_SESSION['language'] = $configuration['application'][$applicationName]['language'];
}
}
else {
$_SESSION['language'] = $configuration['application'][$applicationName]['language'];
}
To this:
switch($configuration['application'][$applicationName]['subdomain']){
case true:
foreach($configuration['language'] as $language){
if($language['abbreviation'].'.'.$configuration['application'][$applicationName]['domain'] == $_SERVER['HTTP_HOST']){
$_SESSION['language'] = $language['abbreviation'];
break;
}
}
default:
$_SESSION['language'] = $configuration['application'][$applicationName]['language'];
break;
}
I think it should be the same but it behaves differently ...
Switch is not working properly ...
I have reformatted your code, please check to make sure it is still correct.
As for your problem, to begin with you are missing a break; statement at the end of your case true: statement. (The break inside the foreach loop simply breaks out of that loop, not the case itself).