Метапрограммирование — это метод программирования, при котором компьютерные программы имеют возможность обращаться с другими программами как со своими данными. Это означает, что программа может быть спроектирована так, чтобы читать, генерировать, анализировать или преобразовывать другие программы и даже изменять себя во время работы. [1] [2] В некоторых случаях это позволяет программистам минимизировать количество строк кода для выражения решения, что, в свою очередь, сокращает время разработки. [3] Это также дает программам большую гибкость для эффективной обработки новых ситуаций без перекомпиляции.
Метапрограммирование можно использовать для перемещения вычислений из времени выполнения во время компиляции , для генерации кода с использованием вычислений во время компиляции и для включения самомодифицирующегося кода . Способность языка программирования быть собственным метаязыком называется рефлексией . [4] Рефлексия — ценная функция языка, облегчающая метапрограммирование.
Метапрограммирование было популярно в 1970-х и 1980-х годах с использованием языков обработки списков, таких как LISP . Аппаратные машины LISP были популярны в 1980-х годах и позволяли использовать приложения, способные обрабатывать код. Их часто использовали в приложениях искусственного интеллекта .
Метапрограммирование позволяет разработчикам писать программы и разрабатывать код, который подпадает под общую парадигму программирования . Наличие самого языка программирования как первоклассного типа данных (как в Lisp , Prolog , SNOBOL или Rebol ) также очень полезно; это известно как гомоиконичность . Общее программирование задействует средства метапрограммирования внутри языка, позволяя писать код, не заботясь об указании типов данных, поскольку при использовании они могут быть предоставлены в качестве параметров .
Метапрограммирование обычно работает одним из трех способов. [5]
Лисп , вероятно, является наиболее существенным языком с возможностями метапрограммирования, как из-за его исторического преобладания, так и из-за простоты и мощи его метапрограммирования. В метапрограммировании на Лиспе оператор отмены кавычек (обычно запятая) вводит код, который оценивается во время определения программы, а не во время выполнения; см. Самооценочные формы и цитирование в Lisp . Таким образом, язык метапрограммирования идентичен основному языку программирования, и существующие подпрограммы Lisp при желании могут быть напрямую повторно использованы для метапрограммирования. Этот подход был реализован на других языках путем включения в программу интерпретатора, который работает непосредственно с данными программы. Существуют реализации такого рода для некоторых распространенных языков высокого уровня, таких как Pascal Script RemObjects для Object Pascal .
Простым примером метапрограммы является этот сценарий POSIX Shell , который является примером генеративного программирования :
#!/bin/sh # метапрограмма echo '#!/bin/sh' > программа для i в $( seq 992 ) do echo "echo $i " >> программа завершена
chmod +x программа
Этот сценарий (или программа) генерирует новую программу из 993 строк, которая печатает числа от 1 до 992. Это всего лишь иллюстрация того, как использовать код для написания большего количества кода; это не самый эффективный способ распечатать список чисел. Тем не менее, программист может написать и выполнить эту метапрограмму менее чем за минуту и за это время сгенерирует более 1000 строк кода.
Куайн — это особый вид метапрограммы , которая на выходе создает собственный исходный код. Куайны обычно представляют только развлекательный или теоретический интерес.
Не все метапрограммирование включает в себя генеративное программирование. Если программы можно изменять во время выполнения или доступна инкрементальная компиляция (например, в C# , Forth , Frink , Groovy , JavaScript , Lisp , Elixir , Lua , Nim , Perl , PHP , Python , REBOL , Ruby , Rust , SAS , Smalltalk , и Tcl ), тогда можно использовать методы метапрограммирования без фактического создания исходного кода.
Одним из стилей генеративного подхода является использование предметно-ориентированных языков (DSL). Довольно распространенный пример использования DSL включает генеративное метапрограммирование: lex и yacc , два инструмента, используемых для создания лексических анализаторов и синтаксических анализаторов , позволяют пользователю описывать язык с помощью регулярных выражений и контекстно-свободных грамматик , а также внедрять сложные алгоритмы, необходимые для эффективного анализа язык.
Одним из применений метапрограммирования является инструментирование программ для проведения динамического анализа программ .
Некоторые утверждают, что для того, чтобы в полной мере использовать возможности метапрограммирования, необходимо резкое обучение. [8] Поскольку метапрограммирование обеспечивает большую гибкость и настраиваемость во время выполнения, неправильное использование или неправильное использование метапрограммирования может привести к необоснованным и неожиданным ошибкам, которые обычному разработчику может быть чрезвычайно сложно отладить. Это может привести к возникновению рисков в системе и сделать ее более уязвимой, если не использовать ее с осторожностью. Некоторые из распространенных проблем, которые могут возникнуть из-за неправильного использования метапрограммирования, - это неспособность компилятора определить недостающие параметры конфигурации, неверные или неправильные данные могут привести к неизвестному исключению или другим результатам. [9] В связи с этим некоторые считают [8] , что только высококвалифицированные разработчики должны работать над разработкой функций, реализующих метапрограммирование на языке или платформе, а средние разработчики должны научиться использовать эти функции в рамках соглашения.
IBM /360 и его производные имели мощные средства ассемблера макросов , которые часто использовались для создания полных программ на языке ассемблера или разделов программ (например, для разных операционных систем) . Макросы, поставляемые с системой обработки транзакций CICS , имели макросы ассемблера, которые генерировали инструкции COBOL в качестве этапа предварительной обработки.
Другие ассемблеры, такие как MASM , также поддерживают макросы.
Метаклассы предоставляются следующими языками программирования:
Использование зависимых типов позволяет доказать, что сгенерированный код никогда не является невалидным. [15] Однако этот подход является новейшим и редко встречается за пределами исследовательских языков программирования.
Список известных систем метапрограммирования поддерживается в Списке систем преобразования программ .