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

Пиша адаптер за комбиниране на два API (един в C и друг в C++). Ако дадена функция се извика на един API, трябва да предам идентификатора на обаждащия се и аргументите на функцията към адаптер и да извикам съответната функция с тази предадена информация.

Сега очевидно те не могат да бъдат картографирани директно, тъй като единият интерфейс изисква компилация на C++ и изкривяването на името би прецакало другия, така че на първо място използвам набор от адаптери.

Тъй като броят на аргументите варира, потърсих различни функции и намерих идеята за доста полезна, но аз работя само с POD и трябва да се справям със структури, enum и много различни аргументи на повикване, които може да се наложи да бъдат върнати обратно в структура, преди да я подадете към целевата функция.

Всеки случай, на който се натъкнах, беше далеч по-прост и включваше предимно аритметични операции като сумиране на неща, намиране на най-големи числа или отпечатване. Най-често се прави с 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
Моля, дайте повече информация за acutal API:s. Самата API функция приема ли променлив брой параметри или се опитвате да създадете функция за работа за всички адаптери? В последния случай бих препоръчал да създадете C++ функция с правилния подпис за всяка C функция. В противен случай, ако имате контрол върху функциите на интерфейса, най-добрият начин е за всяка варадична функция да предоставите функции, като вземете va_list. Тези функции могат да бъдат тривиално извикани от C++ обвивка. Например, стандартната библиотека осигурява printf (варадик) и 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)


Първо, трябва да се вслушате в съвета на Kerrek относно 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