Я слышал, что язык D имеет мощные функции метапрограммирования для выполнения функций во время компиляции. Это звучит очень захватывающе, но мне трудно думать о практических примерах вещей, которые трудно сделать без них.
Кто-нибудь может привести примеры ситуаций, когда функции метапрограммирования D оказываются очень полезными?
Одним из действительно интересных и практичных способов использования выполнения функции во время компиляции является генерация кода во время компиляции, возможно, из файлов конфигурации или, возможно, из скриптов.
Вот простой пример обработки файла во время компиляции.
main.d
string make_ints(string s)
{
string ret = "";
foreach (varname; split(s))
ret ~= "int " ~ varname ~ "; ";
return ret;
}
void main()
{
mixin(make_ints(import("script")));
foo = 1;
bar = 2;
xyz = 3;
}
script
foo bar xyz
Во время компиляции файл «script» будет прочитан, разделен пробелами, а затем make_ints вернет int foo; int bar; int xyz;
прямо в код D, готовый к использованию этих переменных.
Хотя это бесполезный пример, вы можете легко увидеть, как его можно использовать для чтения значений из файла конфигурации (возможно, значений размера кеша или чего-то подобного). Игры могут использовать это для генерации необработанного кода D из сценариев, что будет иметь большое значение для производительности (обычно игры прибегают к использованию интерпретируемого кода для написания сценариев, что снижает производительность).
Вы также можете использовать это для автоматической настройки производительности. Допустим, у вас есть некоторая константа X, которую можно настроить, чтобы повлиять на производительность различными способами, но вы не знаете, какое значение X даст вам наилучшую производительность.Вы можете поместить X в файл, прочитать его во время компиляции для использования, попробовать некоторые другие значения во время выполнения и вернуть лучшее из них в файл. Таким образом, вы постепенно улучшаете производительность без необходимости делать что-либо вручную.
Если вам нужны практические примеры того, как Чтобы использовать возможности метапрограммирования D (CTFE, или оценка функции времени компиляции, это лишь одно из них, но даже не самое важное), не ищите ничего, кроме Phobos, стандартной библиотеки D2. Большая часть этого кода написана Андреем Александреску, который изобрел множество методов метапрограммирования шаблонов на C ++, и теперь работает с Уолтером Брайтом над дизайном и реализацией D.
Лучшими модулями для поиска являются std.range
и std.algorithm
. Они почти полностью состоят из шаблонов, были разработаны Андреем и удивительно удобочитаемы, учитывая количество используемых в них метапрограммирований. Я внес значительный вклад в оба этих модуля, и в основном я научился читать код, который был там, когда я начинал.
Весь код находится под (чрезвычайно разрешительной) лицензией Boost и может быть просмотрен прямо из вашего браузера на сайте Phobos Trac на dsource.org.
Чтобы дать вам представление о том, на что вы смотрите, средства метапрограммирования D в основном делятся на 4 категории:
Шаблоны , которые в основном похожи на шаблоны C ++, но с некоторыми добавленными функциями, такими как static if
, static assert
, вариативные шаблоны и ограничения, которые в основном похожи на концепции, но проще.
Размышление / самоанализ во время компиляции .Сюда входят встроенные выражения is ()
и __ traits
, а также стандартный библиотечный модуль std.traits.
Миксины . Они позволяют вам взять либо шаблон (миксины шаблонов), либо строку времени компиляции (миксины строк) и оценить их как код в текущей области. Строковые миксины можно рассматривать как нечто вроде оператора eval, за исключением того, что строка оценивается как код во время компиляции, а не во время выполнения.
Оценка функции времени компиляции, или CTFE , который позволяет выполнять оценку функций, соответствующих определенным критериям, во время компиляции. Одним из важных применений CTFE является то, что в сочетании со строковыми миксинами вы можете сгенерировать код в виде строки во время компиляции, а затем оценить его как код в области, в которой встречается оператор mixin
. Примеры этого см. В std.range.Lockstep и std.range.OutputRangeObject, которые я недавно проверил в SVN-версиях Phobos.
Я плохо разбираюсь в целях метапрограммирования. Вот мой взгляд на три основные конструкции, найденные в D.
Наиболее интересное практическое применение, о котором я знаю, это библиотека, которую я написал*
для использования средства проверки типов для обеспечения безопасности единиц (то есть запрещающего добавление расстояний в разы и обеспечивая правильное преобразование при добавлении метров в футы). То же самое можно сделать и было сделано в C++, но сделать это в D во время компиляции немного сложнее, чем во время выполнения.
*
извините за беспардонный плаг.