Well, there is a real hardcore trick that exploits the fact that in C every function is a pointer and you can cast a pointer to any other pointer. The original code, where I got this from, was written, when compilers didn t gave errors on implicit casts, so it took me a while to figure out that I had to cast the functions. What it does is that it casts the callback function to a function with a variable number of arguments. But at the same time, the invocation function is cast to a function with 10 arguments, of which not all will be supplied. Especially this last step seems tricky, but you ve seen it before, where you give the wrong number of arguments to printf and it just compiles. It might even be that this is what va_start/va_end does under the hood. The code is actually for doing a custom operation on any element in the database, but it could be used for your situation as well:
#include <stdio.h>
typedef int (*INTFUNC)(int,...);
typedef int (*MAPFUNCTION)(int [], INTFUNC, ...);
//------------------CALLBACK FUNCTION----------------
static int callbackfunction(int DatabaseRecord,int myArgument,int *MyResult){
if(DatabaseRecord < myArgument){
printf("mapfunction record:%d<%d -> result %d+%d=%d
",DatabaseRecord,myArgument,*MyResult,DatabaseRecord,*MyResult+DatabaseRecord);
*MyResult+=DatabaseRecord;}
else{
printf("mapfunction record:%d<%d not true
",DatabaseRecord,myArgument);
}
return 0; // keep looping
}
//------------------INVOCATION FUNCTION---------------
static int MapDatabase(int DataBase[], INTFUNC func, void* a1, void* a2, void* a3, void* a4, void* a5, void* a6, void* a7, void* a8, void* a9)
{
int cnt,end;
int ret = 0;
end = DataBase[0]+1;
for(cnt = 1;cnt<end;++cnt){
if(func(DataBase[cnt], a1, a2, a3, a4, a5, a6, a7, a8, a9)) {
ret = DataBase[cnt];
break;
}
}
return ret;
}
//------------------TEST----------------
void TestDataBase3(void)
{
int DataBase[20];
int cnt;
int RecordMatch;
int Result = 0;
DataBase[0] = 19;
for(cnt = 1;cnt<20;++cnt){
DataBase[cnt] = cnt;}
// here I do the cast to MAPFUNCTION and INTFUNC
RecordMatch = ((MAPFUNCTION)MapDatabase)(DataBase,(INTFUNC)callbackfunction,11,&Result);
printf("TestDataBase3 Result=%d
",Result);
}
The same functionality can perfectly be written by using va_start/va_end. It might be the more official way of doing things, but I find it less user friendly. Either the callbackfunction needs to decode its arguments or you need to write a switch/case block inside the invocation function for every combination of arguments that the callback function can have. This means that you have to supply the format of the arguments (just like printf does) or you have to require that all arguments are the same and you just supply the number of arguments, but then you still have to write a case for each amount of arguments. Here is an example where the callback function decodes the arguments:
#include <stdio.h>
#include <stdarg.h>
//------------------CALLBACK FUNCTION----------------
static int callbackfunction(int DatabaseRecord,va_list vargs)
{
int myArgument = va_arg(vargs, int); // The callbackfunction is responsible for knowing the argument types
int *MyResult = va_arg(vargs, int*);
if(DatabaseRecord < myArgument){
printf("mapfunction record:%d<%d -> result %d+%d=%d
",DatabaseRecord,myArgument,*MyResult,DatabaseRecord,*MyResult+DatabaseRecord);
*MyResult+=DatabaseRecord;}
else{
printf("mapfunction record:%d<%d not true
",DatabaseRecord,myArgument);
}
return 0; // keep looping
}
//------------------INVOCATION FUNCTION---------------
static int MapDatabase(int DataBase[], int (*func)(int,va_list), int numargs, ...)
{
int cnt,end;
int ret = 0;
va_list vargs;
end = DataBase[0]+1;
for(cnt = 1;cnt<end;++cnt){
va_start( vargs, numargs ); // needs to be called from within the loop, because va_arg can t be reset
if(func(DataBase[cnt], vargs)) {
ret = DataBase[cnt];
break;
}
va_end( vargs ); // avoid memory leaks, call va_end
}
return ret;
}
//------------------TEST----------------
void TestDataBase4(void)
{
int DataBase[20];
int cnt;
int RecordMatch;
int Result = 0;
DataBase[0] = 19;
for(cnt = 1;cnt<20;++cnt){
DataBase[cnt] = cnt;}
RecordMatch = MapDatabase(DataBase,callbackfunction,2,11,&Result);
printf("TestDataBase4a Result=%d
",Result);
Result = 0;
RecordMatch = MapDatabase(DataBase,callbackfunction,0,11,&Result); // As a hack: It even works if you don t supply the number of arguments.
printf("TestDataBase4b Result=%d
",Result);
}