1

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?

4
  • It would seem like you're going to need to pass in all the variables that one of the sets of functions that might get called would need and then just ignore the useless ones in the functions that don't need them. Commented Jan 15, 2014 at 21:30
  • 2
    Make sure you really really need to determine the order of the functions at runtime. I get the feeling that this could easily become a case of thedailywtf.com/Articles/Soft_Coding.aspx Commented Jan 15, 2014 at 21:32
  • You could use an array to store your function parameters in, and pass it to the functions to be called. This means that the functions have to be of the same type, casting may be possible though. Commented Jan 15, 2014 at 21:38
  • This looks like an overkill; 20 methods that need to be executed in a sequence and that sequence can vary! That's just too many combinations and if a piece of software does that, then it needs to be revisited. If there are only a few distinct sequences, I would call such methods one by one rather than using if-then-else. Software complexity goes too high with branching. Commented Jan 15, 2014 at 22:04

3 Answers 3

3

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*

Sign up to request clarification or add additional context in comments.

Comments

1

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.

Comments

0

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;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.