Description
Imagine a scenario where you would like to invoke different types of functions dynamically (with different arguments and containing types) in a sequence. Alternatively speaking, let this be address by the use of a function list/table where user is interested to execute a particular function from that table, based on certain run-time conditions.
Implementation
Let us devise first the typedefs of the function definitions going to be used. Here, we assume that the function table is not going to be of very large size in nature, but this scheme has been necessitated due to changing function arguments mainly.
Consider the following three function types and the associated function pointer definitions.
// Typedefs of selected function pointers to be used in the table
typedef void (*LPFUNCTION1)(int, bool);
typedef void (*LPFUNCTION2)(int);
typedef void (*LPFUNCTION3)(void*);
// Forward declaration of the function table structure
typedef struct _tagFuncTable FUNCTABLE;
// Define macros to manipulate the function table at runtime
#define FUNCPTR(id) LPFUNCTION##id
#define DECLARE_FUNCPTR(id, var, table) FUNCPTR(id) var = (FUNCPTR(id))table[id-1].funcPtr;
#define GETFUNC_ENTRY(id, table) ((FUNCTABLE*)&table[id-1]);
#define GETFUNC_ENTRY_PARAMS(id, entry) ((MyFuncParams##id*)&entry->params.f##id);
#define GETFUNC_ENTRY_PARAMS_EX(id, table, params) { \
FUNCTABLE *pTblEntry = GETFUNC_ENTRY(id, table); \
params = GETFUNC_ENTRY_PARAMS(id, pTblEntry); \
}
Now, having defined some macros above, let us translate the function arguments of each interested functions into structure definitions. This method allows us to use different function arguments (variable).
// Function arguments translated to structure packings
typedef struct _tagMyFuncParams1 {
int a;
bool b;
} MyFuncParams1;
typedef struct _tagMyFuncParams2 {
int a;
} MyFuncParams2;
typedef struct _tagMyFuncParams3 {
void* p;
} MyFuncParams3;
struct _tagFuncTable {
void *funcPtr;
unsigned char funcType;
union {
MyFuncParams1 f1; // use if funcType = 0x01
MyFuncParams2 f2; // use if funcType = 0x02
MyFuncParams3 f3; // use if funcType = 0x03
}params;
_tagFuncTable *next;
};
We now define our actual function definition for the application. These functions obey the typedefs of the function pointers defined above at the top.
void MyFunction1(int a, bool b)
{
printf("MyFunction1: a = %d, b = %d\n", a, (short)b);
}
void MyFunction2(int a)
{
printf("MyFunction2: a = %d\n", a);
}
void MyFunction3(void* p)
{
printf("MyFunction3: p = %x\n", p);
}
Following the app-level function definitions, we now set to define our function table or list of function pointers.
// App defined function table (**See supported functions in tyepdefs)
FUNCTABLE FuncArray[3] = {
{(LPFUNCTION1)&MyFunction1, 0x01, {10, false}, NULL},
{(LPFUNCTION2)&MyFunction2, 0x02, {25}, NULL},
{(LPFUNCTION3)&MyFunction3, 0x03, {NULL}, NULL},
};
Finally, we deploy the method in the application in the following way.
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
..
// The only way if function call modifies arguments (expanded)
{
MyFuncParams1 *params = NULL;
ASSERT(FuncArray[0].funcType == 0x01);
DECLARE_FUNCPTR(1, pFunc, FuncArray);
GETFUNC_ENTRY_PARAMS_EX(1, FuncArray, params);
(*pFunc) (params->a, params->b);
}
..
}