Cristofor
Cristofor

Reputation: 2097

Function pointer casting

Hello guys I'm currently facing a pretty annoying problem:

I'm writing a system of callback manager that works by using function pointers, now given the fact that far now I've been assigned void function pointers to some other non-void function pointers, I've realized that it's not actually allowed in C.

At the moment I'm thinking about casting the void pointer so that matches the prototype function I have for my each callback, but I cannot understand how to implement the casting.

Would it be viable or should I adopt another solution?

Currently these are my callback containers and their typedefs:

typedef void modbus_read_t(unsigned int uid, unsigned int address, unsigned int count, char* response);
typedef void modbus_write_t(unsigned int uid, unsigned int address, unsigned int *data, char* response);

typedef void system_read_t(unsigned int uid, unsigned int var_number, unsigned int count, char* response);
typedef void system_write_t(unsigned int uid, unsigned int var_number, struttura_system *storage, char* response );

void (*M_callbacks_r[3]) (unsigned int uid, unsigned int address, unsigned int count, char* response);
void (*M_callbacks_w[2]) (unsigned int uid, unsigned int address, unsigned int *data, char* response);
void (*P_callbacks_r[2]) (unsigned int uid, unsigned int var_number, unsigned int count, char* response);
void (*P_callbacks_w[2]) (unsigned int uid, unsigned int var_number, struttura_system *storage, char* response );

This is the prototype of the function that registers the callback:

void registerCallback(int systemType,int operationType, void (callback)(void*));

What has to be done is to cast void (callback)(void*) to one of the typedefs above.

Upvotes: 2

Views: 1356

Answers (1)

Well, casting should work. The C standard allows casting between function pointer types, the only thing you shouldn't do is call a function by a function pointer that has the wrong prototype, i.e. cast back to the correct prototype before the call, in order to avoid undefined behavior.

Let's start by simplifying your arrays a little, for readability sake:

modbus_read_t*  M_callbacks_r[2];
modbus_write_t* M_callbacks_w[2];
system_read_t*  P_callbacks_r[2];
system_write_t* P_callbacks_w[2];

I don't know why you typedefed the function type instead of the function pointer type, but I'll go along with it.

Now registerCallback can be implemented as follows:

void registerCallback(int systemType,int operationType, void (*callback)()) {
  // .. determine array by system type and operation type
  M_callbacks_r[0] = (modbus_read_t*)callback;
}

The reason I deleted the parameter from the callback prototype, is that in standard C, an empty parameter list conveys no information about the expected parameters (as opposed to C++ where it explicitly means "no parameters"). That way callbacks can be registered without requiring a cast.


As you mentioned in the comments, you are building with -Wstrict-prototypes -Werror. So an empty parameter list will not work for you. You are thus restricted to defining registerCallback like this:

void registerCallback(int systemType,int operationType, void (*callback)(void))

Note the explicit void in the parameter list. You will need to cast the callback when passing it to registerCallback as well as casting it back inside the function itself.

registerCallback(type, op, (void(*)(void))func_cb);

Side note: The _t postfix is reserved on POSIX systems. To be portable, it may be best not to name your types with it.

Upvotes: 2

Related Questions