Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
А про D очень мало информации (в книге Александреску этого нет, на официальном сайте простейший пример). Конструкция mixin — там просто строки, в которых код. Непонятно, можно ли эти строки конструировать программно, если можно — какие операции разрешены для времени компиляции и т.д.
import std.stdio;
enum A
{
a1,
a2,
a3
}
enum B
{
b1,
b2,
b3
}
// Берем два энума и название переменной, для которой будет
// сгенерирован switch
string genSwitch(E1, E2)(string var)
if(is(E1 == enum) && is(E2 == enum))
{
// Просим компилятор выдать нам tuple всех элементов типа
enum E1Members = __traits(allMembers, E1);
enum E2Members = __traits(allMembers, E2);
// Проверяем, чтобы их длинны совпадали, проверка во время компиляции
static assert(E1Members.length == E2Members.length);
// Используем все обычные средства для работы со строками
// для генерации свитча
string s = "final switch("~var~")\n{\n";
foreach(i, member; E1Members)
{
s ~= "\tcase("~E1.stringof~"."~member~"):\n\t{\n";
s ~= "\t\twriteln("~E2.stringof~"."~E2Members[i]~");\n";
s ~= "\t\tbreak;\n\t}\n";
}
return s~"}";
}
void main()
{
auto a = A.a2;
// Способ вывести что-нибудь во время компиляции в консоль
pragma(msg, genSwitch!(A, B)("a"));
// Встраиваем сгенеренный свитч
mixin(genSwitch!(A, B)("a"));
}
final switch(a)
{
case(A.a1):
{
writeln(B.b1);
break;
}
case(A.a2):
{
writeln(B.b2);
break;
}
case(A.a3):
{
writeln(B.b3);
break;
}
}
// Через variable length arguments можно в шаблон
// передавать практически что угодно
template GenSwitch(E1, E2, TS...)
{
// Но лучше проверять, что именно пользователь передал
static assert(TS.length == 1);
static assert(is(typeof(TS[0]) == string));
enum var = TS[0];
enum E1Members = __traits(allMembers, E1);
enum E2Members = __traits(allMembers, E2);
static assert(E1Members.length == E2Members.length);
// Циклы заменяем рекурсией через nested templates
private template GenBody(TSS...)
{
// через TSS передаем текущий номер итерации
enum i = TSS[0];
// и оставшийся кусок от E1Members
alias TS = TSS[1..$];
static if(TS.length == 0) // Дно рекурсии
{
// Процедура 'возврата значения/типа' из шаблона
// нужно объявить enum или alias,
// имя которого совпадает с именем шаблона
enum GenBody = "";
}
else
{
enum GenBody = "\tcase("~E1.stringof~"."~TS[0]~"):\n\t{\n"
~ "\t\twriteln("~E2.stringof~"."~E2Members[i]~");\n"
~ "\t\tbreak;\n\t}\n"
~ GenBody!(i+1, TS[1..$]);
}
}
enum GenSwitch = "final switch("~var~")\n{\n"
~GenBody!(0, E1Members)
~"}";
}
Всё время, пока я писал пост, меня не покидала мысль: «Это или уже есть в Бусте, или пишется там в три строки».
Интерпретация во время компиляции, или Альтернативное понимание лямбд в C++11