Computing enum "string" from the value - c

So I have the following enum and I have some states in each of which I want to print the STATUS instance based on event_id. So is there a way to compute the enum "string" from its value?
typedef enum {
WIFI_START,
WIFI_DIS,
WIFI_CON
} STATUS;
void eventHandler(int event_id)
{
if (event_id == WIFI_START) {
// ...
printf ("WIFI_START is triggered");
}
...
}

The least-clever approach is to allocate some string constants that line up with the enumeration and index into it using the enumeration:
#include <stdio.h>
typedef enum {
WIFI_START,
WIFI_DIS,
WIFI_CON
} STATUS;
const char *status[] = {"WIFI_START", "WIFI_DIS", "WIFI_CON"};
void eventHandler(int event_id) {
printf("%s is triggered", status[event_id]);
}
int main() {
eventHandler(WIFI_DIS);
return 0;
}
I prefer this over a macro because, debugging/development utilities aside, program logic should be decoupled from the names used by the program to represent data. In other words, the program shouldn't behave differently based on variable name characteristics; it should be possible to minify, obfuscate or refactor the source code and be guaranteed you'll still get the same output.

Related

using function names as functions in a C macro

Suppose i have code like this in my program:
if (!strcmp(current, "sin")) {
pushFloat(sin(x), &operands);
} else if (!strcmp(current, "cos")) {
pushFloat(cos(x), &operands);
} else if (!strcmp(current, "tan")) {
pushFloat(tan(x), &operands);
} else if (!strcmp(current, "ctg")) {
pushFloat(1. / tan(x), &operands);
} else if (!strcmp(current, "ln")) {
pushFloat(log(x), &operands);
} else if (!strcmp(current, "sqrt")) {
pushFloat(sqrt(x), &operands);
}
There are function names such as "sin" or "cos" saved in the current char array
Instead of using this long if block, or replacing it with an even longer switch block, i wanted to write a simple macro like this: #define PUSHFUNC(stack, func, value)(pushFloat(func(value), &stack)) and call it like this PUSHFUNC(operands, current, x)
Doing it this way creates an error "current is not a function or function pointer". I initially thought macros are just text replacement, so if i force a string that is equal to an actual function into a macro, it would expand to the function itself, but looks like i was wrong. Is there a way to achieve what i want using a macro, or should i just write a map block?
I initially thought macros are just text replacement,
That's your problem: macros are just text replacement. So if you have:
#define PUSHFUNC(stack, func, value) (pushFloat(func(value), &stack))
And you write:
PUSHFUNC(operands, current, x)
You get:
(pushFloat(current(value), &operands))
And indeed, you have no function named current. Macros are expanded before your code compiles; the preprocessor has no knowledge of the content of your variables.
If you really want to avoid a long chain of if statements, you could implement some sort of table lookup:
#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <math.h>
typedef double (*floatop)(double x);
typedef struct {
char *name;
floatop operation;
} entry;
double ctg(double);
entry opertable[] = {
{"sin", sin},
{"cos", cos},
{"tan", tan},
{"ctg", ctg},
{"sqrt", sqrt},
{NULL, NULL},
};
double ctg(double x) {
return 1. / tan(x);
}
floatop findop(char *name) {
int i;
for (i=0; opertable[i].name; i++) {
if (strcmp(opertable[i].name, name) == 0) {
return opertable[i].operation;
}
}
}
int main() {
float x = 4;
printf("sin(%f) = %f\n", x, findop("sin")(x));
printf("sqrt(%f) = %f\n", x, findop("sqrt")(x));
printf("tan(%f) = %f\n", x, findop("tan")(x));
printf("ctg(%f) = %f\n", x, findop("ctg")(x));
}
...but this requires that all of your functions take the same arguments, so for things like ctg you would need to add a helper function. You also need to decide if the increased complexity of the table lookup makes sense: it really depends on how many different operation names you expect to implement.
The output of the above code is:
sin(4.000000) = -0.756802
sqrt(4.000000) = 2.000000
tan(4.000000) = 1.157821
ctg(4.000000) = 0.863691
Is there a way to achieve what i want using a macro, or should i just write a map block?
I would recommend using an enum containing symbols for all the functions you might want to call, and using that in a switch-case block, instead of comparing a bunch of strings. Here's a very brief sample that only uses some of the functions you refer to...
enum which_func { SIN, COS, TAN, };
enum which_func which = SIN;
switch (which) {
case SIN:
pushFloat(sin(x), &operands);
break;
case COS:
pushFloat(cos(x), &operands);
break;
case TAN:
pushFloat(tan(x), &operands);
break;
default:
assert(false); // shouldn't be reachable if enum value is well-defined
}
This version will be easier to maintain in the long run, more efficient to execute and possibly more robust to logic errors (there are some compiler warnings that you can enable which will warn you if you're not handling all enum values, which can help you catch missed cases in your logic).
To add to what other answers said, what you can do is to make a macro that expands to the "basic block" of your if chain, avoiding some repetitions thanks to the stringizing operator:
#define HANDLE_FN_EXPR(fn, expr) \
else if(!strcmp(current, #fn)) \
pushFloat((expr), &operands)
#define HANDLE_FN(fn) \
HANDLE_FN_EXPR(fn, fn(x))
Then you can do
if(0);
HANDLE_FN(sin);
HANDLE_FN(cos);
HANDLE_FN(tan);
HANDLE_FN_EXPR(ctg, 1./tan(x));
HANDLE_FN(ln);
HANDLE_FN(sqrt);
Macros do in fact do text replacement. Given your macro definition, this:
PUSHFUNC(operands, current, x)
expands to this:
(pushFloat(current(x), &operands))
So as you can see, the text that is being replaced is the name of the variable, not the text that it contains.
And even if this did work as you expected, it wouldn't be able to properly handle the 1. / tan(x) case.
This means there isn't really a better way to do what you want.
Why not create some objects for each function type? I know, this is C not C++, but the idea will still work. First, create the function object type:-
typedef struct _Function
{
char *name;
float (*function) (float argument);
} Function;arg
And now create an array of function objects:-
Function functions [] =
{
{ "sin", sin },
{ "cos", cos }
// and so on
};
where the functions are defined:-
float sin(float x)
{
return 0; // put correct code here
}
float cos(float x)
{
return 0; // put correct code here
}
Finally, parse the input:-
for (int i = 0; i < sizeof functions / sizeof functions[0]; ++i)
{
if (strcmp(functions[i].name, current) == 0)
{
pushFloat(functions[i].function(arg)); // add operands!
break;
}
}
I find using enums for stuff like this very hard to maintain! Adding new functions means going through the code to find cases where the enum is used and updating it prone to errors (like missing a place!).
All because it's not C++, doesn't mean you can't use objects! It's just there's no language support for it so you have to do a bit more work (and, yeah, there are features missing!)

Using a switch to map function pointers to strings

I'm working on a network service that based on commands it receives over the network, it has workers perform different jobs. I want to have a log entry for every time a certain worker is tasked with doing some job.
I have a function (say function_caller) which, among other things, calls another function which it receives its pointer as an argument. I'd like to have my logger notify what kind of function function_caller calls.
Originally I wanted the function_caller to receive some enum instead of a function pointer, provide the enum to the logger, and then use a helper function which returns a suitable pointer based on the enum. However, function_caller is already deeply tangled in the codebase I'm working on, and it looks like it would be a lot of work to refactor all the functions that call function_caller to choose the right enum and use a new argument.
So my next idea was having a switch that for every function pointer will have some string representation of, but I've never stumbled upon something like that (and struggled to find anyone even mentioning such an idea on Google), so I have a feeling I might be missing some serious downsides to this option.
The only significant problem I see is that every developer that decides to pass a new kind of function pointer to function_caller will have to somehow know to update the switch, otherwise it will fail.
Am I missing anything else? Or maybe there's some other approach I should consider?
How about something like this? Instead of a switch, store a table of functions and their name strings. The table can even be kept dynamically updated, unlike a switch case. You will not need to walk along the edge of the standard as well!
#include <stdio.h>
typedef void (*callback_t) (void);
void first (void) { printf("%d", 1); };
void second (void) { printf("%d", 2); };
void third (void) { printf("%d", 3); };
typedef struct fntable_t
{
callback_t fn;
char *name;
} fntable_t;
fntable_t fntable[] =
{
{ first, "first" },
{ second, "second" },
{ third, "third" }
};
char* log_str(callback_t c)
{
for(int i = 0; i < sizeof(fntable) / sizeof(fntable_t); i++)
{
if(fntable[i].fn == c)
return fntable[i].name;
}
return "unknown";
}
void function_caller(callback_t c)
{
printf("%s",log_str(c));
c();
}
int main(void)
{
function_caller(first);
function_caller(second);
function_caller(third);
return 0;
}
You could replace function_caller with a wrapper macro of the same name that calls the renamed function function_caller_internal which gets an additional string argument. The wrapper macro can then pass an additional stringified function name.
This works only if function_caller is always called with a function name, not a function pointer variable.
Example:
#include <stdio.h>
static void funcA(void)
{
printf("This is funcA\n");
}
static void funcB(void)
{
printf("This is funcB\n");
}
/* renamed function gets an additional string argument */
static void function_caller_internal(void (*func)(void), const char *name)
{
printf("calling %s\n", name);
func();
}
/* wrapper macro stringifies the function name to pass it the additional argument */
#define function_caller(func) function_caller_internal(func, #func)
int main(void)
{
/* unchanged calls */
function_caller(funcA);
function_caller(funcB);
return 0;
}
This prints
calling funcA
This is funcA
calling funcB
This is funcB
If you can change the API of the functions, then consider using __func__ to get the textual name of each function. If you can have a function pointer type along the lines of this:
typedef void func_t (const char** name);
Then you can have each function return its name to the caller.
void foo (const char** name)
{
/* do foo stuff here */
*name = __func__;
}
void bar (const char** name)
{
/* do bar stuff here */
*name = __func__;
}
Example:
#include <stdio.h>
typedef void func_t (const char** name);
void foo (const char** name)
{
/* do foo stuff here */
*name = __func__;
}
void bar (const char** name)
{
/* do bar stuff here */
*name = __func__;
}
const char* function_caller (func_t* func, const char** name)
{
func(name);
return *name;
}
int main(void)
{
static func_t*const func [] =
{
foo,
bar,
};
const char* name;
for(size_t i=0; i<sizeof func/sizeof *func; i++)
{
puts( function_caller(func[i], &name) );
}
}
Assuming your codebase has sane variable names and function names, you can add a char * argument to your function caller:
void function_caller(char *name, int fpnt());
and then provide a macro:
#define function_caller_autoname(fpnt) function_caller(#fpnt, fpnt)
(Or, for spaghetti code, you can provide a macro with the same name as the function).
The #fpnt will be expanded by the proceprocessor to a string literal with the function name.
Then when your codebase called:
function_caller(some_function)
refactor it to:
function_caller_autoname(some_function)
# will be expanded to by the processor:
# function_caller("some_function", some_function)
or refactor it manually to provide the name/identificator/description of the function:
function_caller("Some function: ", some_function)
That way you can pass a custom string that describes the function along with the pointer. Also, each developer can pass a custom description string.

How to create derived structure properties in C

In Python, its possible to create a derived property from a class using the #property decorator for example
class State():
def __init__(self, fav_num_monday, fav_num_not_monday, is_monday):
self.fav_num_monday = fav_num_monday
self.fav_num_not_monday = fav_num_not_monday
self.is_monday = is_monday
#property
def fav_num(self):
return self.is_monday * self.fav_num_monday + \
(1 - self.is_monday) * self.fav_num_not_monday
state = State(12, 5, 0)
print("Current favourite number: %d" % state.fav_num)
My question is then what is the best way to achieve this in C (where speed is of the utmost importance). I've have added below some ways I have tried but am not sure if they could have repercussions in a larger codebase. They are as follows:
Simply writing out the whole expression each time.
Pros: No unexpected repercussions, no code/speed penalty.
Cons: Ugly code, takes a long time to write.
Using a get function.
Pros: Code easier to read.
Cons: Inefficient code (slower than 1).
Defining a macro.
Pros: No code/speed penalty. Code quick to write.
Cons: Potential repercussions later, code not so easy to follow.
The example program is below
#include <stdio.h>
#include <string.h>
#define state_fav_num state.is_monday * state.fav_num_monday + (1 - state.is_monday) * state.fav_num_not_monday
struct State {
int fav_num_monday;
int fav_num_not_monday;
int is_monday;
};
int get_state(struct State *state, char *property) {
// Returns value of the property in state.
// Allows us to create derived properties also.
if (!strncmp(property, "fav_num_monday", 14)) {
return state->fav_num_monday;
} else if (!strncmp(property, "fav_num_not_monday", 18)) {
return state->fav_num_not_monday;
} else if (!strncmp(property, "is_monday", 9)) {
return state->is_monday;
} else if (!strncmp(property, "fav_num", 7)) {
return state->is_monday * state->fav_num_monday +
(1 - state->is_monday) * state->fav_num_not_monday;
}
}
int main() {
// Set the state.
struct State state;
state.fav_num_monday = 12;
state.fav_num_not_monday = 5;
state.is_monday = 1;
// Print favourite number in different ways.
printf("\n1) Current favourite number is %d.",
state.is_monday * state.fav_num_monday +
(1 - state.is_monday) * state.fav_num_not_monday);
printf("\n2) Current favourite number is %d.",
get_state(&state, "fav_num"));
printf("\n3) Current favourite number is %d.",
state_fav_num);
printf("\n");
return 0;
}
You can get the best of both worlds (function and macro) for readability and performance, with a static inline function.
You usually wouldn't use that, but if you know the compiler is going to optimize its code away, then it's OK to use it. The usual rule I use is 3 or less lines of code, and the function should require extra performance.
That said, your get_state doesn't meet the (my) requirements for a static inline function, but if you only want a function to get only the fav_num, it would make sense:
struct State {
int fav_num_monday;
int fav_num_not_monday;
bool is_monday;
};
static inline int get_fav_num(const struct State *state)
{
if (state->is_monday)
return state->fav_num_monday;
else
return state->fav_num_not_monday;
}
int main(void)
{
struct State state;
int fav_num;
state = (struct State){
.fav_num_monday = 12;
.fav_num_not_monday = 5;
.is_monday = 1;
};
// Print favourite number in different ways.
printf("\n");
if (state.is_monday)
fav_num = state->fav_num_monday;
else
fav_num = state->fav_num_not_monday;
printf("1) Current favourite number is %d.\n", fav_num);
fav_num = get_fav_num(&state);
printf("4) Current favourite number is %d.\n", fav_num);
return 0;
}
Disclaimer: This code needs C99 or later.
Although here the code is all together, the struct State {...}; and the static inline function would usually go in a header .h file.
Also, I would improve your get_state function in this way:
enum Properties {
FAV_NUM_MONDAY,
FAV_NUM_NOT_MONDAY,
IS_MONDAY,
FAV_NUM
};
int get_state(const struct State *state, int property)
{
switch (property) {
case FAV_NUM_MONDAY:
return state->fav_num_monday;
case FAV_NUM_NOT_MONDAY:
return state->fav_num_not_monday;
case IS_MONDAY:
return state->is_monday;
case FAV_NUM:
return get_fav_num(state);
default:
return -1; /* Error */
}
}
This function would be a usual extern function and would go in a .c file, although the enum Properties should go in a header file so that it can be used by the user of the function.
Edit: Add high performance version using array
state.h
#include <stdint.h>
enum State_Properties {
FAV_NUM_MONDAY,
FAV_NUM_NOT_MONDAY,
IS_MONDAY,
STATE_PROPERTIES
};
static inline
uint_fast8_t get_fav_num(const uint_fast8_t *restrict (state[STATE_PROPERTIES]))
{
if ((*state)[IS_MONDAY])
return (*state)[FAV_NUM_MONDAY];
else
return (*state)[FAV_NUM_NOT_MONDAY];
}
main.c
#include <inttypes.h>
#include "state.h"
int main(void)
{
uint_fast8_t state[STATE_PROPERTIES];
uint_fast8_t fav_num;
uint_fast8_t fav_num_monday;
state = (uint_fast8_t [STATE_PROPERTIES]){
[FAV_NUM_MONDAY] = 12;
[FAV_NUM_NOT_MONDAY] = 5;
[IS_MONDAY] = true;
};
// Print favourite number in different ways.
fav_num = get_fav_num(&state);
printf("5) Current favourite number is %"PRIuFAST8".\n", fav_num);
// Example of how to retrieve any property:
fav_num_monday = state[FAV_NUM_MONDAY];
}
Of course you can change the type to anyone you want. I used uint_fast8_t, because your data can fit in there, and it is the fastest type on any system.

Is it possible to call functions from arrays in C? [duplicate]

This question already has answers here:
How can I use an array of function pointers?
(12 answers)
Closed 4 years ago.
When I was making my terminal i was wondering if I can call a function by array.
(This code is not done yet so please code is a bit messy.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <unistd.h>
#include <limits.h>
#define true 1
#define false 0
typedef int bool;
/* Static */
static char Input[CHAR_MAX];
static char CurrentDirectory[CHAR_MAX];
static char *Command;
static char *Argument;
static char *Commands[]={"test","test2"};
/* Functions */
int Check_Command();
int test();
int test2();
/* --------- */
int main(){
printf("#######################\n\tterminal\n\tType \"help\" for the list of commands\n#######################\n");
prompt:
printf(">");
fgets(Input,CHAR_MAX,stdin);
int res=Check_Command();
if(res==0){printf("Unknown Command!\n");}
goto prompt;
}
/* Check_Command() function returns 0 if doesn't suceed and returns 1 of it suceeds */
int Check_Command(){
//Since input variable is static, no need to send in arguments
Input[strcspn(Input,"\r\n")]=0;
Command=strtok(Input," ");
Argument=strtok(NULL," ");
int x=0;
while(x<sizeof(Commands)){
if(strcmp(Command,Commands[x])==0){
Commands[x](); <----- Can I call a function like this?
return 1;
}
x++;
}
return 0;
}
/* Commands */
int test(){
printf("Success!\n");
getchar();
exit(0);
}
int test2(){
print("Success [2] \n");
getchar();
exit(0);
}
If this possible then this would be lit, Im too lazy to make commands into a executable and using if statements for all commands.
if you are too lazy to read the whole code here is a basic concept (UNTESTED):
static *Commands[]={"test","test2"};
int main(){
char *Command="test";
int x=0;
while(x<sizeof(Commands)){
if(strcmp(Command,Commands)==0){
Commands[x]();
}
x++
}
}
int test(){
printf("Hi");
}
int test2(){
printf("hey");
}
Edit:
static char Commands[]={test,test2}; DOES NOT WORK
This also includes the "possible duplicate" answer. (Im using Mingw, Windows 10)
It appears that you want to be able to take in a string such as test2 from the user, and then invoke the function test2(). There are two main ways you can approach this:
Homebrew structure mapping names to function pointers.
Using 'dynamic library loading' and function name resolution.
Array of structures
For the first, you define a structure such as:
struct FuncName
{
const char *name;
int (*function)(void);
};
And you can then define an array of these:
struct FuncName functions[] =
{
{ "test", test },
{ "test2", test2 },
};
enum { NUM_FUNCTIONS = sizeof(functions) / sizeof(functions[0]) };
When you get a name from the user, you can search through the array of names and find the matching function pointer to call.
int invoke_function(const char *name)
{
for (int i = 0; i < NUM_FUNCTIONS; i++)
{
if (strcmp(name, functions[i].name) == 0)
{
return (*functions[i].function)();
// Or just: return functions[i].function();
}
}
return -1; // No match found
}
This works reliably on all systems, but the demerit is that you must create the table of function pointers when you compile the program.
Dynamic library
The alternative is to use functions dlopen() and dlsym() from the <dlsym.h> header on Unix (POSIX) systems, or the equivalent on Windows.
Normally, you expect to find the functions in dynamically loaded libraries loaded with dlopen(), but there's usually a way to search the main executable for the names instead (pass a null pointer as the file name to dlopen() on POSIX systems). You can then call dlsym() to get the function pointer corresponding to the name you specify, which you can call.
void *dlh = dlopen(NULL, RTLD_NOW);
int (*funcptr)(void) = (int (*)(void))dlsym("test", dlh);
return (*funcptr)();
This omits error checking and you need the cast to convert from an object pointer (void *) to a function pointer because the C standard does not require that to be doable, but POSIX does (see the specification of
dlsym() already linked to).
Non-uniform function signatures
With both solutions, life is easy if all the callable functions have the same interface. Life is much messier if the different functions have different interfaces (so some expect no arguments, some expect one, some expect two, and the types of the arguments vary between functions, as do the return types). Expect to use lots of casts and be prepared to bludgeon the compiler into submission — isolate the code from everything else so as to leave the non-portable part well separated from the main code.
Beware: no compiler was consulted about the validity of any of this code!

How do I write a dispatcher, if my compiler's support for pointers-to-functions is broken?

I am working on an embedded application where the device is controlled through a command interface. I mocked the command dispatcher in VC and had it working to my satisfaction; but when I then moved the code over to the embedded environment, I found out that the compiler has a broken implementation of pointer-to-func's.
Here's how I originally implemented the code (in VC):
/* Relevant parts of header file */
typedef struct command {
const char *code;
void *set_dispatcher;
void *get_dispatcher;
const char *_description;
} command_t;
#define COMMAND_ENTRY(label,dispatcher,description) {(const char*)label, &set_##dispatcher, &get_##dispatcher, (const char*)description}
/* Dispatcher data structure in the C file */
const command_t commands[] = {
COMMAND_ENTRY("DH", Dhcp, "DHCP (0=off, 1=on)"),
COMMAND_ENTRY("IP", Ip, "IP Address (192.168.1.205)"),
COMMAND_ENTRY("SM", Subnet, "Subunet Mask (255.255.255.0)"),
COMMAND_ENTRY("DR", DefaultRoute, "Default router (192.168.1.1)"),
COMMAND_ENTRY("UN", Username, "Web username"),
COMMAND_ENTRY("PW", Password, "Web password"),
...
}
/* After matching the received command string to the command "label", the command is dispatched */
if (pc->isGetter)
return ((get_fn_t)(commands[i].get_dispatcher))(pc);
else
return ((set_fn_t)(commands[i].set_dispatcher))(pc);
}
Without the use of function pointers, it seems like my only hope is to use switch()/case statements to call functions. But I'd like to avoid having to manually maintain a large switch() statement.
What I was thinking of doing is moving all the COMMAND_ENTRY lines into a separate include file. Then wraps that include file with varying #define and #undefines. Something like:
/* Create enum's labels */
#define COMMAND_ENTRY(label,dispatcher,description) SET_##dispatcher, GET_##dispatcher
typedef enum command_labels = {
#include "entries.cinc"
DUMMY_ENUM_ENTRY} command_labels_t;
#undefine COMMAND_ENTRY
/* Create command mapping table */
#define COMMAND_ENTRY(label,dispatcher,description) {(const char*)label, SET_##dispatcher, GET_##dispatcher, (const char*)description}
const command_t commands[] = {
#include "entries.cinc"
NULL /* dummy */ };
#undefine COMMAND_ENTRY
/*...*/
int command_dispatcher(command_labels_t dispatcher_id) {
/* Create dispatcher switch statement */
#define COMMAND_ENTRY(label,dispatcher,description) case SET_##dispatcher: return set_##dispatcher(pc); case GET_##dispatcher: return get_##dispatcher(pc);
switch(dispatcher_id) {
#include "entries.cinc"
default:
return NOT_FOUND;
}
#undefine COMMAND_ENTRY
}
Does anyone see a better way to handle this situation? Sadly, 'get another compiler' is not a viable option. :(
--- Edit to add:
Just to clarify, the particular embedded environment is broken in that the compiler is supposed to create a "function-pointer table" which is then used by the compiler to resolve calls to functions through a pointer. Unfortunately, the compiler is broken and doesn't generate a correct function-table.
So I don't have an easy way to extract the func address to invoke it.
--- Edit #2:
Ah, yes, the use of void *(set|get)_dispatcher was my attempt to see if the problem was with the typedefine of the func pointers. Originally, I had
typedef int (*set_fn_t)(cmdContext_t *pCmdCtx);
typedef int (*get_fn_t)(cmdContext_t *pCmdCtx);
typedef struct command {
const char *code;
set_fn_t set_dispatcher;
get_fn_t get_dispatcher;
const char *_description;
} command_t;
You should try changing your struct command so the function pointers have the actual type:
typedef struct command {
const char *code;
set_fn_t set_dispatcher;
get_fn_t get_dispatcher;
const char *_description;
} command_t;
Unfortunately, function pointers are not guaranteed to be able to convert to/from void pointers (that applies only to pointers to objects).
What's the embedded environment?
Given the information posted in the updates to the question, I see that it's really a bugged compiler.
I think that your proposed solution seems pretty reasonable - it's probably similar to what I would have come up with.
A function pointer isn't actually required to fit in a void*. You could check to make sure that the value you're calling is actually the address of the function. If not, use a function pointer type in the struct: either get_fn_t, or IIRC void(*)(void) is guaranteed to be compatible with any function pointer type.
Edit: OK, assuming that calling by value can't be made to work, I can't think of a neater way to do what you need than auto-generating the switch statement. You could maybe use an off-the-shelf ASP-style preprocessor mode for ruby/python/perl/php/whatever prior to the C preprocessor. Something like this:
switch(dispatcher_id) {
<% for c in commands %>
case SET_<% c.dispatcher %>: return set_<% c.dispatcher %>(pc);
case GET_<% c.dispatcher %>: return get_<% c.dispatcher %>(pc);
<% end %>
default:
return NOT_FOUND;
}
might be a bit more readable than the macro/include trick, but introducing a new tool and setting up the makefiles is probably not worth it for such a small amount of code. And the line numbers in the debug info won't relate to the file you think of as the source file unless you do extra work in your preprocessor to specify them.
Can you get the vendor to fix the compiler?
To what extent is the pointer-to-function broken?
If the compiler allows you to get the address of a function (I'm from C++, but &getenv is what I mean), you could wrap the calling convention stuff into assembler.
As said, I'm a C++ssie, but something in the way of
; function call
push [arg1]
push [arg2]
call [command+8] ; at the 4th location, the setter is stored
ret
If even that is broken, you could define an array of extern void* pointers which you define, again, in assembly.
try this syntax:
return (*((get_fn_t)commands[i].get_dispatcher))(pc);
It's been awhile since I've done C & function pointers, but I believe the original C syntax required the * when dereferencing function pointers but most compilers would let you get away without it.
Do you have access to the link map?
If so, maybe you can hack your way around the wonky function-pointer table:
unsigned long addr_get_dhcp = 0x1111111;
unsigned long addr_set_dhcp = 0x2222222; //make these unique numbers.
/* Relevant parts of header file */
typedef struct command {
const char *code;
unsigned long set_dispatcher;
unsigned long get_dispatcher;
const char *_description;
} command_t;
#define COMMAND_ENTRY(label,dispatcher,description) {(const char*)label,
addr_set_##dispatcher, addr_get_##dispatcher, (const char*)description}
Now compile, grab the relevant addresses from the link map, replace the constants, and recompile. Nothing should move, so the map ought to stay the same. (Making the original constants unique should prevent the compiler from collapsing identical values into one storage location. You may need a long long, depending on the architecture)
If the concept works, you could probably add a post-link step running a script to do the replacement automagically. Of course, this is just a theory, it may fail miserably.
Maybe, you need to look into the structure again:
typedef struct command {
const char *code;
void *set_dispatcher; //IMO, it does not look like a function pointer...
void *get_dispatcher; //more like a pointer to void
const char *_description;
} command_t;
Let say your dispatchers have the following similar function definition:
//a function pointer type definition
typedef int (*genericDispatcher)(int data);
Assume that the dispatchers are like below:
int set_DhcpDispatcher(int data) { return data; }
int get_DhcpDispatcher(int data) { return 2*data; }
So, the revised structure will be:
typedef struct command {
const char *code;
genericDispatcher set_dispatcher;
genericDispatcher get_dispatcher;
const char *_description;
} command_t;
Your macro will be:
#define COMMAND_ENTRY(label,dispatcher,description) \
{ (const char*)label, \
set_##dispatcher##Dispatcher, \
get_##dispatcher##Dispatcher, \
(const char*)description }
Then, you can set your array as usual:
int main(int argc, char **argv)
{
int value1 = 0, value2 = 0;
const command_t commands[] = {
COMMAND_ENTRY("DH", Dhcp, "DHCP (0=off, 1=on)")
};
value1 = commands[0].set_dispatcher(1);
value2 = commands[0].get_dispatcher(2);
printf("value1 = %d, value2 = %d", value1, value2);
return 0;
}
Correct me, if I am wrong somewhere... ;)

Resources