Thread overview
Calling arbitrary functions at runtime?
Dec 11, 2016
Kevin Balbas
Dec 11, 2016
Adam D. Ruppe
Dec 11, 2016
Kevin Balbas
Dec 11, 2016
ketmar
December 11, 2016
I'm writing a system to register functions to be called at runtime.  With zero-argument functions, it works fine.  However, I run into a problem with functions that take arguments.

This is the relevant code I started with (zero-argument version):

mixin template CommandSystemRegister(string s = __MODULE__)
{
    void CommandSystemRegisterCommands()
    {
        foreach(name; __traits(allMembers, mixin(s)))
        {
            static if (hasUDA!(mixin(name), RegisterCmd))
            {
                commandTable[name] = &mixin(name);
            }
        }
    }
}

void CommandSystemExecuteCommand(string cmd)
{
    auto result = cmd in commandTable;

    if (result !is null)
    {
        (*result)();
    }
    else
    {
        writefln("command %s not found.", cmd);
    }
}

The way to extend this seemed fairly straightforward.  I did the following things:

1.  Wrap function with a Variant, and put that Variant into a struct alongside an array of stringified parameter types (because Parameters!T can't be stored directly).

2.  On execution, parse the arguments to their correct types.

The problem is, I can't figure out how to actually *call* the function.  If it were python, I could construct a tuple with a comprehension and unpack that, but I can't figure out any way to dynamically construct tuples this way in D.

Basically, I need some way to turn an array of strings into an argument list at runtime.  Is this possible?
December 11, 2016
On Sunday, 11 December 2016 at 22:00:27 UTC, Kevin Balbas wrote:
> Basically, I need some way to turn an array of strings
> into an argument list at runtime.  Is this possible?

Write (or generate) a helper function that loops over the Parameters!Func tuple and populates it from the strings. Call the helper function.


// declare your arguments tuple
Parameters!Func args;

// populate the arguments
foreach(idx, ref arg; args) {
   arg = to!(typeof(arg))(string_args[idx]);
}

Func(args); // call the function with that tuple



The free sample of my book: https://www.packtpub.com/application-development/d-cookbook has a more in-depth example near the end of it.
December 11, 2016
import std.traits;
import std.stdio;


alias FDg = void delegate (string args);

FDg[string] cmdlist;

void register(DG) (string name, DG dg) if (isCallable!DG) {
  cmdlist[name] = delegate (string args) {
    import std.array : split;
    import std.conv : to;
    alias Args = Parameters!DG;
    auto spx = args.split(' ');
    Args ara;
    foreach (immutable idx, ref a; ara) {
      a = spx[idx].to!(typeof(a));
    }
    dg(ara);
  };
}


void main () {
  register("test", (int a, bool b) { writeln("a=", a, "; b=", b); });
  cmdlist["test"]("42 true");
}
December 11, 2016
On Sunday, 11 December 2016 at 22:18:02 UTC, Adam D. Ruppe wrote:
> On Sunday, 11 December 2016 at 22:00:27 UTC, Kevin Balbas wrote:
>> Basically, I need some way to turn an array of strings
>> into an argument list at runtime.  Is this possible?
>
> Write (or generate) a helper function that loops over the Parameters!Func tuple and populates it from the strings. Call the helper function.
>
>
> // declare your arguments tuple
> Parameters!Func args;
>
> // populate the arguments
> foreach(idx, ref arg; args) {
>    arg = to!(typeof(arg))(string_args[idx]);
> }
>
> Func(args); // call the function with that tuple
>
>
>
> The free sample of my book: https://www.packtpub.com/application-development/d-cookbook has a more in-depth example near the end of it.

I see.  I was planning on doing a wrapper-based approach to the function calls if this didn't work out, but I didn't expect it'd be that simple.  Thanks for the tip.