Добавление дополнительных аргументов в список переменных в стиле C

Я пытаюсь написать оболочку для функции с переменным числом в стиле C, такой как printf, которая добавляет дополнительные аргументы, но у меня возникли проблемы с этим:

void printf(char* fmt, ...);  // the thing I'm trying to wrap

void wrapper(char* fmt, ...)
{
printf(fmt, extra_arg1, extra_arg2, /* the variadic arguments */);
}

но что я пишу для /* the variadic arguments */?

Даже если у функции, которую я пытаюсь обернуть, есть версия, которая требует va_listЯ не могу это сделать:

void vprintf(char* fmt, va_list args);

void wrapper(char* fmt, ...)
{
va_list args;
va_start(args, fmt);
vprintf(fmt, extra_arg1, extra_arg2, args);
va_end(args);
}

extra_arg1, extra_arg2 а также args не волшебным образом превращаться в va_list тот vprintf надеется.

Я знаю, что мог бы написать макрос и использовать __VA_ARGS__:

void printf(char* fmt, ...);  // the thing I'm trying to wrap

#define wrapper(fmt, ...) printf(fmt, extra_arg1, extra_arg2, __VA_ARGS__)

но я пытаюсь избежать этого и написать обертку как функцию. Есть способ сделать это?

(Кстати, я не могу использовать вариабельные шаблоны C ++ 11).

2

Решение

Я не вижу ничего плохого в использовании макросов, но это только я 🙂

В любом случае, есть способ сделать это.
Если вас не волнует переносимость, вы можете использовать встроенную сборку.
Функции Variadic используют соглашение о вызовах cdecl, что означает, что вызывающая сторона отвечает за передачу аргументов в стек, а затем за очистку стека после возврата вызываемого.
Таким образом, простой вызов printf

printf("%d, %d, %d", 1, 2, 3)

будет выглядеть примерно так:

char format[] = "%d, %d, %d";
__asm {
push 3
push 2
push 1
lea  eax, [format]
push eax
call printf
add  esp,10h
}

Обратите внимание, что последний нажатый параметр используется va_start получить адрес в стеке и va_arg просто перебирайте аргументы.

Вы можете использовать то же поведение и снова выдвинуть аргументы variadic:

char new_format[] = "%d, %d, %d, %d, %d";
wrapper(new_format, ...) {
char *va = addressof(new_format)+sizeof(char*);  // first argument after new_format
int i;

__asm {
push extra2
push extra1
}
for(i=0; i < num_of_arguments; i++)
__asm push, va += sizeof(argument)
// we just pushed [3, 2, 1] in our example
__asm {
lea  eax, [new_format]
push eax
call printf
add  esp,18h   // <-- We now have to discard more stuff on the stack
}
}

Обратите внимание, что вы должны заранее знать, сколько аргументов в стеке и размер каждого аргумента (printf вычисляет это из строки формата).

Вы можете имитировать printf и извлекать соответствующую информацию из строки формата (или любой другой функции, которую вы переносите).

Теперь, если у вас нет никаких знаний о типах, вы все равно можете сойти с рук
освободив место в стеке для ваших переменных и скопировав память (из стека в стек):

__asm {
push extra2
push extra1
sub esp, total-bytes-needed
}
memcpy(esp, block-of-memory, size-of-block);

Это довольно громоздкое и не переносимое решение, но оно должно работать.

Надеюсь, это поможет!

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]