1

I have a function that runs other functions, and these functions can have a variable number of arguments.

The parameter of this function is then passed to makecontext, which attaches the function to a ucontext_t structure, but the problem is this function also takes variable number of arguments.

So my question is, how do I pass ellipsis argument obtained from a function to makecontext given that I can't change makecontext?

void run_function(void (*func), int argc, ...) {
    va_list vl;
    va_start(vl, argc);
    
    makecontext(&ucontext, argc, ...);
    va_end(vl);
}
Rachid K.
  • 4,490
  • 3
  • 11
  • 30
doctopus
  • 5,349
  • 8
  • 53
  • 105
  • 3
    Short answer is you can't. That's why variadic functions in C often have versions which take a `va_list` (*e.g.*, `printf`/`vprintf`). Longer answer is it's possible, but you'll need to use something like [libffi](https://sourceware.org/libffi/). This is a comment not an answer because I'm too lazy to explain how. – nemequ Apr 20 '19 at 02:04
  • Another possibility is using a variadic macro instead of a function for `run_function`. For compilers which support statement expressions (basically most non-Microsoft compilers) that's probably pretty feasible… – nemequ Apr 20 '19 at 02:06
  • i figured so. just wanted to clarify. thanks! – doctopus Apr 20 '19 at 02:35

1 Answers1

2

As pointed out in the comments by @nemequ, with a compiler like gcc, it is possible to use a variadic macro. But

  • The va_start() macro needs at least one parameter before the variable number of parameters: the last parameter is the name of the last argument before the variable argument list;
  • The function func() passed to makecontext() is passed the variable number of parameters but not the argc parameter when it is called.

Hence, the macro wrapper forces argc as first parameter for func() to make it use va_start():

#include <ucontext.h>
#include <stdio.h>
#include <stdarg.h>

#define run_function(ucp, func, argc, ...) \
           makecontext(ucp, func, (argc) + 1, argc, ##__VA_ARGS__)

typedef void (* ucfunc_t)(void);

void func1(int argc, ...)
{
  va_list ap;
  int param;
  int i;

  va_start(ap, argc);

  printf("func1() running with %d parameters:\n", argc);
  for (i = 0; i < argc; i ++) {
    param = va_arg(ap, int);
    printf("\t%d\n", param);
  }

  va_end(ap);
}

int main(void)
{
  ucontext_t main_uc, uc1, uc2, uc3;
  char stack1[4096], stack2[4096], stack3[4096];

  getcontext(&uc1);
  uc1.uc_link = &main_uc;
  uc1.uc_stack.ss_sp = stack1;
  uc1.uc_stack.ss_size = sizeof(stack1);

  getcontext(&uc2);
  uc2.uc_link = &uc1;
  uc2.uc_stack.ss_sp = stack2;
  uc2.uc_stack.ss_size = sizeof(stack2);

  getcontext(&uc3);
  uc3.uc_link = &uc2;
  uc3.uc_stack.ss_sp = stack3;
  uc3.uc_stack.ss_size = sizeof(stack3);

  run_function(&uc1, (ucfunc_t)func1, 0);
  run_function(&uc2, (ucfunc_t)func1, 5, 1, 2, 3, 4, 5);
  run_function(&uc3, (ucfunc_t)func1, 2, 1, 2);

  getcontext(&main_uc);
  main_uc.uc_link = NULL;

  swapcontext(&main_uc, &uc3);

  return 0;
}

The previous example gives:

$ gcc ex1.c -o ex1
$ ./ex1 
func1() running with 2 parameters:
    1
    2
func1() running with 5 parameters:
    1
    2
    3
    4
    5
func1() running with 0 parameters:
Rachid K.
  • 4,490
  • 3
  • 11
  • 30