Reputation: 337
I am attempting to create a linked list that will keep track of which order I want to use certain functions. I've have around 20 functions that all execute in a couple areas of my code, but the order in which they execute are dynamic, so I was looking at creating a list where I insert which function would be executed at a certain time to clean up the code to only have one area for all the if checks, and another area to do the functions. This makes it look efficient and easy to use. Problem I have is when I want to pass in variables. Take a look at the pseudo code in C...
void func1() { ... }
void func2() { ... }
void func3(x,y) {...}
void func4(z) {...}
void func5() {...}
// Do some If checks to determine order
addFuncToList( func3 );
addFuncToList( func5 );
addFuncToList( func1 );
while(condition) {
x++;
y--;
execute_funcs( currentNode );
currentNode = myList->next;
}
// Do some If checks to determine order
addFuncToList( func1 );
addFuncToList( func5 );
addFuncToList( func2 );
while(condition2) {
execute_funcs( currentNode );
currentNode = myList->next;
}
void execute_funcs( currentNode ) {
if( currentNode == 1 ) func1();
if( currentNode == 2 ) func2();
if( currentNode == 3 ) func3();
...
}
So I like this approach, but I don't want to make a bunch of global variables, I'd like to be able to pass in variables into most of these functions. Most functions need no variables passed in but some need different types passed in. Any ideas?
Upvotes: 1
Views: 107
Reputation: 9618
You just need to hide the fact that the functions have different arity. If you can store the arity along with the function pointer then the exec function will know how to apply the parameters. Here I have assumed that all the parameters are the same - if that isn't true a bit more work needs to be done. The advantage of this method is that you don't have to change the destination functions at all.
#include <stdlib.h> /* malloc, free */
#include <stdarg.h> /* va_list, va_start, va_arg, va_end */
typedef struct {
int (*func)();
int arity;
int params[0];
} node;
node *construct(int (*func)(), int arity) {
node *n = malloc(sizeof(node) + sizeof(int) * arity);
n->func = func;
n->arity = arity;
return n;
}
void assign(node *n, ...) {
int i=0;
va_list va;
va_start(va,n);
for(; i<n->arity; i++) n->params[i] = va_arg(va,int);
va_end(va);
}
int exec(node *n) {
switch(n->arity) {
case 3: return n->func(n->params[0],n->params[1],n->params[2]);
case 2: return n->func(n->params[0],n->params[1]);
case 1: return n->func(n->params[0]);
default: return n->func();
}
}
void destruct(node *n) {
free(n);
}
int sum(int a, int b, int c) { return a+b+c; }
int add(int a, int b) { return a+b; }
int ret(int i) { return i; }
int one() { return 1; }
int main() {
int result;
/* step 0: construct the nodes */
node *n3 = construct(/* func = */ sum,/* params = */ 3);
node *n2 = construct(/* func = */ add,/* params = */ 2);
node *n1 = construct(/* func = */ ret,/* params = */ 1);
node *n0 = construct(/* func = */ one,/* params = */ 0);
/* step 1: assign the values */
assign(n3,/* args */ 3,4,5);
assign(n2,/* args */ 3,4);
assign(n1,/* args */ 3);
/* step 2: execute the functions */
result = exec(n3)+exec(n2)+exec(n1)+exec(n0);
/* step 3: destruct the nodes */
destruct(n3);
destruct(n2);
destruct(n1);
destruct(n0);
return result;
}
Upvotes: 0
Reputation: 259
It's scary, but there are really only two ways to do this. You can pass every conceivable argument to every function, and have each one only use the arguments it needs. This is ugly and terrible.
Alternatively, you can have every function take a void*, and it can cast it as needed. For example:
void func1(void*) { ... }
void func2(void*) { ... }
void func3(void* p) {x = **((int**)p); y = **((int**)(p+1)); ...}
void func4(void* p) {z = *((int*)p); ...}
void func5(void*) {...}
//For the linked list, store a pointer to the function, and a pointer to the parameter.
struct Node {
void (func*)(void*);
void* p;
Node* next;
} Node;
// Do some If checks to determine order
//Use an array to store the two ints for func3. You could also use a struct.
int* pForFunc3[2] = {&x, &y};
int z;
//Func three gets passed an array with pointers to x and y, so it can use them.
addFuncToList( func3 , &pForFunc3);
addFuncToList( func5 , 0);
addFuncToList( func1 , 0);
while(condition) {
x++;
y--;
currentNode->func(currentNode->p);
currentNode = currentNode->next;
}
// Do some If checks to determine order
addFuncToList( func1 );
addFuncToList( func5 );
addFuncToList( func2 );
while(condition2) {
currentNode->func(currentNode->p);
currentNode = currentNode->next;
}
This method involves a lot of scary void* casting, so you need to be certain what you're doing. But, it's far more general than passing every parameter to every function, and it avoids global variables.
Upvotes: 1
Reputation: 17455
Looks like you need a concept of "context" created upon adding a function to the list.
Thus your functions prototypes become looking like this:
int func1(void* context); ... int funcN(void* context);
and the list should contain both function address and its context. In most cases context will be NULL, but when a function needs, it can be a structure, an array of data and so on. Only a caller and a particular function knows exact meaning of that void*
Upvotes: 3