Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D: call a function using a string variable with its name

Tags:

d

D learner here... if I have a string (value only known at runtime) that is the name of a function I want to call, how can I do that? Example below...

void func001() {
//stuff
}

void func002() {
//stuff
}

// .........

void func100() {
//stuff
}

void main(char[][] args) {
  auto funcnum = to!uint(args[0]);
  auto funcname = format('func%03d', funcnum);
  ///// need to run the function named 'funcname' here
}
like image 794
J.R. Allen Avatar asked Aug 13 '15 16:08

J.R. Allen


People also ask

How do you call a function in a string name?

You just need convert your string to a pointer by window[<method name>] . example: var function_name = "string"; function_name = window[function_name]; and now you can use it like a pointer.

How do you call a function by its string name in Python?

String To Function Using The eval() Function In Python We can also use the eval() function to convert a string to a function. Here, the input string is the name of the function. In the eval() function, we will pass the name of the function and the ' () ' separated by the addition symbol ' + '.

How can I call a function given its name as a string in C?

Use getattr() to call a class method by its name as a string Call getattr(object, name) using a method name in string form as name and its class as object . Assign the result to a variable, and use it to call the method with an instance of the class as an argument.

How do you call a function by string name in PHP?

To call a function from a string stored in a variable, use $func.


2 Answers

Here's an example using compile time reflection. With __traits(allMembers), we can loop through the names of all members in an aggregate (module, struct, class, etc.) and with __traits(getMember), we can fetch a member by name and do stuff like call it.

The tricky part is getMember requires a compile time string, so we can't just pass it the command line argument directly. Instead, we build a switch to dispatch from the argument - almost just like you would by hand, but instead of writing all the names yourself, you let the loop handle it.

There's only two functions here, but it will scale to any number of them without needing to modify the main function.

See more comments inline:

import std.stdio;

// I'm grouping all the commands in a struct
// so it is easier to loop over them without
// other stuff getting in the way
struct Commands {
    // making them all static so we don't need to instantiate it
    // to call commands. This is often not the best way but it makes
    // for an easy demo and does work well a lot of the time.
    static:

    // Also assuming they all return void and have no arguments.
    // It is possible to handle other things, but it gets a lot
    // more involved. (I think my book example goes partially into
    // it, or something like my web.d goes all the way and generates
    // web/http and javascript/json apis from a full signature but that
    // code is pretty unreadable...)

    void func001() {
        writef("func001 called\n");
    }
    void func002() {
        writef("func002 called\n");
    }
}

void main(string[] args) {
    if(args.length > 1)
    // we switch on the runtime value..
    // the label will be used below
    outer: switch(args[1]) {
        // then loop through the compile time options to build
        // the cases. foreach with a compile time argument works
        // a bit differently than runtime - it is possible to build
        // switch cases with it.
        //
        // See also: http://dlang.org/traits.html#allMembers
        // and the sample chapter of my book
        foreach(memberName; __traits(allMembers, Commands)) {
            case memberName:
                // get the member by name with reflection,
                // and call it with the parenthesis at the end
                __traits(getMember, Commands, memberName)();

            // breaking from the labeled switch so we don't fallthrough
            // and also won't break the inner loop, which we don't want.
            break outer;
        }

        default: // default is required on most D switches
            writef("No such function, %s!\n", args[1]);
            break;
    }
    else { // insufficient args given
        writeln("Argument required. Options are:");
        // we can also loop to list names at runtime
        foreach(memberName; __traits(allMembers, Commands)) {
            writeln(memberName);
        }
    }
}
like image 121
Adam D. Ruppe Avatar answered Nov 28 '22 22:11

Adam D. Ruppe


You can also use an associative array, assuming that each function matches the same prototype:

module test;
import std.format, std.stdio, std.conv;

void func001() {
    writeln(__FUNCTION__);
}

void func002() {
    writeln(__FUNCTION__);
}

alias Proto = void function();
Proto[string] funcs;

// assign the functions to a string in the static constructor
static this() {
    funcs["func001"] = &func001;
    funcs["func002"] = &func002;
}

void main(string[] args) {
    if (args.length < 2) return;
    //!\ note that first argument is always the application exename /!\\
    auto funcnum = to!uint(args[1]);
    auto funcname = format("func%03d", funcnum);

    // try to get the matching function pointer
    Proto* f = funcname in funcs;

    // call it if the function pointer is assigned
    if (f != null) (*f)(); 
}

Note that in your initial example you've made an error with the argument. args[0] is always set to the application exename. The first custom argument is actually args[1].

The solution I propose will work if 1 or 2 is passed as argument and prints:

test.func001

test.func002

or nothing

like image 42
Abstract type Avatar answered Nov 28 '22 22:11

Abstract type