将va_list转换为固定参数

时间:2011-04-09 10:01:09

标签: c variadic-functions

我正在编写原型的线程产生函数:

void Thread_create(void (*func)(void*), int argc, ...);

我已经通过了参数计数,因此确定长度没有问题。 问题是如何将func重新赋值为具有argc长度的函数,然后使用我拥有的参数调用它?

编辑:我也限制了函数只接受void *参数(即不需要担心传入任何其他类型)

例如:

void foo(void *bar, void *baz);
void fooTwo(void *bar, void *baz, void *bam);
int main(int argc, char *argv[]){
    Thread_create(&foo, 2, (void*)argv[0], (void*)argv[1]); //foo gets called in a new thread with the arguments: argv[0] and argv[1]
    Thread_create(&fooTwo, 3, (void*)argv[0], (void*)argv[1], (void*)argv[2]); //fooTwo gets called in a new thread with the arguments: argv[0] and argv[1] and argv[2]
    return 0;
}

附注:表格

的解决方案
Thread_create(void (*func)(void*), int argc, ...); //call with 1 arg
Thread_create(void (*func)(void*, void*), int argc, ...); //call with 2 args
Thread_create(void (*func)(void*, void*, void*), int argc, ...); //call with 3 args

不起作用,因为我无法通过线程创建库调用传递该信息,无论是pthread_create还是windows ThreadCreate函数。

2 个答案:

答案 0 :(得分:2)

嗯,除非您了解相关平台的CPU架构和ABI并自行编码,否则您无法以编程方式自行构建堆栈帧。

例如,对于x86 32位的通用调用约定,您需要以相反的顺序将参数传递给堆栈(例如,最后一个参数优先),然后通过调用调用该函数,并且一旦它通过弹出返回清理堆栈值(或通过调整堆栈指针)。

所以对于一个函数" foo(int bar,int baz)"你会:

pushl <value-for-baz>
pushl <value-for-bar>
call  foo
addl  8, $esp

也许你也可以用C编写代码,但是从C中搞乱堆栈肯定需要一些装配魔法。

此外,使用不同的编译器标志时,堆栈帧甚至可能看起来不同。 x86_64首先将参数放在寄存器中,并且只有当参数多于寄存器时才使用堆栈。

简而言之,不要这样做。 :)

唯一的选择是创建一个大的条件,其中每个分支将使用所需数量的参数调用正确的函数,或者按照您的建议调用重载函数,或使用数据结构传递此信息,如其他答案。

Thread_create(void (*func)(void), int argc, ...)
{
  if (argc == 1)
     ((void (*)(void *)) func)(arg0);
  else if (argc == 2)
     ((void (*)(void *, void *)) func)(arg0, arg1); 
}

答案 1 :(得分:1)

您要做的不是变量参数的用途。最好只需要一个传递回用户函数的aditional void*参数。这是实现用户数据回调的常用方法。

typedef void (*ThreadMainFunc)(void*);

void ThreadCreate(ThreadMainFunc func, void* user_data){
  // do whatever you need to do
  func(user_data);
  // whatever else
}

struct ThreadData{
  // arguments here as member
  int arg1;
  double arg2;
};

void MyThreadMain(void* my_data){
  ThreadData* my_real_data =(ThreadData*)my_data;
  // use my_real_data here
}

int main(){
  ThreadData data;
  data.arg1 = 42;
  data.arg2 = 13.37;
  CreateThread(&MyThreadMain,&data);
//                           ^^^^^ --- you don't need to cast to void, only from void
}