В C возможно ли переадресация вызова функции с переменными числами? Как в,
int my_printf(char *fmt, ...) {
fprintf(stderr, "Calling printf with fmt %s", fmt);
return SOMEHOW_INVOKE_LIBC_PRINTF;
}
Пересылка вызова описанным выше способом, очевидно, не является строго необходимой в этом случае (поскольку вы можете регистрировать вызовы другими способами или использовать vfprintf), но кодовая база, над которой я работаю, требует, чтобы оболочка выполнила некоторую фактическую работу, и не не имеет (и не может добавить) вспомогательную функцию, похожую на vfprintf.
[Обновление: кажется, что есть некоторая путаница, основанная на ответах, которые были предоставлены до сих пор. Чтобы сформулировать вопрос по-другому: в общем, вы можете обернуть какую-то произвольную функцию variadic без изменения определения этой функции .]
Ответы:
Если у вас нет функции, аналогичной
vfprintf
той, которая принимаетva_list
вместо переменного числа аргументов, вы не сможете это сделать . Видетьhttp://c-faq.com/varargs/handoff.html .Пример:
источник
Не напрямую, однако часто (и вы найдете это почти повсеместно в стандартной библиотеке), когда функции с
varargs
переменными числами идут в паре с альтернативной функцией стиля. напримерprintf
/vprintf
Функции v ... принимают параметр va_list, реализация которого часто выполняется с помощью специфической для компилятора 'макро-магии', но вы гарантированно, что вызов функции v ... style из функции с переменными числами, подобной этой, будет работать:
Это должно дать вам эффект, который вы ищете.
Если вы планируете написать библиотечную функцию с переменным числом аргументов, вам также следует сделать компилятор стиля va_list доступным как часть библиотеки. Как вы можете видеть из вашего вопроса, он может оказаться полезным для ваших пользователей.
источник
va_copy
перед передачейmyargs
переменной в другую функцию. Пожалуйста, смотрите MSC39-C , где говорится, что то, что вы делаете, является неопределенным поведением.va_arg()
объектva_list
с неопределенным значением», я этого не делаю, потому что никогда не используюva_arg
в своей вызывающей функции. Значение изmyargs
после вызова (в данном случае)vprintf
является неопределенным (при условии , что она делает насva_arg
). Стандарт гласит, чтоmyargs
«должен быть переданva_end
макросу до любой дальнейшей ссылки на [it]»; это именно то, что я делаю. Мне не нужно копировать эти аргументы, потому что я не собираюсь повторять их в вызывающей функции.ap
передается функции, которая использует,va_arg(ap,type)
то значениеap
не определено после возврата этой функции.C99 поддерживает макросы с переменными аргументами ; в зависимости от вашего компилятора вы можете объявить макрос, который делает то, что вы хотите:
В целом, тем не менее, лучшее решение - использовать форму va_list функции, которую вы пытаетесь обернуть, если она существует.
источник
Почти, используя средства, доступные в
<stdarg.h>
:Обратите внимание, что вам нужно будет использовать
vprintf
версию, а не простоprintf
. В этой ситуации не существует способа прямого вызова функции с переменным значением без ее использованияva_list
.источник
Поскольку на самом деле невозможно правильно переадресовывать такие вызовы, мы решили эту проблему, настроив новый кадр стека с копией исходного кадра стека. Однако это крайне непереносимо и делает всевозможные предположения , например, что в коде используются указатели кадров и «стандартные» соглашения о вызовах.
Этот заголовочный файл позволяет обернуть переменные функции для x86_64 и i386 (GCC). Он не работает для аргументов с плавающей запятой, но должен быть прямым, чтобы расширять их поддержку.
В конце вы можете обернуть вызовы так:
источник
Используйте vfprintf:
источник
Нет возможности перенаправить такие вызовы функций, потому что единственное место, где вы можете получить необработанные элементы стека, находится в
my_print()
. Обычный способ обернуть вызовы подобным образом состоит в том, чтобы иметь две функции, одна из которых просто преобразует аргументы в различныеvarargs
структуры, а другая фактически воздействует на эти структуры. Используя такую модель с двумя функциями, вы можете (например) обернутьprintf()
, инициализируя структурыmy_printf()
сva_start()
, и затем передавать ихvfprintf()
.источник
Да, вы можете сделать это, но это несколько уродливо, и вы должны знать максимальное количество аргументов. Более того, если вы используете архитектуру, в которой аргументы не передаются в стек, например, x86 (например, PowerPC), вам необходимо знать, используются ли «специальные» типы (double, float, altivec и т. Д.), И если Итак, разберитесь с ними соответственно. Это может быть болезненно быстро, но если вы используете x86 или если исходная функция имеет четко определенный и ограниченный периметр, она может работать. Это все еще будет взломать , используйте его для отладки. Не создавайте программное обеспечение вокруг этого. В любом случае, вот рабочий пример на x86:
По какой-то причине вы не можете использовать float с va_arg, gcc говорит, что они конвертируются в удвоенные, но программа вылетает. Уже одно это демонстрирует, что это решение является взломом и что нет общего решения. В моем примере я предположил, что максимальное количество аргументов было 8, но вы можете увеличить это число. Обернутая функция также использует только целые числа, но она работает так же, как и другие «нормальные» параметры, поскольку они всегда приводятся к целым числам. Целевая функция будет знать их типы, но ваша промежуточная оболочка не обязана. Оболочке также не нужно знать правильное количество аргументов, поскольку целевая функция также будет знать это. Чтобы сделать полезную работу (кроме простой регистрации звонка), вам, вероятно, придется знать и то и другое.
источник
GCC предлагает расширение, которое может сделать это:
__builtin_apply
и родственники. См. Построение вызовов функций в руководстве по gcc.Пример:
Попробуй это на Годболте
В документации есть некоторые предостережения о том, что она может не работать в более сложных ситуациях. И вы должны жестко закодировать максимальный размер для аргументов (здесь я использовал 1000). Но это может быть разумной альтернативой другим подходам, которые включают анализ стека на C или ассемблере.
источник
По сути, есть три варианта.
Один из них - не передавать его, а использовать вариативную реализацию целевой функции, а не передавать эллипсы. Другой - использовать макрос с переменным значением. Третий вариант - это все, чего мне не хватает.
Я обычно выбираю первый вариант, так как чувствую, что с ним действительно легко справиться. У варианта два есть недостаток, потому что есть некоторые ограничения к вызову variadic макросов.
Вот пример кода:
источник
Лучший способ сделать это
источник
Не уверен, поможет ли это ответить на вопрос OP, так как я не знаю, почему применяется ограничение на использование вспомогательной функции, похожей на vfprintf в функции-обертке. Я думаю, что ключевая проблема здесь заключается в том, что пересылка списка аргументов с переменными параметрами без их интерпретации является сложной задачей. Что возможно, это выполнить форматирование (используя вспомогательную функцию, похожую на vfprintf: vsnprintf) и переслать отформатированный вывод в упакованную функцию с переменными аргументами (т.е. без изменения определения упакованной функции). Итак, поехали:
Я столкнулся с этим решением здесь .
источник