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
}
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.
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 ' + '.
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.
To call a function from a string stored in a variable, use $func.
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);
}
}
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With