David
David

Reputation: 21

Is there a way to pass an unknown number of arguments to a function?

Right now, I am trying to call a function in C++ through a Json object. The Json object would provide me with the name of the callee function and all the parameters. I will be able to extract the parameters using a for loop, but I am not sure how I can pass them in. For loop only allows me to pass arguments one by one, and I did not find a way to call a function besides passing in all the arguments at once. I've made a temporary solution of:

if (parameter_count == 1)
      func(param_1);
if (parameter_count == 2)
      func(param_1, param_2);
...

This solution seems would not work for all cases since it can only work for functions with a limited number of arguments (depending on how many ifs I write). Is there a better way for this? Thanks!

EDIT: Sorry if I was being unclear. I do not know anything about func. I will be reading func from DLL based on its string name. Since I can't really change the function itself, I wouldn't be able to pass in a vector or struct directly.

Or perhaps did I have the wrong understanding? Are we allowed to pass in a single vector in place of a lot of parameters?

Sorry for making a mess through so many edits on this question. Brandon's solution with libffi works. Thanks!

Upvotes: 1

Views: 1077

Answers (1)

Daniel McLaury
Daniel McLaury

Reputation: 4293

So the problem as I understand it is that you have a void * pointer (which would come from your platform's DLL loading code) which "secretly" is a pointer to a function with a signature which is only known at runtime. You'd like to call this function at runtime with specified arguments.

Unfortunately, this is not possible to do cleanly with standard C++ alone. C++ cannot work with types that are not present in the program at compile-time, and since there is an infinite number of potential function signatures involved here there is no way to compile them all in.

What you'll want to do instead is manually set up the stack frame on your call stack and then jump to it, either via inline assembly or via some library or compiler extension that accomplishes this for your platform.

Here is a simple example of doing this via inline assembly. (To do this in general you will need to learn your platform's calling convention in detail, and needless to say this will constrain your program to the platform(s) you've implemented this for.)

I haven't actually tried it, but gcc has a compiler extension __builtin_apply that is apparently just meant to forward the arguments from one method wholesale to another but which could perhaps be used to accomplish something like this if you learned the (apparently opaque) description of the method.

[Update: Apparently I missed this in the comments, but Brandon mentioned libffi, a library which implements a bunch of platforms' calling conventions. This sounds like it might be the best option if you want to take this sort of approach.]

A final option would be to constrain the allowed signatures of your functions to a specified list, e.g. something like

switch(mySignature)
{
  case VOID_VOID:
    dynamic_cast<std::function<void(void)> *>(myPtr)(); 
    break;

  case VOID_INT:
    dynamic_cast<std::function<void(int)> *>(myPtr)(my_int_arg_1); 
    break;
  // ...
}

(Syntax of the above may not be 100% correct; I haven't tested it yet.) Whether this approach is sensible for your purposes depends on what you're doing.

Upvotes: 1

Related Questions