Reputation: 161
How do I reduce long if..else statement such as this one?
if( strcmp( alnumToc, "log") == 0){
ARGNUMCHECK( in, 1);
mpfr_log( num, stack[j-1], MPFR_RNDN);
CPYRES( 1);
} else if( strcmp( alnumToc, "log2") == 0){
ARGNUMCHECK( in, 1);
mpfr_log2( num, stack[j-1], MPFR_RNDN);
CPYRES( 1);
} else if( strcmp( alnumToc, "log10") == 0){
ARGNUMCHECK( in, 1);
mpfr_log10( num, stack[j-1], MPFR_RNDN);
CPYRES( 1);
} else if( strcmp( alnumToc, "sqrt") == 0){
ARGNUMCHECK( in, 1);
mpfr_sqrt( num, stack[j-1], MPFR_RNDN);
CPYRES( 1);
} else if( strcmp( alnumToc, "cbrt") == 0){
ARGNUMCHECK( in, 1);
mpfr_cbrt( num, stack[j-1], MPFR_RNDN);
CPYRES( 1);
} else if( strcmp( alnumToc, "abs") == 0){
ARGNUMCHECK( in, 1);
mpfr_abs( num, stack[j-1], MPFR_RNDN);
CPYRES( 1);
} else if( strcmp( alnumToc, "sin") == 0){
ARGNUMCHECK( in, 1);
mpfr_sin( num, stack[j-1], MPFR_RNDN);
CPYRES( 1);
} else if( strcmp( alnumToc, "cos") == 0){
ARGNUMCHECK( in, 1);
mpfr_cos( num, stack[j-1], MPFR_RNDN);
CPYRES( 1);
} else if( strcmp( alnumToc, "csc") == 0){
ARGNUMCHECK( in, 1);
mpfr_csc( num, stack[j-1], MPFR_RNDN);
CPYRES( 1);
} else if( strcmp( alnumToc, "cot") == 0){
ARGNUMCHECK( in, 1);
mpfr_cot( num, stack[j-1], MPFR_RNDN);
CPYRES( 1);
} else if( strcmp( alnumToc, "acos") == 0){
ARGNUMCHECK( in, 1);
mpfr_acos( num, stack[j-1], MPFR_RNDN);
CPYRES( 1);
} else if( strcmp( alnumToc, "asin") == 0){
ARGNUMCHECK( in, 1);
mpfr_asin( num, stack[j-1], MPFR_RNDN);
CPYRES( 1);
} else if( strcmp( alnumToc, "atan") == 0){
ARGNUMCHECK( in, 1);
mpfr_atan( num, stack[j-1], MPFR_RNDN);
CPYRES( 1);
} else if( strcmp( alnumToc, "cosh") == 0){
ARGNUMCHECK( in, 1);
mpfr_cosh( num, stack[j-1], MPFR_RNDN);
CPYRES( 1);
} else if( strcmp( alnumToc, "sinh") == 0){
ARGNUMCHECK( in, 1);
mpfr_sinh( num, stack[j-1], MPFR_RNDN);
CPYRES( 1);
}
It's not just ugly but also slow. I thought of using hashtable with function pointers. Is that good solution?
Note: there will be some mpfr functions, that take more arguments, so I can't create just one macro.
Upvotes: 4
Views: 454
Reputation:
The performance issue came from that strcmp
repeatedly scan the same string over and over again.
If your input data is clean, means no unexpected content in alnumToc
, you can actually skip lots of characters comparison to gain performance. Try this:
switch(alnumToc[0]) {
case 'a': // abs, acos, asin or atan, you don't have to check beyond alnumToc[1]
switch(alnumToc[1]) {
case 'b'
// deal with "abs"
break;
case 'c'
// deal with "acos"
break;
case 's'
// deal with "asin"
break;
case 't'
// deal with "atan"
break;
}
break;
case 'l': // log, log2, or log10, "og" is common, no need to check
switch(alnumToc[3]) {
case '\0' // terminator of c string
// deal with "log"
break;
case '2'
// deal with "log2"
break;
case '1'
// deal with "log10"
break;
}
break;
// you can figure out the rest
}
The bottom line is to only scan alnumToc
once for best performance. If you cannot guarantee alnumToc
is clean, exhause all alnumToc[0], [1], [2], ...
.
Upvotes: 1
Reputation: 213892
That is very poor code and indeed it will be very slow. It must be rewritten from scratch.
The fastest possible way is to store all string literals in a sorted array, then use bsearch
to search through it. Use an array of structs where the string is the search key. You'll have to write your own comparison function for your struct type.
If you find the string you were looking for, call its corresponding function through the function pointer:
typedef struct
{
const char* STRING;
func_t action;
} my_str_thing;
const my_str_thing TABLE [N] =
{
{ "ABC", do_this},
{ "DEF", do_that},
...
};
A hash table would work out too, but seems needlessly complex for this specific case.
Upvotes: 6
Reputation: 22457
Store all of the names and functions in a structure:
struct calcRoutine_t {
const char *name;
void (*function)(int, int, int);
} calc_routine[] = {
{ "log", mpfr_log },
{ "log2", mpfr_log2 },
{ "log10", mpfr_log10 },
{ "sqrt", mpfr_sqrt },
{ "cbrt", mpfr_cbrt },
{ "abs", mpfr_abs },
{ "sin", mpfr_sin },
{ "cos", mpfr_cos },
{ "csc", mpfr_csc },
{ "cot", mpfr_cot },
{ "acos", mpfr_acos },
{ "asin", mpfr_asin },
{ "atan", mpfr_atan },
{ "cosh", mpfr_cosh },
{ "sinh", mpfr_sinh }
};
Loop over the array and use strcmp
to locate the correct function:
for (i=0; i<sizeof(calc_routine)/sizeof(calc_routine[0]); i++)
{
if (!strcmp ( calc_routine[i], alnumToc) )
{
ARGNUMCHECK( in, 1);
calc_routine[i].function (num, stack[j-1], MPFR_RNDN);
CPYRES( 1);
break;
}
}
(You can add a flag for 'success' before the break
, or test if i == sizeof(calc_routine) /sizeof(calc_routine[0])
at the bottom.)
This has the initial advantage you can add, shuffle, and remove any of the sub-routines at will.
As soon as you decided on a final set of names/functions, sort them once by name and then use bsearch
instead of this loop. On success, bsearch
will point to the correct structure member and you can call its associated function right away; on failure, bsearch
will return NULL
.
As noted in a comment, some functions may need more arguments than 1. This number can be stored in the struct calcRoutine_t
as well, to be tested in the loop.
Upvotes: 4
Reputation: 9474
1. Instead keep all strings in an array.
char *strarray[] = {"log", "log2", "blah Blah"};
2. Run a for loop like
for(i = 0; i < NO_OF_STRINGS; i++)
{
// strcmp
}
3. Keep array of function pointers for mpfr_sqrt, mpfr_acos,..
It will reduce code size.
Upvotes: 0