Метапрограммирование — это метод компьютерного программирования , при котором компьютерные программы могут обрабатывать другие программы как свои данные . Это означает, что программа может быть разработана для чтения, генерации, анализа или преобразования других программ и даже для изменения самой себя во время работы. [1] [2] В некоторых случаях это позволяет программистам минимизировать количество строк кода для выражения решения, что в свою очередь сокращает время разработки. [3] Это также обеспечивает программам большую гибкость для эффективной обработки новых ситуаций без перекомпиляции.
Метапрограммирование может использоваться для перемещения вычислений из времени выполнения во время компиляции , для генерации кода с использованием вычислений во время компиляции и для включения самомодифицирующегося кода . Способность языка программирования быть своим собственным метаязыком позволяет осуществлять рефлексивное программирование и называется рефлексией . [4] Рефлексия — ценная языковая функция, облегчающая метапрограммирование.
Метапрограммирование было популярно в 1970-х и 1980-х годах с использованием языков обработки списков, таких как Lisp . Аппаратное обеспечение машины Lisp получило некоторое внимание в 1980-х годах и позволило приложениям, которые могли обрабатывать код. Они часто использовались для приложений искусственного интеллекта .
Метапрограммирование позволяет разработчикам писать программы и разрабатывать код, который подпадает под парадигму обобщенного программирования . Наличие самого языка программирования в качестве типа данных первого класса (как в Lisp , Prolog , SNOBOL или Rebol ) также очень полезно; это известно как гомоиконичность . Обобщенное программирование вызывает средство метапрограммирования в языке, позволяя писать код, не заботясь об указании типов данных, поскольку они могут быть предоставлены в качестве параметров при использовании.
Метапрограммирование обычно работает одним из трех способов. [5]
Lisp , вероятно, является наиболее существенным языком с возможностями метапрограммирования, как из-за его исторического прецедента, так и из-за простоты и мощности его метапрограммирования. В метапрограммировании Lisp оператор снятия кавычек (обычно запятая) вводит код, который вычисляется во время определения программы , а не во время выполнения. Таким образом, язык метапрограммирования идентичен основному языку программирования, и существующие процедуры Lisp могут быть напрямую повторно использованы для метапрограммирования, если это необходимо. Этот подход был реализован в других языках путем включения интерпретатора в программу, который работает напрямую с данными программы. Существуют реализации такого рода для некоторых распространенных языков высокого уровня, таких как RemObjects ' Pascal Script for Object Pascal .
Простым примером метапрограммы является этот скрипт оболочки POSIX , который является примером генеративного программирования :
#!/bin/sh # метапрограмма echo '#!/bin/sh' > программа for i in $( 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 , R , SAS , Smalltalk и Tcl ), то можно использовать методы для выполнения метапрограммирования без генерации исходного кода.
Один из стилей генеративного подхода заключается в использовании предметно-ориентированных языков (DSL). Довольно распространенный пример использования DSL включает генеративное метапрограммирование: lex и yacc , два инструмента, используемые для генерации лексических анализаторов и парсеров , позволяют пользователю описывать язык с помощью регулярных выражений и контекстно-свободных грамматик , а также встраивать сложные алгоритмы, необходимые для эффективного синтаксического анализа языка.
Одним из применений метапрограммирования является инструментирование программ с целью проведения динамического анализа программ .
Некоторые утверждают, что для полного использования возможностей метапрограммирования требуется крутая кривая обучения. [8] Поскольку метапрограммирование обеспечивает большую гибкость и настраиваемость во время выполнения, неправильное или неправильное использование метапрограммирования может привести к необоснованным и неожиданным ошибкам, которые может быть чрезвычайно сложно отладить среднему разработчику. Это может привести к рискам в системе и сделать ее более уязвимой, если не использовать ее с осторожностью. Некоторые из распространенных проблем, которые могут возникнуть из-за неправильного использования метапрограммирования, — это неспособность компилятора определить отсутствующие параметры конфигурации, недействительные или неверные данные могут привести к неизвестному исключению или другим результатам. [9] В связи с этим некоторые считают [8] , что только высококвалифицированные разработчики должны работать над разработкой функций, которые реализуют метапрограммирование на языке или платформе, а средние разработчики должны научиться использовать эти функции как часть соглашения.
IBM /360 и производные имели мощные возможности макроассемблера , которые часто использовались для генерации полных программ на языке ассемблера [ требуется ссылка ] или разделов программ (например, для различных операционных систем). Макросы, предоставляемые системой обработки транзакций CICS , имели макросы ассемблера, которые генерировали операторы COBOL в качестве шага предварительной обработки.
Другие ассемблеры, такие как MASM , также поддерживают макросы.
Метаклассы предоставляются следующими языками программирования:
Использование зависимых типов позволяет доказать, что сгенерированный код никогда не является недействительным. [15] Однако этот подход является передовым и редко встречается за пределами исследовательских языков программирования.
Список известных систем метапрограммирования можно найти в разделе Список систем преобразования программ .