从字符串获取类 - 通过字符串名称调用函数

Dr.*_*eon 4 d mixins dmd

OK,我想要做的是神仙复杂,但我会试着解释.

比方说,我们希望(在编译时)所有derivedMembers类的someClass.那我们就是这样做的:

const string[] methods = [__traits(derivedMembers,someClass)];
Run Code Online (Sandbox Code Playgroud)

现在,我们怎么能得到someClass"someClass"?(是的,它的字符串表示).


让我解释一下我正在尝试做的事情:

我想创建一个"中间"函数,它接受一个function在一个特定的(预定义的)组类名作为参数(具有参数数组一起)并调用适当的功能从可用静态方法的列表.喜欢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 "";
}
Run Code Online (Sandbox Code Playgroud)

与上面的代码的麻烦是,它仅查找的方法Math.我怎么可以改变它,在一个优雅d友好的方式,所以它会经历像类数组的形式[Math,String,SomethingElse]-它并不必须是变量(我们需要在编译时反正)?


更新:

试图沿着线的东西:

const string[] methods = [__traits(derivedMembers,mixin("Math")];
Run Code Online (Sandbox Code Playgroud)

但它抱怨说Cannot interpret Math at compile time.


更新2:

此外,尝试使用,Object.factory("Math")但它仍然无法正常工作.(也许我只是创建的实例Math类?)

Ada*_*ppe 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"]);
}
Run Code Online (Sandbox Code Playgroud)

请注意,根本没有mixin使用任何表达式.我没有从字符串中获取类的实例,而是创建了TypeTuple我想要使​​用的所有类.这是优选的,mixin因为当在不同的范围内使用时,它不太可能找到名称类; 如果possibleClassesexecute来自不同模块的编译时参数,则类列表仍然有效,而字符串列表将看到未定义的标识符错误,因为库模块不会导入您的用户模块.

另一个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与嵌套循环同上.

顺便说一句,你也可以自动生成包装函数,string[]如果你潜入了std.traits这些参数,它们会转换成其他类型的参数.我希望我的书已经出版了,我在那里写了很长一段时间,并且不想现在写一遍,但是如果你看一下std.traits.ParameterTypeTuple并且ReturnType在同一个模块中,如果你想尝试它就会让你开始.