c function pointer pass parameter during runtime - c

I have a pretty complex problem about c function pointers and passing the parameters to them.
I have a function pointer and a couple of function addresses within a lookup table. I get all of my data via a serial interface. First the number of the function which has to be called. I look it up in the table and pass the reference to my function pointer.
After that, i receive several pairs of 4 byte values as data as the arguments. Problem is, i have to call different functions with the same return type but a different amount of parameters.
Is there a way to pass dynamically data to a function call. Maybe by pushing them on the stack manually? Couldn't find a solution for that.
Does anybody has any idea or a hint to solve that problem?

I don't believe there's an easy way to answer this since argument passing is ABI (Application Binary Interface) specific. If your platform is fixed and you don't mind writing non-portable code then you can write your code in an ABI specific way but I wouldn't advise that.
I've solved this issue in a small emulator I wrote, for me it was easier since the number of commands was never greater than 4, I basically used a union of function pointers which had the number of arguments I wanted and a switch statement to select the right one.
typedef struct
{
union
__attribute__((__packed__))
{
void *func;
void (*func_a0)(void);
uint32_t (*func_a0r)(void);
void (*func_a1)(uint32_t);
uint32_t (*func_a1r)(uint32_t);
void (*func_a2)(uint32_t, uint32_t);
uint32_t (*func_a2r)(uint32_t, uint32_t);
void (*func_a3)(uint32_t, uint32_t, uint32_t);
uint32_t (*func_a3r)(uint32_t, uint32_t, uint32_t);
void (*func_a4)(uint32_t, uint32_t, uint32_t, uint32_t);
uint32_t (*func_a4r)(uint32_t, uint32_t, uint32_t, uint32_t);
};
unsigned args;
bool ret;
const char* name;
} jump_entry_t;
bool jump_table_exec(
jump_table_t* table, void* addr,
uint32_t* args, uint32_t* ret)
{
#ifdef JUMP_TABLE_DEBUG
if (!table)
return false;
#endif
if ((uintptr_t)addr < (uintptr_t)table->base)
return false;
unsigned i = ((uintptr_t)addr - (uintptr_t)table->base);
if ((i & 4) || (i >= table->size))
return false;
jump_entry_t j = table->entry[i >> 3];
if (!j.func)
return false;
if (j.args && !args)
return false;
if (j.ret)
{
if (!ret) return false;
switch (j.args)
{
case 0:
*ret = j.func_a0r();
break;
case 1:
*ret = j.func_a1r(args[0]);
break;
case 2:
*ret = j.func_a2r(args[0], args[1]);
break;
case 3:
*ret = j.func_a3r(args[0], args[1], args[2]);
break;
case 4:
*ret = j.func_a4r(args[0], args[1], args[2], args[3]);
break;
default:
return false;
}
}
else
{
switch (j.args)
{
case 0:
j.func_a0();
break;
case 1:
j.func_a1(args[0]);
break;
case 2:
j.func_a2(args[0], args[1]);
break;
case 3:
j.func_a3(args[0], args[1], args[2]);
break;
case 4:
j.func_a4(args[0], args[1], args[2], args[3]);
break;
default:
return false;
}
}
#ifdef JUMP_TABLE_DEBUG
if (j.name)
{
fprintf(stderr, "Info: Jump table %s(", j.name);
if (j.args >= 1) fprintf(stderr, "%" PRIu32, args[0]);
if (j.args >= 2) fprintf(stderr, ", %" PRIu32, args[1]);
if (j.args >= 3) fprintf(stderr, ", %" PRIu32, args[2]);
if (j.args >= 4) fprintf(stderr, ", %" PRIu32, args[3]);
fprintf(stderr, ")");
if (j.ret) fprintf(stderr, " returned %" PRIu32, *ret);
fprintf(stderr, ".\n");
}
#endif
return true;
}

Sometimes the following approach using unions is useful:
union foo {
struct {
int arg1;
} f1_args;
struct {
int arg1, arg2;
} f2_args;
};
int f1(union foo*);
int f2(union foo*);
int (*table[])(union foo*) = {f1, f2};
//...
union foo data;
//...
int answer = table[1](&data); // calls f2, which uses arg1 and arg2
And, if you prefer, f1 and f2 can be simple wrappers to the "real" functions, as in:
int f1(union foo *u) { return f1_real(u->f1_args.arg1); }
int f2(union foo *u) { return f2_real(u->f2_args.arg1, u->f2_args.arg2); }
This is quite flexible. But if your arguments are always only 4-byte ints, then you can get rid of the union and just use arrays. Rewritten, the above becomes:
int f1(uint32_t *a) { return f1_real(a[0]); } // wrapper
int f2(uint32_t *a) { return f2_real(a[0], a[1]); } // wrapper
int (*table[])(uint32_t *) = {f1, f2}; // lookup table
//...
uint32_t data[99]; // data from e.g. serial port
//...
int answer = table[1](data); // calls f2, which uses two args

Since the functions can distinguish their parameters, you can always give them a ... type. For example:
int f(...)
{
/* extract one int */
}
int g(...)
{
/* extract two floats */
}
...
int (*fp)(...);
if (type_one)
fp(10);
else if (type_two)
fp(1.3, 4.3);
Or better yet use a union. However, in your particular case, since the parameters themselves are "pairs of 4 bytes", you can always use an array:
struct arg
{
uint32_t pair_of_4_bytes[2];
};
int f(struct arg *args, size_t count)
{
}
int g(struct arg *args, size_t count)
{
}
...
int (*fp)(struct arg *args, size_t count);
struct arg args[MAX];
size_t count = 0;
/* get args from serial and put in args/count */
fp(args, count);

I think "Variadic functions" can solve your problem needs. Checkout a simple example here:
http://www.gnu.org/software/libc/manual/html_node/Variadic-Example.html#Variadic-Example

Related

Passing a generic argument in a C function

