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

D student is here ... if I have a string (the value is known only at runtime), that is, the name of the function I want to call, how can I do this? 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 } 
+6
source share
4 answers

Here is an example using compile time reflection. Using __traits(allMembers) we can scroll through the names of all the members in the aggregate (module, structure, class, etc.) and __traits(getMember) , we can get a member by name and do things like call it.

The complex part of getMember requires a compile-time string, so we cannot just pass it a command line argument directly. Instead, we create a switch to send from the argument - almost the same as you, but instead of writing all the names yourself, you let this method handle it.

There are only two functions here, but it will scale to any number of them without having to change the main function.

Additional comments:

 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); } } } 
+10
source

Instead of wringing everything with the conversion of your string (parameter, runtime) into a function call (which is mainly compiled) and getting into intensive memory / runtime / DLL, you can simply make a simple if .

Some pseudo for you, I will gladly translate it to D if you want me to ...

 Given functions func001, func002, func003: Read and store a string input if the input is equal to "func001": Call func001 else if input is equal to "func002": Call func002 else if the input is equal to "func003": Call func 003 else Print "Not a valid function name. Available functions are func001, func002, and func003." 
+4
source

You can also use an associative array, assuming that each function corresponds to 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 first example, you made a mistake with an argument. args[0] always set to the exename of the application. The first user argument is actually args[1] .

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

test.func001

test.func002

or nothing

+4
source

The approach you take is most likely not what you ultimately want to do. I highly recommend that you read about the Team Template , as this is most likely what you are trying to do.

PS. The Wikipedia article contains several complex examples. In short, your Command objects can have names, and you can easily have a map of Command objects that you can search by name. The real power is that you don’t have a huge switch or something like that. Instead, you simply pass the command you want to execute, and it knows exactly what to do.

If the OOP approach is not suitable for you, then this exercise is for you to come up with a declarative solution.

I believe that you can define a user-defined attribute (UDA) with the name, say, command , and use it to annotate each function that you want to process as a β€œcommand”, and then you can use compilation like what Adam does in in your example, to configure everything so that you can perform these functions when necessary. You may have a CommandDelegate[string] map, where CommandDelegate is just void delegate() or similar ...

0
source

All Articles