[C++] Вызов фукнции с ...

deniska

Тут все немножко упрощено, но суть примерно та.
В нашей библиотеке есть функция
void A(int N, ...);
где N - число параметров, которые ей передадут через ... и все они типа (void *).

Хочется научиться из фунции B с тем же интерфейсом (после некоторой работы) вызвать функцию A с теми же параметрами.

Как это сделать - непонятно. Пока я вижу два пути это сделать
1. Написать case для N и разобрать в ручную случаи, когда N, например, не превосходит 16. В случае, когда N > 16 выкинуть пользователю предупреждение, чтобы не стоит ее вызывать с таким количеством параметров и пусть сделает как-нить подругому.
2. Через asm{} перепихать в стэк в цикле весь тот кусок памяти, что я получил на вход, далее вызвать по адресу функцию А (тоже через asm{}) и подчистить за собой стэк. Это решение относительно N более универсальное, но моему эстетическому чувству прекрасного что-то в нем не нравится. Точнее не нравится то, что такая игра со стеком имеет свойство очень сильно все испортить если будет какая-то неожиданная ошибка (например, надо ловить все exception из А и глубже, чистить стэк и перекидывать исключение дальше). Так же я не до конца уверен, что стэк на всех машинах работает одинаково и эта штука будет работать на странных машинах (а мы частенько работаем на таких).
Есть какие-нить другие варианты, как это можно сделать не переписывая функцию А?

mkrec

va_list и иже с ним ?

deniska

Как "разобрать" то, что мне пришло на вход я понимю. Я не понимаю, как вызвать функцию A. Вызов ее с параметром "va_list" вызовет ее не со стеком, в котором N ссылок на void * а с ссылкой на память, где лежат ссылки на void *. Если иметь доступ в A, то да, можно добавить ей такой интерфейс. Но пока мне интересно решение, когда я не имею никакого отношения к A и не имею право ее менять.

freezer

Я бы сделал кучу перегруженных функций с разным числом аргументов...

mkrec

черт, попутал. Я ею пользовался для впринтфа :( там все заранее готово

deniska

Это эквивалент решению 1 да? Ну то есть от switch по N он мало отличается. Разве что в одном случае я дублирую код B а выбор делает за меня компилятор на этапе компиляции, а в другом выбор делается во время работы и дублирование кода функции B нет. Мне это нравится чуть меньше, так как функция B должна торчать из либки и их будет очень много там.

freezer

дело житейское.
Но портируемых способов решить задачу (а asm - это не портируемый способ) по-моему нет

mkrec

вот их некоего фака:
7.5 Как написать функцию с переменным числом аргументов, которая передает
их другой функции с переменным числом аргументов?
О: В общем случае задача неразрешима. В качестве второй функции нужно
использовать такую, которая принимает указатель типа va_list, как это
делает vfprintf в приведенном выше примере. Если аргументы должны
передаваться непосредственно (a не через указатель типа va_list и
вторая функция принимает переменное число аргументов (и нет
возможности создать альтернативную функцию, принимающую указатель
va_list то создание переносимой программы невозможно. Проблема
может быть решена, если обратиться к языку ассемблера соответствующей
машины.
Я бы пошел по второму варианту, но совместимости не будет. Говорят, сам va_list не вполне кроссплатформен.

deniska

То есть с asm практически никаких гарантий, что это будет работать на чем-то кроме "обычных" компов?

freezer

есть у меня сильное сомнение что это будет работать на "необычных" компах и компиляторах, но обосновать не могу

klyv

Я бы пошел по второму варианту
А я бы - по первому.
ничего не стоит написать функцию f(int,... в которой первым же делом будет вызвана функция f(int,va_list а последнюю вызывать из g

myrka68

а если объединить все параметры в единый список?
типа std::list<void*>

deniska

Проблему просто решить небольшим переписыванием А. Можно сделать и через лист аргументов. Можно просто добавить интерфейс типо vA(int N, void ** где лежит ссылка на кусок памяти откуда все брать. Весь код A просто перенести в vA и просто вызывать из A функцию vA.

Просто не хотелось лезть туда, где функция A релизована.
Но видимо придется. Скорее всего так и поступлю. Просто на всякий случай хотел узнать по поводу существования "универсальных решений", которые не трогают А.

agent007new

Это эквивалент решению 1 да? Ну то есть от switch по N он мало отличается. Разве что в одном случае я дублирую код B а выбор делает за меня компилятор на этапе компиляции, а в другом выбор делается во время работы и дублирование кода функции B нет. Мне это нравится чуть меньше, так как функция B должна торчать из либки и их будет очень много там.
Для этих целей есть boost::preprocessor - он тебе нагенерит сколько угодно функций (почти). А то, что у тебя из либки будет торчать лишних двадцать функций - ничего страшного: у тебя из либки и так торчат все функции, которые нестатические и не в безымянном неймспейсе.
ЗЫ. Эллипсы - зло.

Sanjaz

А я видел решение на с++ на rsdn. Поищи там.
Но суть была такая же как и 2: на стеке выделялся кусок памяти params, потом с помощью va_list в этот кусок копировали параметры, а потом вызывалась B(N, params).
Оставить комментарий
Имя или ник:
Комментарий: