Во время компиляции отражение Д’

Во время компиляции отражение Д’

485

Хороший день, Хабр!
Итак, какую информацию дозволяет получить компилятор и как его можно применять? Д’дозволяет программеру впрямую применять информацию, как это определено компилятором и не вывести ее хитрые методы. Сейчас давайте побеседуем о том, что делает метапрограммирования в D так гибкий и мощнейший — времени компиляции отражение.
Они ведут себя идиентично, но у их есть одна узкая идеологическая разница(для вызова typeof(выражение)) не проверить, что компиляция, и инспектирует существование Тип выражения. Потому, на теоретическом уровне, полностью может быть, что Тип может быть известна, но для всех правил, таковая конструкция не компилируется. Давайте начнем с, пожалуй, более нередко используемый, способы — определение корректности выражения:
__черты нрава( компилирует, (А + Б );
это( typeof на( А + Б ) );

И __черты нрава(компилирует, выраж) и(вызова typeof(выражение)) ожидать допустимым, с точки зрения лексики, выражение выражение (к примеру, выражение 12thb не является допустимым идентификатором, потому компилятор выдаст ошибку). На практике я не лицезрел таковых ситуациях (наверняка, все-таки не в языке).

Пример использованиязадача: сделать функцию, которая воспринимает любые «похожие» на массивы объектов, содержащих «похожие» на ряд частей, которые возвращает среднее значение (мат. длина );
}

Внедрение:
импорт ЗППП.строчка : Формат;

структура Vec2
{
поплавок х=0, у=0;

// перегруженные операторы сложения и вычитания
авто opBinary(строчка ОП)( авто реф значения const Vec2 РГО ) уст
раз( ОП == «+» || ОП == «-» )
{ миксин( Формат( «возвращение Vec2( х %1$s в РГО.х, у, %1$s в РГО.г );», ОП ) ); }

// перегрузка оператора умножения
авто opBinary(строчка ОП)( поплавок РГО ) уст
раз( ОП == «*» )
{ возвращение Vec2( х * рит, у * РГО ); }
}

структура треугольника
{
Vec2 Р1, Р2, Р3;

// перегрузка эта форма Варе[индекс]
авто opIndex(Тип size_t в)
{
выключатель(в)
{
вариант 0: вернуть Р1;
вариант 1: возвращение Р2;
вариант 2: Возвращение Р3;
по умолчанию: кинуть новое исключение( «треугольник есть лишь три стихии» );
}
}

чисто статические типы size_t длины() { возвращение 3; }
}

