Reputation: 71
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
}
Upvotes: 7
Views: 750
Reputation: 19797
The approach you are taking is most likely not what you ultimately want to do. I strongly recommend you read about the Command pattern as that is most likely what you are trying to do.
PS. The Wikipedia article gives some complicated examples. In short, your Command objects can have names and you can easily have a map of Command objects, that you can lookup by name. The real power is that you do not have an enormous switch or something similar. Instead you simply pass a Command that you want to execute, and it knows exactly what to do.
If OOP approach is not good for you, then it is an exercise for you to come up with declarative solution.
It is, I believe, possible to define an user defined attribute (UDA) named, say, command
,and use it to annotate every function you want to be treated as "command", and then you can use compile-time introspection like what Adam does in his example to setup everything so you can execute these functions when needed. You can have a map CommandDelegate[string]
where CommandDelegate is simply void delegate()
or similar...
Upvotes: 0
Reputation: 1865
Instead of going all wild with converting your string (parameter, runtime) into a function call ( which are mostly compiled ) and getting into intense memory/runtime/DLL stuff, you can just make a simple if
statement.
Some pseudo for you, I'll happily 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."
Upvotes: 3
Reputation: 1921
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
Upvotes: 6
Reputation: 25595
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);
}
}
}
Upvotes: 12