Использование var_arg для передачи параметров для вызовов функций

Я пишу адаптер для объединения двух API (один на C, а другой на C++). Если функция вызывается в одном API, мне нужно передать идентификатор вызывающего абонента и аргументы функции адаптеру и вызвать соответствующую функцию с этой переданной информацией.

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

Поскольку количество аргументов варьируется, я искал вариативные функции и нашел эту идею довольно полезной, однако я работаю только с POD и должен иметь дело со структурами, перечислениями и множеством различных аргументов для каждого вызова, которые, возможно, придется вернуть. в структуру перед передачей ее целевой функции.

Каждый пример, на который я наткнулся, был намного проще и включал в себя в основном арифметические операции, такие как суммирование, поиск наибольших чисел или печать. В основном это делается с помощью циклов for в var_list.

Может быть, я застрял на идее, и это вообще не сработает, но мне просто интересно...

Скажем, я хотел присвоить аргументы из списка параметрам моих целевых функций (порядок переданных аргументов правильный), что было бы хорошим способом?

BOOL Some_Function(
    /* in  */ CallerId *pObjectId,
    /* in  */ someDataType argument1 )
{
    BOOL ret = Adapter_Call(pFunction, pObjectId, argument1);
    return ret;
}

и так раз я добрался до нужного адаптера хочу сделать

BOOL Adapter_Call(*pFunction, *pObjectId, argument1, ...)
{
  va_list args;
  va_start(args, argument1);

  /*go over list and do `var_list[i] = pFunctionArgList[i]` which is  
    of whatever type so I can use it as input for my function */

  va_end(args);
  pObjectId.pFunction(arg1,...,argn);
}

Могу ли я получить доступ к входным параметрам функции для выполнения подобных назначений? Кто-нибудь делал что-то подобное раньше? Есть ли концептуальная ошибка в моем мышлении?

Все, что я нашел в сети, это http://www.drdobbs.com/cpp/extracting-function-parameter-and-return/240000586but из-за использования шаблонов я не уверен, что это не создаст еще одну проблему, поэтому, в конце концов, реализация адаптера для каждого и каждый вызов функции может быть проще сделать.

Поиск SO вернул только это: Динамические вызовы функций во время выполнения (va_list)


person AnyOneElse    schedule 23.08.2013    source источник
comment
Вы знаете о extern "C" в C++ для получения имен C?   -  person Kerrek SB    schedule 23.08.2013
comment
Не могли бы вы привести пример того, как будет выглядеть написанный от руки адаптер? Может есть другой способ упростить написание адаптеров.   -  person Vaughn Cato    schedule 23.08.2013
comment
Пожалуйста, дайте больше информации об актуальных API:s. Принимает ли сама функция API переменное количество параметров или вы пытаетесь создать работу для всех функций адаптера? В последнем случае я бы рекомендовал создать функцию C++ с правильной подписью для каждой функции C. В противном случае, если у вас есть контроль над функциями интерфейса, лучше всего для каждой функции varadic предоставить функции, принимающие va_list. Эти функции можно тривиально вызывать из оболочки C++. Например, стандартная библиотека предоставляет printf (varadic) и vprintf (используя va_list).   -  person Lindydancer    schedule 23.08.2013
comment
@Lindydancer: да, я пытаюсь написать один для всех. Я знаю о extern C на самом деле. Но моей отправной точкой является функция C, которая должна вызывать функцию C++, но не может принимать заголовок C++ (поскольку любой API жалуется на проблемы с компиляцией, если оба установлены на C или C++). Что вы подразумеваете под контролем над функциями интерфейса?   -  person AnyOneElse    schedule 02.09.2013
comment
@AnyOneElse, под контролем над интерфейсом я имею в виду, можете ли вы изменить его по своему усмотрению, а не когда он предоставляется кем-то другим.   -  person Lindydancer    schedule 02.09.2013
comment
@ Lindydance: Я не могу.   -  person AnyOneElse    schedule 03.09.2013