пустота основной()
{
авто F = [ 1.0 Ф, 2, 3 ];
утверждаю( Ф.имею в виду == Ф 2.0 ); // с вещественными числами

авто с V = [ Vec2(1,6), Vec2(2,7), Vec2(3,5) ];
утверждать( имею в виду в. Решение:
шаблон isNumArray(Т)
{
перечисление isNumArray = __черты нрава(компилирует,
{
авто в = т. инициализации.длина; // свойство length
статические утверждать( это( для вызова typeof(б) : Тип size_t ) );
});
}

авто среднего(Т)( Т обр ) @раз имущество( isNumArray!Т )
в { утверждать( обр. длина )
рет = цоб + обр[я]; // мы еще не проверили перегрузка оператора +=
возвращение в отставке * ( 1.0 Ф / обр. инит[0]; // opIndex с int параметр

раз статический( !__черты нрава(isArithmetic,а) ) // раз Тип Не является арифметической, то он должен
{
статический способ assert( __черты нрава( компилирует, А=А+А ) ); // берем форму
статический способ assert( __черты нрава( компилирует, А=А-А ) ); // вычитание
статический способ assert( __черты нрава( компилирует, А=А*.0С ) ); // будет умножена на поплавок
}

авто Б = т. == Vec2(2,6) ); // массив с элементами пользовательского типа

авто т = треугольник( Vec2(1,6), Vec2(2,7), Vec2(3,5) );
утверждать( т.имею в виду == Vec2(2,6) ); // с пользовательского типа
} обр. ожидание). длина > 0 ); } тело
{
// безопасно внедрение arr[индекс] и арр.длина
// для частей, которые возвращают модуль arr[индекс] будет иметь нужные операции
авто в отставке = модуль arr[0] — модуль arr[0]; // элемент нейтральный с точки зрения сложения (0)
по каждому элементу( я; 0 ..
Внимание: не используйте код из примера (isNumArray), так как он не учитывает некие детали (opIndex может возвращать константную ссылку, то будет нереально привязать эксплуатации).
);
В этом случае TypeTempl — описание-типа (композит) и TemplParams частей, составляющих TypeTempl. И есть форма, которая соединяет в для себя проверку декларации и alias’а
это( Т идент == Специализация );

Есть еще одна увлекательная методика — картины-сравнение типов. это( Т == Тип ); // раз Тип T-Тип
это( Т : Тип ); // раз Тип T может быть неявно приведен к типу
Есть формы заключается в том, что сделать новейший псевдоним ы
это( Т идент );
В этом случае, когда деяния типа T, псевдоним будет сотворен для него под заглавием идент. это( Т ); // инспектирует семантические деяния Т
Дальше, Тип «Т». Соответственно, проверяется, является ли Тип T-это структура, объединение, класс и др. это( Т == TypeTempl, TemplParams… )
Конструкция имеет достаточно большой набор способностей. );
это( Т идент : TypeTempl, TemplParams… );
это( Т : TypeTempl, TemplParams… Дизайн(… во всех вариантах проверяется на семантические деяния. );

// с объявлением псевдоним ы
это( Т идент == TypeTempl, TemplParams… Но было бы любопытно соединить эту форму с чеком
это( Т идент : Тип );
это( Т идент == Тип );
Примернедействительными фу(Т)( Т )
{
раз статический( ИС( Т U : долго ) ) // раз Тип T преобразуется в Long
псевдоним Числ = у; // применять
еще
псевдоним Числ = длиннющий; // по другому долго
}
Вы также сможете проверить, какой тип, чтоб изучить его модификаторы
это( Т == Специализация );
В этом случае, Специализация-одно из вероятных значений: структуры, объединения, класса, интерфейса, перечисления, функции, делегировать, константный, постоянный, общий.
stringof);
прагма(глутамат натрия, «Н: «, Н);
прагма(глутамат натрия, «Т: «, Т);
}
еще статический раз( есть( у Т : Т[Х], Х ) )
{
прагма(глутамат натрия, «ассоциативный массив т[х]: «, п );
прагма(глутамат натрия, «Т(значение): «, Т);
прагма(глутамат натрия, «х(ключ): «, х);
}
еще статический раз( есть( у Т : Т[Н], типы size_t П ) )
{
прагма(глутамат натрия, «статический массив Т[Н]: «, П );
прагма(глутамат натрия, «Т(значение): «, Т);
прагма(глутамат натрия, «Н(длина): «, Н);
}
еще прагма(глутамат натрия, «иной: «, П );
прагма(глутамат натрия,»»);
}

пустота основной()
{
функ( Фу!(10,двойной).инит );
функ( бар!(12,строчка).инит );
клавишу func( [ «привет»: 23 ] );
функ( [ 42: «хабр» ] );
функ( Фу!(8,кор).инит.данных );
клавишу func( 0 );
}

Вывод при компиляции
структура таковой как Foo: Фу!(10LU, двойной)
Ы: Фу(улонг Н, Т), раз (н > 0)
Н: 10LU
Т: двойной

структура таковой как Foo: бар!(12LU, строчка)
Ы: бар(улонг Н, Т), раз (н > 0)
Н: 12LU
Т: строчку

ассоциативный массив т[х]: инт[строчка]
Т(значение): инт
Х(ключ): строчку

ассоциативный массив т[х]: строчка[инт]
Т(значение): строчка
Х(ключ): инт

