Following situation:
My system gets an hardware signal and writes a time value to a buffer in my
signal handler routine. Afterwards a (software) signal is sent with the time value as argument to the appropriate slot function.
The slot routine gets called correctly, but here my problem lays in:
In the slot function I have a simple switch-case statement like this:
switch(id) {
case 1:
do something..
id = 2;
break;
case 2:
start_time = val;
id = 3;
break;
case 3:
end_time = val;
id = 1;
break;
}
In those three cases I store a start and end time value between case 2 and 3 and
out of those time values I determine the elapsed time between the hardware
signals. This works fine, but now I have to measure the time sometimes "longer",
depening on parameter. This means, I can't stop the measurement at case 3 instead
I have case 4, 5, 6 and so on . What is an elegant and optimal solution for this "problem"
instead of writing:
if (param < xy) {
switch(id) {
case 1:
...
break;
case 2:
...
break;
} else if (param > xy) {
switch(id) {
case 1:
...;
break;
case 2:
...;
break;
case 3:
...;
break;
case 4:
...;
break;
case 5:
...;
break;
}
}
}
What you are describing is called a finite state machine there are a large number of excellent state machine libraries out there that will take care of the heavy lifting for you.
Take a look at this question and some of the others that it references.
You can try following:
switch ((param - xy) >= 0 ? id : -id) {
// param >= xy cases
case 1:
...
break;
case 2:
...
break;
...
// param < xy cases
case -1:
...
break;
case -2:
...
break;
...
}
Or for something fun an exciting, you could write some self modifying code to dynamically change your swithc jump table as the parameters it receives differ. You'd have to allocate a large enough area for the largest table size and play around with funciton pointers or assembler, but it could be done.
Try using a std::map of function pointers, a.k.a. jump table, rather than a switch statement. The map allows flexibility during run-time.
Store a pointer to the function, along with the case value. Search the map for the case value, retrieve the pointer and dereference to call the function.
Related
I am looking for a way to convert a list of QVariants into one of overloaded functions that takes either 1, 2, 3 or 4 QVariants.
My initial idea is to just make a switch statement on the list's length and call functions based on it, but I am not sure if it is the only solution for this problem, and it doesn't look very scalable:
switch(list.length())
{
case 0:
fun();
case 1:
fun(list[0]);
break;
case 2:
fun(list[0], list[1]);
break;
case 3:
fun(list[0], list[1], list[2]);
break;
...
}
A simple programm that reads strings, and responds using a switch;
in this do-while loop containing a switch, I am able to run case 1-4 with no issues, but once i hit the default case, the programme simply loops the default case over and over again the code is as follows;
do { switch ( switchstring (entry, input) )
/*the switchstring function is one 1 wrote to convert a given entry(string),
into an input(integer)*/
{
case 1:
//code
repeat = 2;
break;
case 2:
//code
repeat = 2;
break;
case 3:
//code
repeat = 2;
break;
case 4:
//code
repeat = 2;
break;
default:
//code
repeat = 1;
break;}} while(repeat == 1);
the 2nd question is regarding my switchstring() function; is there a way to change the switch function such that it reads;
case (insert string):
i.e. so that I can remove the entire switchstring() function
thanks in advance!
Show us how switchstring (entry, input) works.
The problem you are facing is because, in default you do the following:
repeat = 1;
Which makes while(repeat == 1) always true. And then switchstring (entry, input) always return something that makes your switch block always go the the default case again.
When no case will be true in switch, then it will go in default case of switch and you are specifying repeat=1; in default. After that while condition will be checked and it will be true because repeat is 1, again it will go to do and check condition, your switch function will return something and it will go to default.
To solve 2nd question regarding your switchstring() function, you have to show your code what you are doing in that function, So that i can give you best suggestion.
I am trying to show my 4by4 matrix in qt gui, there for I have used one text browser for each element of matrix. Right now I am able to display matrix using switch case but I dont like this method. I want to make an array in which I can save the name of textbrowser and willing to access them using for loop. below is the my current code. please guide me how can I get what I am willing to do.
for (i = 0; i <= 3; i++)
{
for (j = 0; j <= 3;j++)
{
switch(no){
case 1:
ui->textBrowser_200->setText(text1);
break;
case 2:
ui->textBrowser_201->setText(text1);
break;
case 3:
ui->textBrowser_202->setText(text1);
break;
case 4:
ui->textBrowser_203->setText(text1);
break;
case 5:
ui->textBrowser_204->setText(text1);
break;
case 6:
ui->textBrowser_205->setText(text1);
break;
case 7:
ui->textBrowser_206->setText(text1);
break;
case 8:
ui->textBrowser_207->setText(text1);
break;
case 9:
ui->textBrowser_208->setText(text1);
break;
case 10:
ui->textBrowser_209->setText(text1);
break;
case 11:
ui->textBrowser_210->setText(text1);
break;
case 12:
ui->textBrowser_211->setText(text1);
break;
case 13:
ui->textBrowser_212->setText(text1);
break;
case 14:
ui->textBrowser_213->setText(text1);
break;
case 15:
ui->textBrowser_214->setText(text1);
break;
case 16:
ui->textBrowser_215->setText(text1);
break;
}
no++;
}
}
Here is a simple solution which will allow you to keep your current Designer ui:
Add this member variable to your own class, to have two-dimensional matrix of widget pointers:
std::array< std::array <QTextBrowser *, 4>, 4> mTextBrowserMatrix;
Then initialize it in the constructor, with 16 lines of code like this, after you have called setupUi() for the Designer ui:
mTextBrowserMatrix[0][0] = ui->textBrowser_200;
// repeat above for all 16 widgets.
And then just access them like mTextBrowserMatrix[0][0] etc.
You could use any container or even plain C arrays for this, above is just an example.
Of course you will save some repetitive copy-paste style code and have cleaner Designer design, if you just create the QTextBrowser matrix in code, instead of using Designer for it. But since you already have them, might as well stick with it for now, 16 lines of repeated code is not that horrible.
I came across a case-switch piece of code today and was a bit surprised to see how it worked. The code was:
switch (blah)
{
case a:
break;
case b:
break;
case c:
case d:
case e:
{
/* code here */
}
break;
default :
return;
}
To my surprise in the scenario where the variable was c, the path went inside the "code here" segment. I agree there is no break at the end of the c part of the case switch, but I would have imagined it to go through default instead. When you land at a case blah: line, doesn't it check if your current value matches the particular case and only then let you in the specific segment? Otherwise what's the point of having a case?
This is called case fall-through, and is a desirable behavior. It allows you to share code between cases.
An example of how to use case fall-through behavior:
switch(blah)
{
case a:
function1();
case b:
function2();
case c:
function3();
break;
default:
break;
}
If you enter the switch when blah == a, then you will execute function1(), function2(), and function3().
If you don't want to have this behavior, you can opt out of it by including break statements.
switch(blah)
{
case a:
function1();
break;
case b:
function2();
break;
case c:
function3();
break;
default:
break;
}
The way a switch statement works is that it will (more or less) execute a goto to jump to your case label, and keep running from that point. When the execution hits a break, it leaves the switch block.
That is the correct behavior, and it is referred to as "falling through". This lets you have multiple cases handled by the same code. In advanced situations, you may want to perform some code in one case, then fall through to another case.
Contrived example:
switch(command)
{
case CMD_SAVEAS:
{
this->PromptForFilename();
} // DO NOT BREAK, we still want to save
case CMD_SAVE:
{
this->Save();
} break;
case CMD_CLOSE:
{
this->Close();
} break;
default:
break;
}
This is called a fall-through.
It is exactly doing what you are seeing: several cases is going to execute same piece of code.
It is also convenient in doing extra processing for certain case, and some shared logic:
// psuedo code:
void stopServer() {
switch (serverStatus)
case STARTING:
{
extraCleanUpForStartingServer();
// fall-thru
}
case STARTED:
{
deallocateResources();
serverStatus = STOPPED;
break;
}
case STOPPING:
case STOPPED:
default:
// ignored
break;
}
This is a typical use of fall-through in switch-case. In case of STARTING and STARTED, we need to do deallocateResources and change the status to STOPPED, but STARTING need some extra cleanup. By the above way, you can clearly present the 'common logic' plus extra logic in STARTING.
STOPPED, STOPPING and default are similar, all of them fall thru to default logic (which is ignoring).
It is not always a good way to code like this but if it is well used it can present the logic better.
Luckily for us, C++ doesn't depend on your imagination :-)
Think of the switch labels as "goto" labels, and the switch(blah) simply "goes to" the corresponding label, and then the code just flows from there.
Actually the switch statement works the way you observed. It is designed so that you can combine several cases together until a break is encountered and it acts something like a sieve.
Here is a real-world example from one of my projects:
struct keystore_entry *new_keystore(p_rsd_t rsd, enum keystore_entry_type type, const void *value, size_t size) {
struct keystore_entry *e;
e = rsd_malloc(rsd, sizeof(struct keystore_entry));
if ( !e )
return NULL;
e->type = type;
switch (e->type) {
case KE_DOUBLE:
memcpy(&e->dblval, value, sizeof(double));
break;
case KE_INTEGER:
memcpy(&e->intval, value, sizeof(int));
break;
/* NOTICE HERE */
case KE_STRING:
if ( size == 0 ) {
/* calculate the size if it's zero */
size = strlen((const char *)value);
}
case KE_VOIDPTR:
e->ptr = rsd_malloc(rsd, size);
e->size = size;
memcpy(e->ptr, value, size);
break;
/* TO HERE */
default:
return NULL;
}
return e;
}
The code for KE_STRING and KE_VOIDPTR cases is identical except for the calculation of size in case of string.
How to handle when there is a new SW Release sometime and it adds another index to switch case. Index represents a parameter in this case. For example,
Rel1: i = 1-5, 7 (excluding 6)
Rel2: i = 1-7
for (int i = 1; i<=7;i++)
{
switch (i)
{
case 1: /*process data*/ break;
case 2: /*process data*/ break;
case 3: /*process data*/ break;
case 4: /*process data*/ break;
case 5: /*process data*/ break;
// case 6: // REL 2
case 7: /*process data*/ break;
default: break;
}
}
Can I actually check by adding if statement between those cases? Any better idea?
#define which release is this and then use #ifdef.
switch (i)
{
case 1: /*process data*/ break;
case 2: /*process data*/ break;
case 3: /*process data*/ break;
case 4: /*process data*/ break;
case 5: /*process data*/ break;
#ifdef REL_2
case 6: /*process data*/ break; // <-- executed only for REL_2
#endif
case 7: /*process data*/ break;
default: break;
}
In production code if I have an unexpected result like this I often try to capture and log it.
Throwing an exception may be ok depending on how it's handled. For future-proofing it's usually good to design stuff like this to gracefully handle new values.
Instead of using a 'for' loop, use a 'while' loop with a release number generator function.
Pseudo code:
typedef enum {rel1, rel2} rel_t;
// Where 'ReleaseSequence' is a generator functor. Class that takes rel_t
// in constructor and creates an appropriate functor that returns the
// required sequence of numbers for a particular release
ReleaseSequence seq(rel1);
while (i = seq()) {
switch (i) {
case 1:...
case 2:...
case 3:...
case 4:...
case 5:...
case 6:...
case 7:...
default:
break;
}
}
I find it very strange to see a switch nested within a loop...
It looks like you are executing a pipe. Then why don't you simply define is as one.
For example, a simple array of pointer to functions would do nicely. You can define one such pipe per release, and provide "noop" functions for the parameters to ignore.
If you want to catch unexpected values, shouldn't you put that check in the default part?
...
default:
assert(false); // We should never get here!