Получить класс из строки. Вызвать функцию по имени строки.

Хорошо, то, что я пытаюсь сделать, довольно сложно, но я постараюсь объяснить.

Допустим, мы хотим (во время компиляции) все 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?)


person Dr.Kameleon    schedule 15.04.2014    source источник
comment
Вы пробовали использовать полное имя, например Object.factory("com.example.kameleon.Math");?   -  person DejanLekic    schedule 15.04.2014
comment
@DejanLekic Похоже, я решил эту часть, используя mixin(className) (который, наконец, сработал). Однако все равно это не нормально. Посмотрите здесь: stackoverflow.com/questions / 23078009 / use-a-loop-at-compile-time   -  person Dr.Kameleon    schedule 15.04.2014
comment
Object.factory просто создает его экземпляр, который позже можно вернуть или использовать через интерфейс. Однако это бесполезно для отражения во время компиляции.   -  person Adam D. Ruppe    schedule 15.04.2014


Ответы (1)


Позвольте мне переписать это, чтобы показать вам несколько интересных трюков:

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, списки аргументов шаблона, результаты _11 _...), вы действительно может помещать в них case операторов!

Итак, все, что вам нужно сделать, это написать обычный оператор switch для переменной времени выполнения, с которой вы хотите сравнить, поместить foreach внутрь него, перебирая в цикле элементы времени компиляции, которые вы ищете, case that_loop_var: и бум, вы в бизнес.

Точно так же для вызова метода я использовал __traits(getMember), а не mixin строку. Это решение поможет избежать конфликтов имен, а IMO - более чистый код. Он также потенциально может обрабатывать перегрузки, если это необходимо (с __traits(getOverloads) вместо __traits(getMember), вы можете затем перебрать каждую из них и сопоставить типы параметров).

Наконец, разрешено вложение switches внутри других case операторов. Если вам нужно выйти из внешнего цикла или switch и не хотите двусмысленности, вы можете пометить циклы и переключатели и использовать break label_name_here;, чтобы указать, из какого из них вы хотите прервать. То же самое для continue с вложенными циклами.

Кстати, вы также можете автоматически генерировать функции-оболочки, которые преобразуют string[] в другие типы аргументов, если вы погрузитесь в std.traits материал. Мне жаль, что моя книга уже не вышла, я написал об этом достаточно подробно и не хочу писать все прямо сейчас, но если вы посмотрите на std.traits.ParameterTypeTuple и ReturnType в том же модуле, это поможет вам начать, если вы захотите попробовать .

person Adam D. Ruppe    schedule 15.04.2014