Ответы (1)


Во-первых, вы должны прислушаться к совету Керрека по поводу extern "C". Это механизм C++ для предоставления идентификатора связи C, что означает, что имя не будет искажено компилятором C++.

Иногда адаптер все же необходимо написать для интерфейса C++, поскольку он манипулирует объектами, которые не отображаются на C POD. Таким образом, адаптер предоставляет интерфейсу C POD или непрозрачный тип указателя для управления, но реализация этого интерфейса преобразует его в объект или ссылку C++, а затем вызывает интерфейс C++. Например, предположим, что вы хотите предоставить интерфейс C для C++ std::map<int, void *>, у вас будет общий заголовочный файл для C и C++, который будет содержать:

#ifdef __cplusplus
extern "C" {
#endif
    struct c_map_int_ptr;
    // ...
    // return -1 on failure, otherwise 0, and *data is populated with result
    int c_map_int_ptr_find (struct c_map_int_ptr *, int key, void **data);
#ifdef __cplusplus
}
#endif

Затем код C++ мог бы реализовать такую ​​функцию, как:

typedef std::map<int, void *> map_int_ptr;

int c_map_int_ptr_find (struct c_map_int_ptr *cmap, int key, void **data) {
    map_int_ptr &map = *static_cast<map_int_ptr *>(cmap);
    map_int_ptr::iterator i = map.find(key);
    if (i != map.end()) {
        *data = i->second;
        return 0;
    }
    return -1;
}

Таким образом, нет необходимости передавать аргументы, передаваемые через интерфейс C, через адаптер переменных аргументов. Таким образом, коду C++ не нужно извлекать аргументы из списка переменных аргументов. Код C обращается непосредственно к коду C++, который знает, что делать с аргументами.

Я предполагаю, что если вы пытаетесь реализовать какой-то автоматический генератор кода адаптера C путем разбора кода C++, вы можете подумать, что использование переменных аргументов обеспечит обычный механизм для передачи аргументов между сгенерированным интерфейсом кода C и сгенерированным кодом адаптера C++, который вызвать исходный интерфейс C++. Для такого сценария код приведенного выше примера будет выглядеть примерно так:

// C interface
typedef struct c_map_int_ptr c_map_int_ptr;
typedef struct c_map_int_ptr_iterator c_map_int_ptr_iterator;
//...
c_map_int_ptr_iterator c_map_int_ptr_find (c_map_int_ptr *map, int key) {
    c_map_int_ptr_iterator result;
    cpp_map_int_ptr_adapter(__func__, map, key, &result);
    return result;
}

// C++ code:
struct cpp_adapter {
    virtual ~cpp_adapter () {}
    virtual void execute (va_list) {}
};

void cpp_map_int_ptr_adapter(const char *func, ...) {
    va_list ap;
    va_start(ap, func);
    cpp_map_int_ptr_adapter_method_lookup(func).execute(ap);
    va_end(ap);
}

//...
struct cpp_map_int_ptr_find_adapter : cpp_adapter {
    void execute (va_list ap) {
        map_int_ptr *map = va_arg(ap, map_int_ptr *);
        int key = va_arg(ap, int);
        c_map_int_ptr_iterator *c_iter = va_arg(ap, c_map_int_ptr_iterator *);
        map_int_ptr::iterator i = map->find(key);
        //...transfer result to c_iter
    }
};

Где cpp_map_int_ptr_adapter_method_lookup() возвращает соответствующий экземпляр cpp_adapter на основе поиска в таблице.

person jxh    schedule 23.08.2013
comment
вау, это выглядит действительно очень впечатляюще. Я вообще не придумал идею таблицы поиска. Я подумал: у меня есть C API и некоторое количество методов (такое же количество, что и в C++ API), поэтому я подумал: просто добавьте вызов адаптера с указателем на соответствующую функцию в вызов C - что на самом деле не будет работать, поскольку C API не может включать заголовок C++. - person AnyOneElse; 02.09.2013