статический массив Т[Н]: короткое[8]
Т(значение): маленький
Н(длина): 8LU

остальные: инт

Дизайн __черты нрава(ключевое слово …)
Крупная часть __черты нрава, опосля klyuchevogo слова, воспринимает выражение как аргумент (либо перечень, разбитый запятыми), инспектирует итог на соответствие и возвращает значение типа boolean, отражающее завершение аудита. Иная часть воспринимает 1 аргумент и возвращает что-то наиболее информативное, чем логическое значение (в основном перечне-то). Выражение обязано возвращать или как таковой тип либо тип значения. Примерструктуры файла Foo(size_t К Н, Т) раз( n > 0 ) { Т[Н] данных; }
структура бар(Тип size_t Н, Т) раз( n > 0 ) { поплавка[Н] обр; Т значение; }

пустота клавишу func(Ю)( Ю Валь )
{
статический раз( есть( Uк Э == ы!(Н,Т), псевдоним-ы, типы size_t Н, Т ) )
{
прагма(глутамат натрия, «структуры, таковой как Foo: «, Е );
прагма(глутамат натрия, «с: «, С.
Рецензент __черты нрава:

компилирует действителен ли выражение
isAbstractClass — абстрактные классы
isArithmetic — арифметические типы (целые и перечисления)
isAssociativeArray — ассоциативные массивы
isFinalClass — Заключительные занятия (от которых нельзя наследовать)
isPOD — обыденные старенькые типы данных для инициализации обычного копирования б (запрещены сокрытые поля, деструкторы)
isNested — вложенные типы (в зависимости от контекста)
B)); // правильно

пустота Ф1()
{
авто Ф2() { возвращение 12; }
прагма(глутамат натрия,__черты нрава(isNested,Ф2)); // правильно
}

авто Ф1()
{
авто в val = 12;
структура ы { авто Ф2() { возвращение в val; } } // используем контекст Ф1
возвращение С. Примерыкласс { класс B {} }
прагма(глутамат натрия, __черты нрава(isNested,А. инициализации;
}
прагма(глутамат натрия,__черты нрава(isNested,вызова typeof(Ф1 ()))); // правильно
isFloating — с плавающей точкой (включая сложные)
isIntegral — целые числа
isScalar — скалярные типы (целые числа, перечисления, указатели), хотя __вектор(Тип int[4]) также представляет собой скалярный Тип
isStaticArray — статические массивы
isUnsigned — беззнаковое целое число
isVirtualMethod — виртуальный способ (что peregruzit)
isVirtualFunction — виртуальные функции (те, что в таблице виртуальных функций)
isAbstractFunction — реферат функции
isFinalFunction — заключительная функция
isStaticFunction — статическая функция
isOverrideFunction — перегруженной функции
isRef — аргумент ссылка
естьиз — выходной аргумент ссылка
isLazy — ленивый аргумент (выполняется по мере необходимости)
isSame — выражения однообразные
hasMember — ли класс/структура такового поля/способа, который воспринимает в качестве первого аргумента Тип (либо «Тип объекта»), 2-ая строчка с именованием поля/способа
Примерструктуры переменной Foo { float значение; }
прагма(глутамат натрия, __черты нрава(hasMember, Фу, «значение»)); // правильно
прагма(глутамат натрия, __черты нрава(hasMember, Фу, «данные»)); // ересь

«ф|» : «-|»;
рет ~= __черты нрава(isStaticFunction,Т)? «П|» : «-|»;
рет ~= __черты нрава(isOverrideFunction,Т)? -|-|-|-|п|-|
делегат: д-|-|-|-|-|-|
с. «;
рет ~= __черты нрава(isVirtualMethod,Т)? Раз функция не перегружена, и был сначало финал, это не виртуальный способ, но это будет виртуальная функция. функ: Ф-|-|-|-|-|-|
глобальные: Ф -|-|-|-|п|-|
собой.стат: Ф -|-|-|-|п|-|
собой.simple1: Ф М|В|-|-|-|-|
собой.simple2: Ф М|В|-|-|-|-|
собой.simple3: Ф-|-|-|-|-|-|
собой.абстракт: Ф М|В|В|-|-|-|
собой.fnlNOver: Ф -|В|-|Ф|-|-|
б.simple1: Ф М|В|-|-|-|О|
б.simple2: Ф М|В|-|Ф|-|О|
б.абстракт: Ф М|В|-|-|-|О|
АР.абстракт: Ф М|В|-|Ф|-|О|
я.абстракт: Ф М|В|В|-|-|-|
я.нос: Ф -|-|А|Ф|-|-|

isVirtualMethod возвращает True для всего, что можно перегрузить, либо был перегружен. -|-|-|-|-|-|
функция:? О<неких>функций, и разницу меж isVirtualMethod и isVirtualFunctionдля ясности написал маленький тест, показывающий разницу
импорт ЗППП.помощью stdio, ЗППП.строчки;

строчку «Test» (псевдоним Т)()
{
строчка в отставке;
рет ~= является( для вызова typeof(Т) == делегат )? «В| на» : «-|»;
рет ~= __черты нрава(isAbstractFunction,Т)? «Ф» : «? О вопросительных символов насчет лямбд и функций (многофункциональный литерал типа) не могут разъяснить, они по неизвестной причине я не инспектировал ни функции, ни делегировать. «А|» : «-|»;
рет ~= __черты нрава(isFinalFunction,т)? «О|» : «-|»;
возвращение в отставке;
}

класс A
{
статический стат недействительными() {}
пустота simple1() {}
пустота simple2() {}
Личная недействительными simple3() {}
Инструкция недействительными абстракт() {}
конечной пустоты fnlNOver() {}
}

класс B : в
{
переопределить недействительными simple1() {}
окончательное переопределить недействительными simple2() {}
переопределить недействительными абстракт() {}
}

класс C : Б
{
окончательное переопределить недействительными абстракт() {}
}

интерфейс я
{
пустота абстракт();
конечной пустоты НСО() {}
}

структура ы { недействительными клавишу func(){} }

пустота globalFunc() {}

пустота основной()
{
В А; Б Б; В С; Я Я; Ы;
writeln( » ИД Т М|В|А|Ф|И|О|» );
writeln( «—————————» );
writeln( » лямбда -:», тест!(х=>х) );
writeln( » функция: «, тест!((){ возвращение 3; }) );
writeln( » делегат: «, тест!((){ возвращение в; }) );
writeln( » ы.функ: «, тест!(ы.клавишу func) );
writeln( » глобальный «, тест!(globalFunc) );
writeln( » файл.стат: «, тест!(а.стат) );
writeln( » файл.simple1: «, тест!(а.simple1) );
writeln( » файл.simple2: «, тест!(а.simple2) );
writeln( » файл.simple3: «, тест!(а.simple3) );
writeln( » файл.абстракт: «, тест!(а.абстракт) );
writeln( «файл.fnlNOver: «, тест!(а.fnlNOver) );
writeln( » б.simple1: «, тест!(б.simple1) );
writeln( » б.simple2: «, тест!(б.simple2) );
writeln( » б.абстракт: «, тест!(б.абстракт) );
writeln( » Си.абстракт: «, тест!(АР.абстракт) );
writeln( » я.абстракт: «, тест!(я.абстракт) );
writeln( » я.ФНЛ: «, тест!(я.нос) );
}

Итог
ИД Т М|В|А|Ф|И|О|
—————————
лямбда:? «М|» : «-|»;
рет ~= __черты нрава(isVirtualFunction,Т)? «Д» :
это( для вызова typeof(Т) == функция )?
Которые возвращают:

идентификатор primet один аргумент, возвращает строчку (аналогично .stringof)
getAliasThis — воспринимает Тип либо объектный Тип, раз Тип имеет этот псевдоним, возвращает их в виде кортежа из строк, по другому пустой кортеж (как я помню, в настоящее время существует лишь один псевдоним для этого типа)
getAttributes — воспринимает идентификатор, возвращает кортеж атрибутов, объявленных юзером (уда — определенные юзером атрибуты)
Примерперечислимого Фу;
бар класса { @(42) @Foo в пустоту клавишу func() незапятнанного @nogc @свойство {} }
прагма(глутамат натрия, __черты нрава(getAttributes, бар.клавишу func)); // кортеж(42, (Фу)), @nogc и @недвижимость не заходит в этот кортеж
@Фу float значение;
прагма(глутамат натрия, __черты нрава(getAttributes, значение)); // кортеж((Фу)), не работает лишь с функциями
Поддерживается чисто, nothrow, @nogc, параметре @Property, @система, @доверял, @безопасным и ref (раз функция возвращает ссылку), для class/struct как есть константный, постоянный, являющийся сразу входным и выходным, и общий. getFunctionAttributes primet функция, многофункциональный литерал, указатель на функцию, которая возвращает кортеж атрибутов как строчки (уда сюда не включены). Порядок зависит от определенной реализации, и на него нельзя полагаться.
Примерперечислимого Фу;
бар класса { @(42) @Foo в пустоту клавишу func() незапятнанного @nogc @свойство {} }
прагма(глутамат натрия, __черты нрава(getFunctionAttributes, бар.клавишу func)); // кортеж(«незапятнанный», «@nogc», «@недвижимость», «@система»)
getMember — воспринимает те же аргументы, что hasMember эквивалентен точечной нотации
Примеркласса бар { float значение; }
Бар Бар;
__черты нрава(getMember, бар, «значение») = 10; // так же как бар.значение = 10;
getOverloads — воспринимает класс/структура/модуля и строчки, соответственной имени функции снутри класса/структуры/модуль, возвращает кортеж все перегрузки эта функция
Примеримпорта ЗППП.потоки;

класс A
{
пустота фу( поплавок ) {}
пустота и Foo( строчка ) {}
фу типа int( Тип int ) { возвращение 12; }
}

пустота основной()
{
по каждому элементу( Ф; __черты нрава(getOverloads, а «фу») )
writeln( команда typeof(Ф).stringof );
}
Итог
недействительным(поплавок _param_0)
недействительными(строчка _param_0)
инт(инт _param_0)
getPointerBitmap — воспринимает Тип, возвращает массив типа size_t. 1-ое число-количество б, занимаемое объектом этого типа, 2-ой обрисовывает размещение указателей, управляемых сборщиком мусора снутри объекта данного типа
Примеркласса А
{
// указатель в таблице виртуальных функций, Размер 1 слово, управляемых с помощью ГХ: нет
// мониторинг, не отмечается, Размер 1, управляемого с помощью ГХ: нет
поплавок знач1; // Размер 1, ГХ: нет
В знач2; // Размер 1, ГХ: да
пустоту* знач3 собой; // Размер 1, ГХ: да
пустота[] val4; // Размер 2 {Размер ГХ: нет ГК указатель: да}
функция void() val5; // Размер 1, ГХ: нет
делегата недействительными() val6; // Размер 2 {контекст GC: да,функция ГК: нет}
}

перечисление БМ = 0b101011000;
// ||||||||+- указатель nableezy виртуальные функции
// |||||||+— указатель к монитору
// ||||||+— поплавок знач1
// |||||+—- В знач2
// ||||+—— пустоту* знач3 собой
// |||+—— пустота[] val4 Размер
// ||+——- пустота[] val4 индекс
// |+——— функция void() val5 индекс
// +——— делегата недействительными() val6 контекст
// 0———- делегата недействительными() val6 индекс
статический способ assert( __черты нрава(getPointerBitmap,А) == [10*Тип size_t.Размер БМ] );

структура Б { поплавок х, у, Z; }
статический способ assert( __черты нрава(getPointerBitmap,Б) == [3*поплавок.Размер, 0] ); // в структуре B нет указателей, управляемых сборщиком мусора
getProtection — воспринимает знак, возвращает строчку, варианты: «публичного», «личного», «защищенный», «экспорт» и «пакет»
getVirtualMethods — берет класс и строчку с именованием функции, которая работает практически как getOverloads, возвращает кортеж функций
getVirtualFunctions — что getVirtualMethods, за исключением того, что это включает в себя окончательную способности, не перегружать ничего
getUnitTests — воспринимает класс/структура/модуль, возвращает кортеж из unittests как статические функции, уда выручил
родитель — возвращает родительский знак передается для
функ;

пустота основной()
{
writeln( __черты нрава(родитель,writeln).stringof ); // потоки модуль
writeln( typeid( команда typeof( __черты нрава(родитель,Ф).значение ) ) ); // поплавка
} Примеримпорта ЗППП.потоки;

структура Б
{
значение типа float;
пустота клавишу func() {}
}

псевдоним Ф = в.
classInstanceSize — берет классе возвращает число байтов, занимаемых экземпляром класса
getVirtualIndex — воспринимает функцию (способ класса) возвращает индекс (типы ptrdiff_t) в таблице виртуальных функций класса. Раз функция является конечной и ничего не переопределяло вернет -1
allMembers — воспринимает тип и возвращает кортеж строк с именами всех полей и способов, без повторений и интегрированные характеристики (Размер, к примеру), для классов включает те же поля и способы из базисных классов
derivedMembers — воспринимает тип и возвращает кортеж строк с именами всех полей и способов, без повторений, без интегрированных параметров и без поля и способы базисного класса (для классов)

Сочетая это совместно с ограничениями подписи вы сможете сделать достойные внимания композиции перегруженных шаблонных функций:
импорт ЗППП.потоки;

пустота клавишу func(Т:долго)( Т Валь ) { writeln( «число» ); }
пустота клавишу func(Т: П[Е], у, Е)( Т Валь ) раз( заместо Е == строчка ) ) { writeln( «АА со строчкой key» ); }
пустота клавишу func(Т: П[Е], у, Е)( Т Валь ) раз( заместо Е : долго ) ) { writeln( «АА с кнопка num» ); }

пустота основной()
{
функ( 120 ); // число
клавишу func( [«привет»: 12] ); // АА строчку с ключом
функ( [10: 12] ); // АА ключ с блоком числовых
}

Обычная библиотека
В обычной библиотеке в почти все пакеты rackedny шаблона, который помогает проверить, соответствует ли Тип поведения (к примеру, нужные для работы с функциями в этом пакете). }

