How to do generic function pointers?
Are void * pointers meant for generic typing in C? - Stack Overflow
void pointers
Distinguishing types in _Generic
Hi all, I'm working on a little embedded project and am creating a generic api for connected devices. The idea is, each peripheral(a managed device, sensor, component) can have a list of parameters. I've made a parameter struct which I would like to contain a name, variable size, pointer to a 'get' function and pointer to 'set' function.
The problem I have is that I need different types of 'get' and 'set' functions (some parameters may be 8 bit, some floats, etc). In addition to the parameter to get/set, the function also takes a handle to the peripheral. Obviously, the handle type will change between peripheral types, so I need a generic way of casting both the handle and the function type. Essentially, they're all just addresses, right?
Here's what I have so far...
typedef void(*handle_t); /** < TRY: casting generic handle pointer as void pointer
They should both be same size? **/
typedef int (*getParam32)(handle_t, uint32_t *);
typedef int (*getParamFloat)(handle_t, float *);
//etc //
typedef esp_err_t (*setParam8)(handle_t, uint8_t);
typedef esp_err_t (*setParam16)(handle_t, uint16_t);
// etc //
typedef int (*getFunc)(handle_t, void *); // my optimistic idea for generic 'get'
typedef int (*setFunc)(handle_t, uin32_t); // same, assume uint32_t largest varible size
typedef struct parameter
{
char param_name[32]; /** < parameter name **/
uint32_t param_id; /** < parameter unique id **/
getFunc get; /** < get function pointer **/
setFunc set; /** < set function pointer **/
param_type_t valueType; /** < size of parameter in bytes **/
} parameter_t;
// calling the get32, for example... //
if(param.valueType == TYPE_U32) {
uint32_t value;
(getParam32 *)param.get(deviceHandle, &value);
}My thoughts are I could cast the getFunc/setFunc back to their specific types depending on param_type_t (which is just an enum of variable types). However, I'm in pretty deep here and have no idea if this is the right way to do things.
If anyone can offer any advice it'd be much appreciated!
The purpose of a void * is to provide a welcome exception to some of C's typing rules. With the exception of void *, you cannot assign a pointer value of one type to an object of a different pointer type without a cast - for example, you cannot write
int p = 10;
double *q = &p; // BZZT - cannot assign an int * value to a double *
When assigning to pointers of different types, you have to explicitly cast to the target type:
int p = 10;
double *q = (double *) &p; // convert the pointer to p to the right type before assigning to q
except for a void *:
int p = 10;
void *q = &p; // no cast required here.
In the old days of K&R C, char * was used as a "generic" pointer type1 - the memory allocation functions malloc/calloc/realloc all returned char *, the callback functions for qsort and bsearch took char * arguments, etc., but because you couldn't directly assign different pointer types, you had to add an explicit cast (if the target wasn't a char *, anyway):
int *mem = (int *) malloc( N * sizeof *mem );
Using explicit casts everywhere was a bit painful.
The 1989/1990 standard (C89/C90) introduced the void data type - it's a data type that cannot store any values. An expression of type void is evaluated only for its side effects (if any)2. A special rule was created for the void * type such that a value of that type can be assigned to/from any other pointer type without need of an explicit cast, which made it the new "generic" pointer type. malloc/calloc/realloc were all changed to return void *, qsort and bsearch callbacks now take void * arguments instead of char *, and now things are a bit cleaner:
int *mem = malloc( sizeof *mem * N );
You cannot dereference a void * - in our example above, where q has type void *, we cannot get at the value of p without a cast:
printf( "p = %d\n", *(int *)q );
Note that C++ is different in this regard - C++ does not treat void * specially, and requires an explicit cast to assign to different pointer types. That's because C++ provides overloading mechanisms that C doesn't.
- Every object type should be mappable to an array of
char. - In K&R C, all functions had to return a value - if you didn't explicitly type the function, the compiler assumed it returned
int. This made it difficult to determine which functions were actually meant to return a value vs. functions that only had side effects. Thevoidtype was handy for typing functions that weren't meant to return a value.
C suffers from the absence of function overloading. So most C "generic" functions as for example qsort or bsearch use pointers to void * that to be able to deal with objects of different types.
In C you need not to cast a pointer of any type to a pointer of the type void *. And a pointer of any type can be assigned with a pointer of the type void * without casting.
So in C the functions from your code snippet can be rewritten like
void store(struct GenericStruct *strct, int *myarr){
strct->ptr = myarr;
}
int *load(struct GenericStruct *strct){
return strct->ptr;
}
Hey guys, I am getting familiar with different concepts of C++. I came across void pointers. I know that when we use malloc it returns a void pointer and we need to typecast it. What else is a void pointer used for and how can we create generic functions using void pointer?