конвертировать va_list в фиксированные аргументы - PullRequest
0 голосов
/ 09 апреля 2011

Я пишу функцию порождения потока прототипа:

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

Я передал количество аргументов, поэтому нет проблем с определением длины. Проблема в том, как мне переписать func в функцию с длиной argc, а затем вызвать ее, используя мои аргументы?

РЕДАКТИРОВАТЬ: я также ограничил функцию принимать только пустые * аргументы (то есть не нужно беспокоиться о том, чтобы передавался любой другой тип)

Например:

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 ]

2 голосов
/ 09 апреля 2011

Ну, вы не можете создать программно стековый фрейм самостоятельно, если не знаете архитектуру ЦП и ABI рассматриваемой платформы и не пишете ее самостоятельно.

Например, при общих соглашениях о вызовах в 32-разрядной системе x86 вам нужно передать аргументы в стек в обратном порядке (например, последний аргумент первым), затем вызвать функцию через вызов, и как только она вернет очистить стек, выскочив значения (или путем настройки указателя стека).

Так что для функции "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 голос
/ 09 апреля 2011

То, что вы пытаетесь сделать, это не то, для чего нужны переменные аргументы.Лучше просто взять дополнительный параметр 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
}
...