I have a struct
typedef struct
{
void *l_var;
void *r_var;
}EXPR;
EXPR expr;
I initialize it
expr.l_var = &motor_rt_params[0].position;
expr.r_var = &motor_rt_params[1].position;
Now I want to operate with the values
void SCRIPT_Process(void *l_var, void *r_var, uint32_t oper)
{
int32_t res;
switch (oper)
{
case OP_PLUS:
res = *((??? *) l_var) + *((??? *)r_var);
break;
case OP_MINUS:
res = *((??? *) l_var) - *((??? *)r_var);
break;
}
}
SCRIPT_Process(expr.l_var , expr.r_var , OP_PLUS);
The variables may be 32, 16, 8 bit.
The question - how can I cast it to an appropriate type (instead of (??? *)) in run time?
What about to also save the size when you initialize the pointers ?
typedef struct
{
void *l_var;
void *r_var;
size_t sz;
}EXPR;
expr.l_var = &motor_rt_params[0].position;
expr.r_var = &motor_rt_params[1].position;
expr.sz = sizeof(motor_rt_params[1].position);
allowing to to for instance
void SCRIPT_Process(void *l_var, void *r_var, size_t sz, uint32_t oper)
{
int32_t res;
switch (oper)
{
case OP_PLUS:
if (sz == sizeof(int32_t))
res = *((int32_t *) l_var) + *((int32_t *)r_var);
else /* suppose a int16_t */
res = *((int16_t *) l_var) + *((int16_t *)r_var);
break;
case OP_MINUS:
if (sz == sizeof(int))
res = *((int32_t *) l_var) - *((int32_t *)r_var);
else /* suppose a int16_t */
res = *((int16_t *) l_var) - *((int16_t *)r_var);
}
}
SCRIPT_Process(expr.l_var , expr.r_var , expr.sz, OP_PLUS);
supposing it is int or short only, I let you adding the case of a int8_t
The advantage also placing the size in the EXPR is to not lost that information / create inconsistencies by error because managed in different piece of code.
Or may be to give the EXPR rather than the fields separately in arguments to SCRIPT_Process ?
May be you also need to know if signed or unsigned, with an additional field, or using an int for the size valuing the positive size for unsigned (4, 2 or 1) and negative size for a signed (-4, -2 -1).
An other way is to save pointers to the right functions rather than the size, a kind of C++ virtual implementation.
Of course all of that supposes you cannot save values as int32_t in the struct and you really need to save the pointers.
safe and the same generic. No pointer punning. The type list in the union might be much longer
typedef union
{
int8_t u8;
int16_t u16;
int32_t u32;
}data_t;
int32_t get(data_t *o, int size)
{
switch (size)
{
case 8:
return o -> u8;
break;
case 16:
return o -> u16;
break;
default:
return o -> u32;
break;
}
}
void SCRIPT_Process(data_t *l_var, data_t *r_var, uint32_t oper, int sizel, int sizer)
{
int32_t res;
int32_t l = get(l_var, sizel);
int32_t r = get(r_var, sizer);
switch (oper)
{
case OP_PLUS:
res = l + r;
break;
case OP_MINUS:
res = l -r;
break;
/* ..... */
}
}
You can simply store the values in the void*, rather than their address:
expr.l_var = (void*)motor_rt_params[0].position;
expr.r_var = (void*)motor_rt_params[1].position;
Then to use them, cast to intptr_t (values, not pointers). These types are guaranteed to be the same width as a pointer, so you won't need to care anymore about the width of the source values.
The variables may be 32, 16, 8 bit.
So you need to know:
variables addresses
variables types and
operation
And you need to pass that information along.
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
enum type_e {
TYPE_u8,
TYPE_u16,
TYPE_u32,
// TODO: add more, ex. TYPE_INT, TYPE_DOUBLE, etc.
};
enum oper_e {
OP_PLUS,
OP_MINUS,
// TODO: add mode, ex. OP_POW or OP_DIV etc.
};
void SCRIPT_Process(void *res, const void *l, const void *r, enum type_e type, enum oper_e oper)
{
switch (oper) {
case OP_PLUS:
switch (type) {
case TYPE_u8:
*(uint8_t*)res = *(uint8_t*)l + *(uint8_t*)r;
break;
case TYPE_u16:
*(uint16_t*)res = *(uint16_t*)l + *(uint16_t*)r;
break;
case TYPE_u32:
*(uint32_t*)res = *(uint32_t*)l + *(uint32_t*)r;
break;
default:
assert(0);
}
break;
case OP_MINUS:
// TODO:
assert(0);
}
}
int main() {
uint32_t l = 5, r = 2, res;
SCRIPT_Process(&res, &r, &l, TYPE_u32, OP_PLUS);
printf("%d + %d = %d\n", (int)l, (int)r, (int)res);
}
Would be nice to provide a macro to make the code more verbose and with less typing:
#define SCRIPT_PROCESS_MACRO(type, res, l, op, r) \
*(type*)res = *(type*)l op *(type*)r;
void SCRIPT_Process(void *res, void *l, void *r, enum type_e type, enum oper_e oper)
{
switch (oper) {
case OP_PLUS:
switch (type) {
case TYPE_u8:
SCRIPT_PROCESS_MACRO(uint8_t, res, l, +, r);
break;
case TYPE_u16:
SCRIPT_PROCESS_MACRO(uint16_t, res, l, +, r);
break;
case TYPE_u32:
SCRIPT_PROCESS_MACRO(uint32_t, res, l, +, r);
break;
default:
assert(0);
}
break;
case OP_MINUS:
// TODO:
assert(0);
}
}
Or even more simple with more macros, which makes adding new operations and types trivial:
#define SCRIPT_PROCESS_MACRO(type, res, l, op, r) \
*(type*)res = *(type*)l op *(type*)r;
#define SCRIPT_PROCESS_TYPE_CASES(type, res, l, op, r) \
switch (type) { \
case TYPE_u8: SCRIPT_PROCESS_MACRO(uint8_t, res, l, op, r); break; \
case TYPE_u16: SCRIPT_PROCESS_MACRO(uint16_t, res, l, op, r); break; \
case TYPE_u32: SCRIPT_PROCESS_MACRO(uint32_t, res, l, op, r); break; \
default: assert(0); break; \
}
void SCRIPT_Process(void *res, void *l, void *r, enum type_e type, enum oper_e oper)
{
switch (oper) {
case OP_PLUS:
SCRIPT_PROCESS_TYPE_CASES(type, res, l, +, r);
break;
case OP_MINUS:
SCRIPT_PROCESS_TYPE_CASES(type, res, l, -, r);
break;
}
}
Or you could even go with even more generic solution, having separate types for result, left and right operands:
void SCRIPT_Process(
void *res, enum type_e restype,
const void *l, enum type_e ltype,
const void *r, enum type_e rtype,
enum oper_e oper) {
if (restype == TYPE_u8 && ltype == TYPE_u32 && rtype == TYPE_u16 && oper == OP_ADD) {
*(uint8_t*)res = *(uint32_t*)ltype + *(uint16_t*)rtype;
} if ( // and so on so on so on so on ....
}

Call c functions from fortran(type enum)

Recently,I call c function from fortran with iso_c_binding.But I found some c code.such as:
typedef enum
{
STRUMPACK_FLOAT,
STRUMPACK_DOUBLE,
STRUMPACK_FLOATCOMPLEX
} STRUMPACK_PRECISION;
typedef enum
{
STRUMPACK_MT,
STRUMPACK_MPI_DIST
} STRUMPACK_INTERFACE;
typedef struct
{
int solver;
STRUMPACK_PRECISION precision1;
STRUMPACK_INTERFACE interface1;
}STRUMPACK_SparseSolver;
int STRUMPACK_init(STRUMPACK_SparseSolver * S,
STRUMPACK_PRECISION precision1, STRUMPACK_INTERFACE interface1, int verbose)
{
S->precision1 = precision1;
S->interface1 = interface1;
switch (interface1)
{
case STRUMPACK_MT:
{
switch (precision1)
{
case STRUMPACK_FLOAT:
printf("srtumpack_float %d\n", verbose);
break;
case STRUMPACK_DOUBLE:
printf("srtumpack_double %d\n", verbose);
break;
default:
printf("ERROR: wrong precision!");
}
}
break;
default:
printf("ERROR: wrong interface!");
}
return 0;
}
I don't know that call this c subfunction with Fortran.because of this structs:
typedef enum
{
STRUMPACK_MT,
STRUMPACK_MPI_DIST
} STRUMPACK_INTERFACE;
I don't know how to solve this problem . I will appreciate any contribution, suggestion about the problem.
Thanks
That isn't a structure, but an enum (enumeration) supported by ISO binding entity ENUM.
In case your binding miss enumerations the following is a workaround.
Enums in C are more or less constant integers which values are assigned by compiler in generally increasing way. You can also force values for each enum member using assignment as in:
typedef enum
{
STRUMPACK_FLOAT = 0,
STRUMPACK_DOUBLE,
STRUMPACK_FLOATCOMPLEX = 100
} STRUMPACK_PRECISION
In this case i.e. we impose to STRUMPACK_FLOATCOMPLEX the value 100. We made the same with the first member, in any case the first member have value 0 by default. The second member STRUMPACK_DOUBLE will get the value 1 as progressive increment from previous member.
Anyway you can get better info on how enum works googling on the net.
In your case the easier way to solve the problem is to convert enums in definitions and using int's as type like in:
#define STRUMPACK_FLOAT 0
#define STRUMPACK_DOUBLE 1
#define STRUMPACK_FLOATCOMPLEX 2
typedef int STRUMPACK_PRECISION;
#define STRUMPACK_MT 0
#define STRUMPACK_MPI_DIST 1
typedef int STRUMPACK_INTERFACE;
typedef struct
{
int solver;
STRUMPACK_PRECISION precision1;
STRUMPACK_INTERFACE interface1;
} STRUMPACK_SparseSolver;
int STRUMPACK_init(STRUMPACK_SparseSolver * S, STRUMPACK_PRECISION precision1,
STRUMPACK_INTERFACE interface1, int verbose)
{
S->precision1 = precision1;
S->interface1 = interface1;
switch (interface1)
{
case STRUMPACK_MT:
{
switch (precision1)
{
case STRUMPACK_FLOAT:
printf("srtumpack_float %d\n", verbose);
break;
case STRUMPACK_DOUBLE:
printf("srtumpack_double %d\n", verbose);
break;
default:
printf("ERROR: wrong precision!");
}
}
break;
default:
printf("ERROR: wrong interface!");
}
return 0;
}

Refactoring: Very similar switch cases

I have several struct declared which contain different data. I also have an enum that corresponds to those structures. There are several places in my code where I need to access information about the structures and I'm doing it via the enum. This results in few switch statements that return this information.
I've enclosed those switch statements in their own functions in order to re-use wherever possible. This resulted in three functions that look very similar.
Example psuedo-code:
#include <stdio.h>
typedef struct
{
int varA;
char varB;
} A;
typedef struct
{
int varA;
int varB;
int varC;
} B;
typedef struct
{
int varA;
short varB;
} C;
typedef enum { structA, structB, structC } STRUCT_ENUM;
int returnSize(STRUCT_ENUM structType)
{
int retVal = 0;
switch(structType)
{
case structA:
retVal = sizeof(A);
break;
case structB:
retVal = sizeof(B);
break;
case structC:
retVal = sizeof(C);
break;
default:
break;
}
return retVal;
}
void printStructName(STRUCT_ENUM structType)
{
switch(structType)
{
case structA:
printf("Struct: A\r\n");
break;
case structB:
printf("Struct: B\r\n");
break;
case structC:
printf("Struct: C\r\n");
break;
default:
break;
}
}
void createDataString(STRUCT_ENUM structType, char* output, unsigned char* input)
{
switch(structType)
{
case structA:
{
A a = *(A*)input;
sprintf(output, "data: %d, %d", a.varA, a.varB);
break;
}
case structB:
{
B b = *(B*)input;
sprintf(output, "data: %d, %d, %d", b.varA, b.varB, b.varC);
break;
}
case structC:
{
C c = *(C*)input;
sprintf(output, "data: %d, %d", c.varA, c.varB);
break;
}
default:
break;
}
}
int main(void) {
char foobar[50];
printf("Return size: %d\r\n", returnSize(structA));
printStructName(structB);
C c = { 10, 20 };
createDataString(structC, foobar, (unsigned char*) &c);
printf("Data string: %s\r\n", foobar);
return 0;
}
Those free functions basically contain the same switch with different code placed in the cases. With this setup, adding new struct and enum value results in three places in the code that needs changing.
The question is: is there a way to refactor this into something more maintainable? Additional constraint is that the code is written in C.
EDIT: online example: http://ideone.com/xhXmXu
You can always use static arrays and use STRUCT_ENUM as the index. Given the nature of your functions, I don't really know if you would consider it more maintainable, but it's an alternative I usually prefer, examples for names and sizes:
typedef enum { structA, structB, structC, STRUCT_ENUM_MAX } STRUCT_ENUM;
char *struct_name[STRUCT_ENUM_MAX] = {[structA] = "Struct A", [structB] = "Struct B", [structC] = "Struct C"};
size_t struct_size[STRUCT_ENUM_MAX] = {[structA] = sizeof(A), [structB] = sizeof(B), [structC] = sizeof(C)};
for printing content you can keep a similar array of functions receiving a void * that will print the value of this argument.
Edit:
Added designated initializers as per Jen Gustedt's comment.
You can make it into a single function and a single switch, with an additional parameter. Like so
int enumInfo(STRUCT_ENUM structType, int type) // 1 = returnSize 2 = printStructName
{
int retVal = 0;
switch(structType)
{
case structA:
If ( type == 1 ) { retVal = sizeof(A); }
else { printf("Struct: A"); }
break;
case structB:
If ( type == 1 ) { retVal = sizeof(B); }
else { printf("Struct: B"); }
break;
case structC:
If ( type == 1 ) { retVal = sizeof(C); }
else { printf("Struct: C"); }
break;
default:
break;
}
return retVal;
}

printf with reference arguments

Imagine having a label that after created updates x amount of times / sec. The text of the label is given as a format-specifier text (ala printf), and any arguments for the format-specifier is updated on redraw, because the arguments for the format specifier is pointers to their respective values.
Does any variant of sprintf work like this?
The code would work something like this:
/* client */
createLabel("Value is %f", &myFloatValue);
I haven't quite figured out a way to do this yet, does anyone have any ideas? I guess one could parse the format text, retrieve the pointers (and types), and store them as some object in a list, where you later could reprint the text and maybe delegate the formatting to the objects themselves, passing them only a textbuffer.. hmmm
Btw, the interface is C, but the host is C++.
Okay i got a "working" prototype, but it's written mainly in assembler. Anyway it demonstrates the supposed use of the api. Can anyone see a portable way to do this / have a better idea for the implementation?
It's pretty large so i'm posting it on pastebin:
http://pastebin.com/H8ZpWb4u
So your createLabel interface would store the format string, along with the addresses of the variables you're wanting to display within the string. Then just use standard old sprintf to reformat the text. Just be careful with those pointers to the data, and make sure you invalidate them when necessary.
I'm not really sure what the problem is. What else are you looking for? sprintf is capable of doing what you want, but you're going to have to track the format string and variable addresses yourself.
Okay i suddenly got an idea .. stringstream + templated polymorphism. I ended up writing the thing in C++ in 5 mins, and at the very least it's a huge improvement.
#include <string>
#include <iostream>
#include <vector>
#include <sstream>
class CBaseValue
{
public:
virtual void toString(std::stringstream & buf) = 0;
};
template< typename T >
class CValue : public CBaseValue
{
typedef T type;
typedef T * ptr_type;
type * val;
public:
CValue(void * val)
{
this->val = reinterpret_cast<ptr_type>(val);
}
CValue(type * val) : val(val) {}
virtual void toString(std::stringstream & buf) {
buf << *val;
}
};
class CLabel
{
std::stringstream ss;
std::vector<CBaseValue *> valueList;
std::string format;
public:
CLabel() {};
void reset() {
format.clear();
ss.str("");
for(unsigned i = 0; i < valueList.size(); i++) {
delete valueList[i];
}
valueList.clear();
}
void setFormat(const char * fmt, ...) {
reset();
format = fmt;
va_list args;
va_start(args, fmt);
for(unsigned i = 0; i < format.size(); ++i) {
if(format[i] == '%') {
++i;
switch(fmt[i])
{
case 'd':
valueList.push_back(new CValue<unsigned int>( va_arg(args, void *) ));
break;
case 'f':
valueList.push_back(new CValue<float>( va_arg(args, void *) ));
break;
}
}
}
va_end(args);
}
std::string get() {
ss.str("");
unsigned count(0);
for(unsigned i = 0; i < format.size(); i++) {
if(format[i] == '%') {
i++; // ignore type specifiers, already polymorphically solved
valueList[count++]->toString(ss);
} else {
ss << format[i];
}
}
return ss.str();
}
~CLabel() {
reset();
}
};
int main() {
int test = 2;
float val = 3.14f;
CLabel myLabel;
myLabel.setFormat("Stringstream test, float: %f, and an int: %d \n", &val, &test);
std::cout << myLabel.get();
test = 3;
std::cout << myLabel.get();
system("pause");
}
You could do something relatively simple with std::bind or boost::bind. I'll leave it as an exercise on how to massage a C interface on top of this.
#include <functional>
int main() {
int test = 2;
float val = 3.14f;
std::function<int()> label = std::bind(
printf,
"Stringstream test, float: %f, and an int: %d \n",
std::ref(val),
std::ref(test));
label();
test = 3;
label();
}

write a C construct

Is there a way (in C) to write a construct like the switch statement, but for strings? Is there a way to write a C construct at all in C?
By C construct I mean a statement with braces ... like an if statement has braces, and it's a C construct... right?
The simplest approach is an if-else chain using strcmp to do the comparisons:
if (strcmp(str, "String 1") == 0)
// do something
else if (strcmp(str, "String 2") == 0)
// do something else
else if (strcmp(str, "String 3") == 0)
// do something else
...
else
printf("%s not found\n", str);
A more complicated approach is to use a lookup table, keyed by the string:
struct lookup {const char *key; int value};
struct lookup LookupTable[] = {
{"String 1", 1},
{"String 2", 2},
{"String 3", 3},
...
{NULL, -1}
};
int lookup(const char *key)
{
size_t i = 0;
while (LookupTable[i].key != NULL)
if (strcmp(str, LookupTable[i].key) == 0)
return LookupTable[i].value;
else
i++;
return -1;
}
...
switch(lookup(str))
{
case 1: ...
case 2: ...
case 3: ...
...
default: printf("%s not found\n", str); break;
}
If you want to get really fancy, you could modify the lookup table so that the value is a pointer to a function:
void String1Cmd(void) { ... }
void String2Cmd(void) { ... }
void String3Cmd(void) { ... }
...
void BadCmd(void) { printf("Key not found!\n"); }
struct lookup {char *key, void (*cmd)(void); };
struct lookup LookupTable[] = {
{"String 1", String1Cmd},
{"String 2", String2Cmd},
{"String 3", String3Cmd},
...
{NULL, BadCmd}
};
void (*lookup(const char *str))(void)
{
size_t i = 0;
while(LookupTable[i].key != NULL)
if (strcmp(str, LookupTable[i].key) == 0)
return LookupTable[i].cmd;
else
i++;
return BadCmd;
}
...
void (*f)(void) = lookup(str); // retrieve the function for the given string
f(); // execute the function
In the last example, if str == "String 1", then String1Cmd will be executed. If str is a string not found in the lookup table, then BadCmd will be executed. This method is very flexible, and depending on your design, allows you to add behavior at runtime (sort of a plug-in architecture).
However, note that we've just deferred the main problem - branching on a string value - to the lookup function, and that the lookup function is back to just doing strcmp against each value in the table. We could speed that part of the process up by using a hash table or a tree to minimize the number of comparisons. Depending on how many strings you're branching on, that may or may not be worth the additional effort.
No, you have to do it yourself. There are many variants:
if (strcmp(str, "toto") == 0)
{
// ...
}
else if (strcmp(str, "tata") == 0)
{
// ...
}
else
{
// ...
}
If the number of strings is expected to grow, then a dispatch table with function pointers
struct dispatch_entry
{
const char *key;
void (*action)(void);
};
// Make sure it is sorted !
dispatch_entry dispatch_table[] =
{
{ "tata", &action_tata },
{ "toto", &action_toto },
};
coupled with binary search:
int dispatch_compare(const void *x, const void *y)
{
const dispatch_entry *xx = x, *yy = y;
return strcmp(xx->key, yy->key);
}
// Return -1 on failure
int dispatch(const char *str)
{
static const size = sizeof(struct dispatch_entry);
static const n = sizeof(dispatch_table) / size ;
dispatch_entry tmp = { str, NULL };
dispatch_entry *what = bsearch(tmp, dispatch_table, n, size, &dispatch_compare);
if (what == NULL) return -1;
(*what->action)();
return 0;
}
will do. Hash table based approaches are OK as well.
if you have the function lfind in your lib (POSIX or gcc) you can use it like:
enum { NOTFOUND, HELLO, WORLD, FOO, BAR };
char list[][100]={"hello","world","foo","bar"};
size_t r, siz = sizeof*list, num = sizeof list/siz;
char *tosearch = "foo";
switch ( (r=lfind(tosearch,list,&num,siz,strcmp))?
(r+siz-(size_t)list)/siz:0 ) {
case HELLO: puts("hello");break;
case WORLD: puts("world");break;
case FOO: puts("foo"); break;
case BAR: puts("bar"); break;
case NOTFOUND:puts("not found");
}
each string in the array must have the same size and should not be a pointer
a hashtable if you have a large number of strings and speed is an issue
No, since the switch may only be used with integral types, or a type convertible to an integral type
No, switch works on an integer value (I think floats/doubles are not even allowed). You can emulate that with if/else if/else doing strcmp.
if (strcmp(mystr, "this") == 0) {
//mystr == "this"
}
else if (strcmp(mystr, "that") == 0) {
//mystr == "that"
}
else {
//mystr is not "this" or "that"
}
Yes, and the way is - long if-else-if statement. (for reference: Why switch statement cannot be applied on strings? )
And what do you mean by "a C construct at all in C" o.O ? I'll edit my post, when you answer :)
Sure, depending on how much work you are willing to do.
You can use a preprocessor and some macros to map strings to integral identifiers, giving you a syntax like:
switch (SOSID_LOOKUP (sample_string)) {
case SOSID (hello): printf ("Hello "); break;
case SOSID (world): printf ("World! "); break;
case 0: default: printf ("unknown "); break;
}
If you can use C++ instead of C, you can use litb's template-based string switcher, giving you syntax like:
sswitch(s) {
scase("foo"): {
std::cout << "s is foo" << std::endl;
break; // could fall-through if we wanted
}
// supports brace-less style too
scase("bar"):
std::cout << "s is bar" << std::endl;
break;
// default must be at the end
sdefault():
std::cout << "neither of those!" << std::endl;
break;
}

Resources