views:

66

answers:

4

It's easy to load functions from dynamic libraries when you know this function in design time. just do something like this:

   int (*fn)(int);

   l0 = dlopen("./libfoo.so", RTLD_LAZY);
   if (!l0) 
   {
      fprintf(stderr, "l0 %s\n", dlerror());
      return 1;
   }


   fn = (int (*)(int))dlsym(l0, "foo");


   if ((error = dlerror()) != NULL)  
   {
      fprintf(stderr, "fn:%s\n", error);
      return 1;
   }

   x=(*fn)(y);

...

How to execute library function when it's unknown in design time? In runtime you have a function name and array of arguments pointers and array of arguments sizes:

char* fn_name="foo"; int foo_argc; void* foo_argv[]; int foo_argv_size[];

In scripting language it's a piece a cake task, but how to implement this nicely in c++?

A: 

How to execute library function when it's unknown in design time?

You can't. There's no reflection in C++. You'll have to know the functions prototype in order to be able to call it. (Unless you implement your own reflection, anyway.)

sbi
+1  A: 

As the problem is stated, you can't do this in standard C++. There is no way to validly call a function unless the compiler knows the callee's parameters at compilation time. Even in the case of a varargs function, the calling code must be compiled knowing what arguments are passed - there's no way to build a va_list.

If you control the functions in the dll, then probably the best thing is to pass a collection of boost::any (if both will be compiled with the same compiler and libraries), or void* (if you don't have that much compatibility), or some serialised form of the parameters (Google's protocol buffer, maybe).

If the dll already exists and you can't change its interface, then you need either to play non-standard tricks with the stack to put the parameters in the right place according to your calling convention (which is what dynamic languages do when they have a C calling mechanism), or else you need a huge switch statement covering all the function signatures that it might be. Obviously the latter is only possible if you already have a good idea what dll(s) you're dealing with.

Also, in general you need more than a list of pointers to arguments and their sizes. The calling convention quite possibly depends on the types of the parameters, not just the sizes. For example, floating-point arguments could be passed in FPU registers instead of on the stack.

You also need to worry about the return type, and what the calling code is expecting to do with it.

Steve Jessop
+1  A: 

Being a static language, C++ really depends on knowing the type/interface at compile time. I doubt there's a good low-level way to accomplish the task you're looking to solve but, as always, if you add another layer of abstraction you can probably accomplish the goal. Specifically in this case the approach I've used in the past when not knowing exactly what "arguments" need to be passed to a dynamically loaded plugin is to pass a single argument of std::map< std::string, std::string > > (i.e a key=value dictionary) Just pack it with whatever information needs to be passed in and let the plugin figure out what to do with it. It's somewhat inconvenient in that you must encode/decode most of your data but it can be used to gracefully handle most cases I've run in to at least.

Of course, this assumes that you have the ability to control the API of the dynamically loaded DLL by standardizing the Plugin interface. If you can't do that... you're probably out of luck.

Rakis
+1  A: 

There's actually a method to call a function at run-time if you know its calling convention and which parameters it receives. This however lies out of the standard C/C++ language scope.

For x86 assembler:

Assuming the following:

  1. You know to prepare all the parameters for your function in a solid buffer, exactly in the manner they'd be packed on the stack.
  2. Your function doesn't take/return C++ objects by value.

You may use then the following function:

int CallAnyFunc(PVOID pfn, PVOID pParams, size_t nSizeParams)
{
    // Reserve the space on the stack
    // This is equivalent (in some sense) to 'push' all the parameters into the stack.
    // NOTE: Don't just subtract the stack pointer, better to call _alloca, because it also takes
    // care of ensuring all the consumed memory pages are accessible
    _alloca(nSizeParams);

    // Obtain the stack top pointer
    char* pStack;
    _asm {
        mov pStack, esp
    };

    // Copy all the parameters into the stack
    // NOTE: Don't use the memcpy function. Because the call to it
    // will overwrite the stack (which we're currently building)
    for (size_t i = 0; i < nSizeParams; i++)
        pStack[i] = ((char*) pParams)[i];

    // Call your function
    int retVal;
    _asm {
        call pfn
        // Most of the calling conventions return the value of the function (if anything is returned)
        // in EAX register
        mov retVal, eax
    };

    return retVal;
}

You may need to adjust this function, depending on the calling convention used

valdo
A nice trick, but it will still require someone (who's calling this function) to know _at the time the call to your function is written_ which arguments to pass to `pfn`.
sbi
No. The "solid buffer" of parameters may be allocated and initialized at run-time.Note: the size of the parameters buffer is a variable, not a const.
valdo