好吧,我想做的事情很复杂,但我会试着解释一下。
假设我们希望(在编译时)类derivedMembers
的所有someClass
。那我们就这样做:
const string[] methods = [__traits(derivedMembers,someClass)];
现在,我们怎样才能从someClass
获得"someClass"
? (是的,它的字符串表示)。
让我再解释一下我正在尝试做的事情:
我想创建一个“中间”函数,它将function
名称作为参数(以及一个params数组),并从特定(预定义)集合中的可用静态方法列表中调用相应的函数课程。与execute("someFunc",["one","two","three"]);
一样。
以下是完整(测试)代码:
class Math {
static string noArgs(string[] s) { writeln(s); return ""; }
static string withOneArg(string[] s) { writeln(s); return ""; }
static string withTwoArgs(string[] s) { writeln(s); return ""; }
}
string cases()
{
string ret = "";
const string[] methods = [__traits(derivedMembers,Math)];
foreach (string s; methods)
{
ret ~= "case \"" ~ s ~ "\": return Math."~s~"(params);";
}
return ret;
}
string execute(string what, string[] params)
{
switch (what)
{
mixin(cases());
default: break;
}
return "";
}
上述代码的问题在于它只查找Math
中的方法。我怎么能以优雅的D友好方式更改它,以便它将通过像[Math,String,SomethingElse]
这样的类的数组 - 它不必是变量的(无论如何我们需要它在编译时) )?
更新
尝试了以下几点:
const string[] methods = [__traits(derivedMembers,mixin("Math")];
但它抱怨Cannot interpret Math at compile time
。
更新2:
此外,尝试使用Object.factory("Math")
,但它仍然无效。 (也许我只是在创建一个Math
类的实例?)
答案 0 :(得分:5)
让我改写一下,向你展示一些很酷的技巧:
import std.stdio;
class Math {
static string noArgs(string[] s) { writeln(s); return ""; }
static string withOneArg(string[] s) { writeln(s); return ""; }
static string withTwoArgs(string[] s) { writeln(s); return ""; }
}
class String {
static string oneArg(string[] s) { return null; }
}
string execute(string what, string[] params) {
import std.string;
auto parts = what.split(".");
auto className = parts[0];
auto methodName = parts[1];
import std.typetuple;
switch(className) {
default: assert(0, "unknown class");
foreach(possibleClass; TypeTuple!(Math, String)) {
case possibleClass.stringof:
switch(methodName) {
default: assert(0, "unknown method");
foreach(memberName; __traits(derivedMembers, possibleClass)) {
case memberName:
return __traits(getMember, possibleClass, memberName)(params);
break;
}
}
break;
}
}
assert(0);
}
void main() {
execute("Math.withOneArg", ["cool"]);
execute("String.oneArg", ["cool"]);
}
请注意,根本没有使用mixin
个表达式。我没有从字符串中获取类的实例,而是创建了我想要使用的所有类的TypeTuple
。这比mixin
更可取,因为当在不同的范围内使用时,它不太可能找到名称类;如果possibleClasses
是来自不同模块的execute
的编译时参数,则类列表仍然有效,而字符串列表将看到未定义的标识符错误,因为库模块不会导入用户模块。
我删除的另一个mixin
是生成案例的那个。这看起来很疯狂,但是在D中是允许的:如果你有一个编译时foreach
(即某个内置元组的foreach
,例如TypeTuple
,模板参数列表,__traits
的结果......)你实际上可以将case
语句放在其中!
所以,你所要做的就是在你要比较的运行时变量上写一个常规的switch
语句,把foreach
放在它里面循环编译时的东西你是寻找,case that_loop_var:
和繁荣,你就是在做生意。
同样,我使用__traits(getMember)
而不是mixin
字符串来调用该方法。此解决方案将有助于避免名称冲突,IMO是更清晰的代码。如果需要,它也可以潜在地处理重载(使用__traits(getOverloads)
而不是__traits(getMember)
,你可以遍历每个然后匹配参数类型。)
最后,允许在其他switch
语句中嵌套case
es。如果您需要突破外部循环或switch
并且不需要歧义,可以标记循环和开关,并使用break label_name_here;
指定要中断的循环。同上continue
嵌套循环。
BTW你也可以自动生成包装函数,如果你潜入string[]
的话,它会将std.traits
转换成其他类型的参数。我希望我的书已经出版了,我在那里写了很长一段时间,并且不想现在就写出来但是如果你在同一个模块中看std.traits.ParameterTypeTuple
和ReturnType
如果您想尝试,请帮助您。