Но точно так же, в случае стандартизации есть формах, как дизайн является проверка неявные преобразования и даже сравнения с прототипом и спортсменов. Стандартизация и ограничение подписи
В простом выполнении шаблона функции смотрится так
пустота клавишу func(Т)( Т Валь ) { … Но есть пара пакетов, которые не реализуют какие-или особые функции, и обеспечивают комфортные обертки вокруг встроенными __черты нрава и доп методы проверки соответствия.

станд.черты нрава — включает в себя множество проверок и обертки
станд.typetuple шаблоны для типов кортежей
Может быть, в языке D реализована одна из самых гибких моделей для метапрограммирования. Лишь зарегистрированные юзеры могут участвовать в опросе. Мне нужна отдельная статья с примерами использования времени компиляции отражение Д’? Резюме
Комбинируя эти подходы, можно сделать невообразимо сложные и гибкие метапрограммы дизайн. Да, все время

Да, не нередко

Время от времени, я стараюсь избегать

Нет, это не для меня (мой ЯП не поддерживает это)

Проголосовали 24 человека. Да

Нет

Проголосовали 17 человек. Постоянно стараюсь поддерживать в чистоте и откомментировать сложные моменты. Используете ли Вы метапрограммирование? Пожалуйста, войдите. Воздержались 5 человек. habrahabr.ru Но постоянно помните, что кто-то может прочесть этот код (может быть даже себя) и осознать таковых структур будет очень проблематично. Воздержались 6 человек.