1

I am writing an adapter to combine two APIs (one in C and another in C++). If a function is called on the one API I need to pass the callers ID and the function's arguments to an adapter and call the according function with this information passed.

Now aparently they can not be mapped directly as one interface requires C++ compilation and the name mangling would screw the other so that is why I am using a set of adapters in the first place.

As the number of arguments varies, I looked up variadic functions and found the idea pretty useful, however I am operating on POD only and have to deal with structs, enums and a lot of different arguments per call, which might need to be put back into a struct before feeding it to the target function.

Every example I stumbled upon was far simpler and involved mostly arithmetic operations like summing stuff up , finding largest numbers or printing. Mostly done with for loops on the var_list.

Maybe I got stuck on the idea and it won't work at all, but I am just curious...

Say I wanted to assign the arguments from the list to my target functions parameters (the order of the arguments passed is the correct one), what would be a good way?

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

and so once I made it to the right adapter I want to do

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);
}

Can I access the input parameters of a function to perform assignments like this? Has anyone done something like this before? Is there a conceptual mistake in my thinking?

All I found on the net was this, http://www.drdobbs.com/cpp/extracting-function-parameter-and-return/240000586but due to the use of templates I am not sure if it wouldn't create another problem and so in the end implementing an adapter for each and every single functioncall may be simpler to do.

A SO search only returned this: Dynamic function calls at runtime (va_list)

Community
  • 1
  • 1
AnyOneElse
  • 406
  • 2
  • 6
  • 17
  • You know about `extern "C"` in C++ to get C names? – Kerrek SB Aug 23 '13 at 13:41
  • Could you give an example of what a hand-written adapter would look like? Maybe there is another way to make writing the adapters easier. – Vaughn Cato Aug 23 '13 at 13:55
  • Please give more information about the acutal API:s. Do the API function themselves accept variable number of parameters, or do you try to create a "work for all" adapter function? In the latter case, I would recommend creating a C++ function, with the correct signature, for each C function. Otherwise, if you have control over the interface functions, the best way is for each varadic function, provide functions taking a `va_list`. These functions can be trivially called from a C++ wrapper. For example, the standard library provides `printf` (varadic) and `vprintf` (using `va_list`). – Lindydancer Aug 23 '13 at 19:14
  • @Lindydancer:yes I try to write one for all. I know about extern C in fact. But my "starting point" is a C function, that need to call a C++ function but cannot take the C++ header (for either API complains about compilation issues if both set to either C or C++) What do you mean by "control over the interface functions? – AnyOneElse Sep 02 '13 at 06:02
  • @AnyOneElse, By "control over the interface" I mean if you can change it as you like, as opposed to when it's provided by somebody else. – Lindydancer Sep 02 '13 at 19:15
  • @ Lindydance: I can't. – AnyOneElse Sep 03 '13 at 07:52

1 Answers1

1

First, you should heed Kerrek's advice about extern "C". This is C++'s mechanism for giving an identifier C linkage, meaning that the name won't be mangled by the C++ compiler.

Sometimes, and adapter still needs to be written for a C++ interface, because it manipulates objects that do not map to a C POD. So, the adapter gives the C interface a POD or opaque pointer type to manipulate, but the implementation of that interface converts that into an C++ object or reference and then calls the C++ interface. For example, suppose you wanted to provide a C interface for C++ std::map<int, void *>, you would have a common header file in C and C++ that would contain:

#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

Then, the C++ code could implement the function like:

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;
}

Thus, there is no need to pass the arguments passed via the C interface through a variable argument adapter. And so, there is no need for the C++ code to tease out the arguments from a variable argument list. The C code calls directly into the C++ code, which knows what to do with the arguments.

I suppose if you are trying to implement some kind of automated C adapter code generator by parsing C++ code, you could think that using variable arguments would provide a regular mechanism to communicate arguments between the generated C code interface and the generated C++ adapter code that would call the original C++ interface. For such a scenario, the code for the above example would look something like this:

// 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
    }
};

Where cpp_map_int_ptr_adapter_method_lookup() returns an appropriate cpp_adapter instance based on a table lookup.

jxh
  • 69,070
  • 8
  • 110
  • 193
  • wow, that looks indeed very impressive. I didn't come up with the lookup table idea at all. My thought was: I have the C API and some number of methods (which is the same number as in the C++ API) so my thought was: well just put add an adapter call with the pointer to the corresponding function into the C call - which matter of fact won't work, since the C API cannot include the C++ header. – AnyOneElse Sep 02 '13 at 07:01