Является ли генерация исходного кода анти-паттерном?


118

Если что-то может быть сгенерировано, то это данные, а не код.

Учитывая это, не является ли вся эта идея генерации исходного кода недоразумением? То есть, если для чего-то есть генератор кода, то почему бы не сделать это чем-то надлежащим образом, чтобы оно могло получать требуемые параметры и выполнять правильные действия, которые мог бы выполнить «сгенерированный» код?

Если это делается по соображениям производительности, то это звучит как недостаток компилятора.

Если это делается для объединения двух языков, то это звучит как отсутствие интерфейса библиотеки.

Я что-то здесь упускаю?

Я знаю, что код тоже данные. Что я не понимаю, зачем генерировать исходный код ? Почему бы не превратить это в функцию, которая может принимать параметры и воздействовать на них?


11
Термин, связанный с генерацией кода - это метапрограммирование
UselesssCat

4
en.wikipedia.org/wiki/Code_as_data , Lisp, FP, сценарии, метапрограммирование, фон Нейман / модифицированная гарвардская архитектура и т. д. Это было рассмотрено до тошноты . Различия «исходный код» и «выходной код», «код» и «данные» и т. д. предназначены для упрощения вещей. Они никогда не должны быть догматичными .
vaxquis

9
@ Utku, лучшие причины для генерации кода часто связаны с желанием предоставить описание более высокого уровня, чем может выразить ваш текущий язык . Может ли компилятор создавать эффективный код или нет, он не имеет к этому никакого отношения. Рассмотрим генераторы синтаксического анализатора - лексер, сгенерированный flexили синтаксический анализатор, сгенерированный с помощью bisonпочти наверняка будет более предсказуемым, более правильным и часто более быстрым в выполнении, чем эквиваленты, написанные от руки на C; и построен из гораздо меньшего количества кода (таким образом, также меньше работы для обслуживания).
Чарльз Даффи

1
Возможно, вы пришли из языка, который не имеет много функциональных элементов, но во многих языках функции являются первоклассными - вы можете передавать их, поэтому в этих типах языков код - это данные, и вы можете обращаться с ними именно так.
Restioson

1
@ Restioson в функциональном коде языка не является данными. Функции первого класса означают именно это: функции являются данными. И не обязательно особенно хорошие данные: вы не можете изменить их немного (например, преобразовать все добавления в функциях в вычитания, скажем). Код - это данные на гомойских языках. (У большинства гомойских языков есть функции первого класса. Но обратное неверно.).
Линдон Уайт

Ответы:


150

Является ли генерация исходного кода анти-паттерном?

Технически, если мы генерируем код, он не является источником, даже если это текст, который читается человеком. Исходный код - это оригинальный код, созданный человеком или другим истинным интеллектом, механически не переведенный и не воспроизводимый немедленно из (истинного) источника (прямо или косвенно).

Если что-то может быть сгенерировано, то это данные, а не код.

Я бы сказал, что все равно данные . Даже исходный код. Особенно исходный код! Исходный код - это просто данные на языке, предназначенном для выполнения задач программирования. Эти данные должны быть переведены, интерпретированы, скомпилированы, сгенерированы, при необходимости, в другие формы - данных - некоторые из которых оказываются исполняемыми.

Процессор выполняет инструкции из памяти. Та же самая память, которая используется для данных. Перед тем, как процессор выполнит инструкции, программа загружается в память как данные .

Итак, все данные , даже код .

Учитывая, что [сгенерированный код является данными], не является ли вся эта идея генерации кода неправильным пониманием?

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

То есть, если для чего-то есть генератор кода, то почему бы не сделать это чем-то надлежащим образом, чтобы оно могло получать требуемые параметры и выполнять правильные действия, которые мог бы выполнить «сгенерированный» код?

Это один из способов, но есть и другие.


Результатом генерации кода является текст, который предназначен для использования человеком.

Не все текстовые формы предназначены для потребления человеком. В частности, сгенерированный код (как текст) обычно предназначен для потребления компилятором, а не для использования человеком.


Исходный код считается оригинальным: мастер - что мы редактируем и разрабатываем; что мы архивируем, используя контроль исходного кода. Сгенерированный код, даже когда текст читается человеком, обычно восстанавливается из исходного исходного кода . Вообще говоря, сгенерированный код не должен находиться под контролем исходного кода, поскольку он восстанавливается во время сборки.


1
Комментарии не для расширенного обсуждения; этот разговор был перемещен в чат .
maple_shaft

65

Практические рассуждения

Хорошо, я знаю, что код тоже данные. Что я не понимаю, зачем генерировать исходный код?

Из этого редактирования я предполагаю, что вы спрашиваете на довольно практическом уровне, а не на теоретической информатике.

Классическая причина генерации исходного кода на статических языках, таких как Java, заключалась в том, что в таких языках просто не было простых в использовании языковых инструментов для создания очень динамичных вещей. Например, еще в дни становления Java просто не было возможности легко создать класс с динамическим именем (сопоставление имени таблицы из БД) и динамическими методами (сопоставление атрибутов из этой таблицы) с динамическими типами данных (сопоставление типы указанных атрибутов). Тем более, что Java придает большое значение, а может быть, и гарантирует возможность распознавать ошибки типов во время компиляции.

Таким образом, при такой настройке программист может только создавать код Java и писать много строк кода вручную. Часто программист обнаруживает, что всякий раз, когда таблица изменяется, он должен вернуться и изменить код для соответствия; и если он забудет об этом, случатся плохие вещи. Следовательно, программист доходит до того, что он пишет некоторые инструменты, которые делают это для него. И, следовательно, дорога к все более интеллектуальному генерированию кода.

(Да, вы можете сгенерировать байт-код на лету, но программирование такой вещи на Java не было бы чем-то, что случайный программист делал бы просто между написанием нескольких строк кода домена.)

Сравните это с очень динамичными языками, например, Ruby, который я в большинстве случаев считаю противоположностью Java (обратите внимание, что я говорю это, не оценивая ни один из подходов; они просто разные). Здесь на 100% нормально и стандартно динамически генерировать классы, методы и т. Д. Во время выполнения, и, самое главное, программист может делать это тривиально прямо в коде, не переходя на «мета» уровень. Да, такие вещи, как Ruby on Rails, сопровождаются генерацией кода, но в нашей работе мы обнаружили, что мы в основном используем это как своего рода расширенный «режим обучения» для новых программистов, но через некоторое время это становится излишним (так как кода так мало написать в этой экосистеме, что, когда вы знаете, что делаете, написание этого вручную становится быстрее, чем очистка сгенерированного кода).

Это всего лишь два практических примера из «реального мира». Тогда у вас есть языки, такие как LISP, где код буквально является данными. С другой стороны, в скомпилированных языках (без движка времени выполнения, таких как Java или Ruby), (или я не следил за современными возможностями C ++ ...) просто нет концепции определения имен классов или методов во время выполнения, поэтому генерация кода процесс сборки является инструментом выбора для большинства вещей (другие, более специфичные для C / C ++ примеры, такие как flex, yacc и т. д.).


1
Я думаю, что это лучше, чем ответы с большим количеством голосов. В частности, пример, упомянутый с Java и программированием базы данных, намного лучше справляется с решением, почему используется генерация кода и является допустимым инструментом.
Panzercrisis

В наши дни возможно ли в Java создавать динамические таблицы из БД? Или только с помощью ORM?
Ноумен

«(или я не поспевал за современными возможностями C ++ ...)», несомненно, это стало возможным в C ++ более двух десятилетий благодаря указателям на функции? Я не проверял это, но я уверен, что должно быть возможно выделить массив символов, заполнить его машинным кодом, а затем привести указатель на первый элемент к указателю на функцию и затем запустить его? (Предполагая, что целевая платформа не имеет каких-либо мер безопасности, чтобы помешать вам делать это, что вполне может быть.)
Pharap

1
msgstr "выделить массив символов, заполнить его машинным кодом, а затем привести указатель на первый элемент к указателю на функцию и затем запустить его?" Помимо неопределенного поведения, это C ++ эквивалент «генерации байт-кода на лету». Он попадает в ту же категорию «не рассматривается обычными программистами»
Caleth

1
@Pharap, «конечно, это было возможно в C ++ более двух десятилетий» ... Мне пришлось немного посмеяться; Прошло около двух десятилетий с тех пор, как я последний раз программировал C ++. :) Но мое предложение о C ++ все равно было сформулировано плохо. Я немного изменил это, теперь должно быть понятнее, что я имел в виду.
Anoe

44

зачем генерировать код?

Потому что программирование перфокартами (или альтернативными кодами в блокноте ) - это боль.

Если это делается по соображениям производительности, то это звучит как недостаток компилятора.

Правда. Я не забочусь о производительности, если я не вынужден.

Если это делается для объединения двух языков, то это звучит как отсутствие интерфейса библиотеки.

Хмм, понятия не имею, о чем ты.

Похоже, это так: генерируемый и сохраняемый исходный код всегда и навсегда является болью в заднице. Он существует только по одной причине. Кто-то хочет работать на одном языке, в то время как кто-то настаивает на работе на другом, и никто не может быть обеспокоен, чтобы выяснить, как взаимодействовать между ними, поэтому один из них выясняет, как превратить свой любимый язык в навязанный язык, чтобы они могли делать то, что они хотят.

Что хорошо, пока я не буду поддерживать это. В этот момент вы все можете умереть.

Это анти паттерн? Вздох нет Многие языки даже не существовали бы, если бы мы не были готовы попрощаться с недостатками предыдущих языков, а генерация кода старых языков - это то, сколько новых языков начинаются.

Это кодовая база, оставленная в полуконвертированном лоскутном одеянии монстра Франкенштейна, которое я терпеть не могу. Сгенерированный код является неприкасаемым кодом. Я ненавижу смотреть на неприкасаемый код. Все же люди продолжают проверять это. ПОЧЕМУ? Вы также можете проверить исполняемый файл.

Ну, теперь я ругаю. Я хочу сказать, что мы все "генерируем код". Когда вы относитесь к сгенерированному коду как к исходному, вы сводите меня с ума. Просто потому, что он выглядит так, как будто исходный код не делает его исходным кодом.


41
Если вы его сгенерируете, это не SOURCE код. Это промежуточный код. Я сейчас пойду плакать.
candied_orange

65
ARG !!! Неважно, как это выглядит !!! Текст, двоичный код, ДНК, если это не ИСТОЧНИК, это не то, к чему следует прикасаться при внесении изменений. Ничего страшного, если мой процесс компиляции имеет 42 промежуточных языка, через которые он проходит. Хватит их трогать. Прекратите регистрировать их. Внесите изменения в источнике.
candied_orange

24
XML - это текст, и он явно не предназначен для потребления человеком. :-)
Ник Кейли,

38
@utku: «Если что-то не предназначено для потребления человеком, это не должно быть текстом»: я совершенно не согласен. Некоторые контрпримеры в моей голове: протокол HTTP, кодировки MIME, файлы PEM - почти все, что везде использует base64. Существует множество причин для кодирования данных в 7-битный безопасный поток, даже если никто не должен его видеть. Не говоря уже о гораздо большем пространстве вещей, с которым обычно человек никогда не должен взаимодействовать, но о том, что они могут захотеть время от времени: файлы журналов, /etc/файлы в Unix и т. Д.
Даниэль Приден,

12
Я не думаю, что «программирование с помощью перфокарт» означает то, что вы думаете, это значит. Я был там, я сделал это, и да, это была боль; но он не имеет никакого отношения к «сгенерированному коду». Колода перфокарт - это просто другой вид файла - наподобие файла на диске, или файла на ленте, или файла на SD-карте. Раньше мы записывали данные в колоды карт и считывали данные с них. Итак, если причина, по которой мы генерируем код, заключается в том, что программирование с использованием перфокарт - это боль, то это означает, что программирование с любым видом хранения данных - это боль.
Соломон Слоу

41

зачем генерировать исходный код

Наиболее частым вариантом использования генераторов кода, с которыми мне приходилось работать в своей карьере, были генераторы, которые

  • принял в качестве входных данных некоторое мета-описание высокого уровня для некоторой модели данных или схемы базы данных (может быть, реляционная схема или какая-то схема XML)

  • и произвел CRUD-код стандартной схемы для классов доступа к данным в качестве выходных данных и, возможно, дополнительных вещей, таких как соответствующие SQL или документация.

Преимущество здесь в том, что из одной строки краткой спецификации ввода вы получаете от 5 до 10 строк отлаживаемого, безопасного с точки зрения типов, безошибочного (предполагается, что вывод генератора кода является зрелым) кода, который в противном случае вам пришлось бы реализовывать и поддерживать вручную. Вы можете себе представить, насколько это уменьшает усилия по обслуживанию и развитию.

Позвольте мне также ответить на ваш первоначальный вопрос

Является ли генерация исходного кода анти-паттерном?

Нет, не генерация исходного кода как таковая, но действительно есть некоторые подводные камни. Как сказано в Pragmatic Programmer , следует избегать использования генератора кода, когда он генерирует код, который трудно понять . В противном случае возросшие усилия по использованию или отладке этого кода могут легко перевесить сэкономленные усилия, если не писать код вручную.

Я также хотел бы добавить, что в большинстве случаев хорошей идеей является физическое отделение сгенерированных частей кода от кода, написанного вручную, таким образом, чтобы повторная генерация не перезаписывала какие-либо изменения вручную. Тем не менее, я также неоднократно сталкивался с ситуацией, когда задача состояла в том, чтобы перенести некоторый код, написанный на старом языке X, на другой, более современный язык Y, с намерением впоследствии перейти на обслуживание на языке Y. Это допустимое использование чехол для одноразовой генерации кода.


Я согласен с этим ответом. Используя что-то вроде Torque для Java, я могу автоматически генерировать исходные файлы Java с полями, соответствующими базе данных SQL. Это делает грубые операции намного проще. Основным преимуществом является безопасность типов, в том числе возможность ссылаться только на поля, существующие в базе данных (спасибо, автозаполнение).
MTilsted

Да, для статически типизированных языков это важная часть: вы можете убедиться, что рукописный код действительно соответствует сгенерированному.
Паŭло Эберманн

«перенести некоторый код, написанный на старом языке» - даже в этом случае одноразовая генерация кода может быть большой болью. Например, после некоторых ручных изменений вы обнаруживаете ошибку в генераторе и вам необходимо повторить генерацию после исправления. К счастью, мерзавец или похожий может обычно облегчить боль.
Маартин

13

зачем генерировать исходный код?

Я столкнулся с двумя вариантами использования для сгенерированного (во время сборки и никогда не проверенного) кода:

  1. Автоматически генерировать шаблонный код, такой как getts / setters, toString, equals и hashCode, из языка, созданного для определения таких вещей (например, проект lombok для Java)
  2. Автоматически генерировать классы типов DTO из некоторой спецификации интерфейса (REST, SOAP и т. Д.) Для последующего использования в основном коде. Это похоже на проблему вашего языкового моста, но в итоге получается чище и проще, с лучшей обработкой типов, чем попытка реализовать то же самое без сгенерированных классов.

15
Очень повторяющийся код на невыразительных языках. Например, мне пришлось написать код, который должен был сделать то же самое на многих похожих, но не идентичных структурах данных. Это, вероятно, могло бы быть сделано с чем-то вроде шаблона C ++ (эй, разве это не генерация кода?). Но я использовал C. Генерация кода спасла меня от написания множества практически идентичных кодов.
Ник Кейли

1
@NickKeighley Возможно, ваша цепочка инструментов не позволяла вам использовать другой более подходящий язык?
Уилсон

7
Обычно вы не можете выбирать язык реализации. Проект был на C, это не вариант.
Ник Кейли

1
@ Уилсон: более выразительные языки часто используют генерацию кода (например, макросы lisp, ruby ​​on rails), они просто не требуют, чтобы их тем временем сохраняли в виде текста.
Пит Киркхэм

4
Да, генерация кода - это, по сути, метапрограммирование. Такие языки, как Ruby, позволяют вам выполнять мета-программирование на самом языке, но C этого не делает, поэтому вместо этого вам придется использовать генерацию кода.
Шон Бертон,

13

Сассманн много интересного говорил о таких вещах в своей классической «Структуре и интерпретации компьютерных программ», главным образом о двойственности кода и данных.

Для меня основным применением генерации кода adhoc является использование доступного компилятора для преобразования небольшого предметно-ориентированного языка в то, что я могу связать в своих программах. Думайте BNF, думайте ASN1 (на самом деле, нет, это некрасиво), думайте электронные таблицы словаря данных.

Тривиальные доменные языки могут значительно сэкономить время, и вывод чего-то, что может быть скомпилировано стандартными языковыми инструментами, - это способ создания таких вещей, которые вы бы предпочли отредактировать, нетривиальный взломанный парсер на любом родном языке. писать, или BNF для автоматически сгенерированного?

Выводя текст, который затем подается в какой-то системный компилятор, я получаю всю эту оптимизацию компиляторов и специфичные для системы конфигурации без необходимости думать об этом.

Я эффективно использую язык ввода компилятора как еще одно промежуточное представление, в чем проблема? Текстовые файлы не являются исходным кодом, они могут быть IR для компилятора , и если они выглядят как C или C ++ или Java или что-то еще, кого это волнует?

Теперь, если вам трудно думать, что вы можете отредактировать OUTPUT парсера языка игрушек, что явно разочарует, когда в следующий раз кто-то отредактирует файлы входного языка и перестроит, ответ - не фиксировать автоматически сгенерированный IR в репо. генерируется вашим инструментарием (и избегайте таких людей в вашей группе разработчиков, они, как правило, счастливее работать в маркетинге).

Это не столько недостаток выразительности в наших языках, сколько выражение того, что иногда вы можете получить (или массировать) части спецификации в форму, которую можно автоматически преобразовать в код, и которая, как правило, порождает гораздо меньше ошибки и быть гораздо проще в обслуживании. Если я могу дать нашим сотрудникам по тестированию и настройке электронную таблицу, которую они могут настроить, и инструмент, который они затем запустят, который берет эти данные и выкладывает полный шестнадцатеричный файл для флэш-памяти на моем ECU, тогда это огромная экономия времени, если кто-то переводит вручную последняя настройка в набор констант на языке дня (в комплекте с опечатками).

То же самое с построением моделей в Simulink, а затем генерацией C с RTW и последующей компиляцией с использованием любого инструмента, который имеет смысл, промежуточный C не читается, и что? Высокоуровневому материалу Matlab RTW нужно знать только подмножество C, а компилятор C заботится о деталях платформы. Единственный раз, когда человеку приходится разбираться в сгенерированном C, это когда в скриптах RTW есть ошибка, и такие вещи гораздо легче отлаживать с помощью номинально читаемого IR, чем с помощью только двоичного дерева разбора.

Конечно, вы можете писать такие вещи для вывода байт-кода или даже исполняемого кода, но зачем вам это делать? У нас есть инструменты для преобразования IR в эти вещи.


Это хорошо, но я бы добавил, что при определении того, какой IR использовать, есть компромисс: использование C в качестве IR делает некоторые вещи проще, а другие - сложнее по сравнению, скажем, с языком ассемблера x86. Выбор становится еще более значимым при выборе, скажем, кода языка Java и байт-кода Java, поскольку существует гораздо больше операций, которые существуют только на одном или другом языке.
Даниэль Приден

2
Но язык ассемблера X86 делает плохой IR при нацеливании на ядро ​​ARM или PPC! Все вещи являются компромиссом в области машиностроения, поэтому они называют это инженерией. Можно было бы надеяться, что возможности байт-кода Java были строгим расширенным набором возможностей языка Java, и что это, как правило, верно, когда вы приближаетесь к металлу независимо от цепочки инструментов и от того, где вы вводите IR.
Дэн Миллс

О, я полностью согласен: мой комментарий был в ответ на ваш последний абзац, спрашивающий, почему вы когда-либо выводили байт-код или какую-то вещь более низкого уровня - иногда вам нужен более низкий уровень. (В Java, в частности, есть много полезных вещей, которые вы можете сделать с помощью байт-кода, которые вы не можете сделать на самом языке Java.)
Даниэль Приден,

2
Я не согласен, но использование IR ближе к металлу обходится дорого, не только в уменьшенной общности, но и в том, что вы обычно несете ответственность за большую часть действительно раздражающей низкоуровневой оптимизации. Тот факт, что в наши дни мы обычно думаем об оптимизации выбора алгоритма, а не о реализации, является отражением того, как далеко продвинулись компиляторы, иногда вам приходится подходить очень близко к металлу в этих вещах, но подумайте дважды, прежде чем выбросить компиляторы способность оптимизировать, используя слишком низкий уровень ИК.
Дэн Миллс

1
«Они обычно счастливее, работая в маркетинге», Кэтти, но смешно.
dmckee

13

Прагматичный ответ: является ли генерация кода необходимой и полезной? Предоставляет ли это что-то действительно полезное и необходимое для проприетарной кодовой базы, или кажется, что оно просто создает другой способ работы таким образом, который увеличивает интеллектуальные издержки для неоптимальных результатов?

Хорошо, я знаю, что код тоже данные. Что я не понимаю, зачем генерировать код? Почему бы не превратить это в функцию, которая может принимать параметры и воздействовать на них?

Если вам нужно задать этот вопрос, и нет четкого ответа, то, вероятно, генерация кода является излишней и просто вносит экзотику и большую интеллектуальную нагрузку на вашу кодовую базу.

Между тем, если вы берете что-то вроде OpenShadingLanguage: https://github.com/imageworks/OpenShadingLanguage

... тогда такие вопросы ставить не нужно, поскольку на них сразу же отвечают впечатляющие результаты.

OSL использует среду компилятора LLVM для преобразования сетей шейдеров в машинный код на лету (как раз вовремя, или «JIT»), и в процессе сильно оптимизирует шейдеры и сети с полным знанием параметров шейдеров и других значений времени выполнения, которые не могли было известно, когда шейдеры были скомпилированы из исходного кода. В результате мы видим, что наши сети шейдеров OSL работают на 25% быстрее, чем эквивалентные шейдеры, созданные вручную в C! (Вот как наши старые шейдеры работали в нашем рендерере.)

В таком случае вам не нужно подвергать сомнению существование генератора кода. Если вы работаете в этом типе домена VFX, то ваш немедленный ответ обычно звучит примерно так: «Замолчи и возьми мои деньги!» или «вау, нам тоже нужно сделать что-то подобное».


переводить шейдерные сети в машинный код . Это похоже на компилятор, а не генератор кода, не так ли?
Утку

2
Это в основном занимает узловую сеть, которую пользователь подключает, и генерирует промежуточный код, который компилируется JIT LLVM. Различие между компилятором и генератором кода довольно размыто. Вы больше думали о возможностях генерации кода в таких языках, как шаблоны в C ++ или препроцессоре C?

Я думал о любом генераторе, который выводил бы исходный код.
Утку

Я вижу, где выход все еще для потребления человеком, я предполагаю. OpenSL также генерирует промежуточный исходный код, но это низкоуровневый код, близкий к сборке для потребления LLVM. Обычно это не код, который должен поддерживаться (вместо этого программисты поддерживают узлы, используемые для генерации кода). Большую часть времени я думаю, что такие типы генераторов кода скорее всего будут злоупотреблять, чем достаточно полезны, чтобы оправдать их ценность, особенно если вам приходится постоянно обновлять код как часть процесса сборки. Иногда у них все еще есть подлинное место, чтобы устранить недостатки ...

... языков, доступных для определенного домена. У QT есть один из тех спорных, с его мета-объектным компилятором (MOC). MOC уменьшает базовый шаблон, который обычно требуется предоставить для свойств, отражений, сигналов и слотов и т. Д. В C ++, но не в такой степени, чтобы четко оправдать его существование. Я часто думаю, что QT мог бы быть лучше без громоздкого бремени генерации кода MOC.

8

Нет, генерация промежуточного кода не является анти-паттерном. Ответ на другую часть вашего вопроса, «Зачем это делать?», Является очень широким (и отдельным) вопросом, хотя я все равно приведу некоторые причины.

Исторические последствия отсутствия промежуточного читабельного кода

Давайте возьмем C и C ++ в качестве примеров, поскольку они являются одними из самых известных языков.

Вы должны заметить, что логическая последовательность компиляции кода C выводит не машинный код, а скорее читаемый человеком код сборки. Аналогично, старые компиляторы C ++ раньше физически компилировали код C ++ в код C. В этой цепочке событий вы можете скомпилировать из читаемого человеком кода 1 в читаемый человеком код 2 в читаемый человеком код 3 для машинного кода. "Почему?" Почему нет?

Если промежуточный, читаемый человеком код никогда не генерировался, мы могли бы вообще не иметь C или C ++. Это, безусловно, возможность; люди идут по пути наименьшего сопротивления своим целям, и если какой-то другой язык наберет обороты первым из-за стагнации развития C, C мог бы умереть, пока он был еще молод. Конечно, вы могли бы возразить: «Но тогда, возможно, мы будем использовать какой-то другой язык, и, возможно, это будет лучше». Может быть, а может и хуже будет. Или, может быть, мы все еще будем писать на ассемблере.

Зачем использовать промежуточный человекочитаемый код?

  1. Иногда требуется промежуточный код, чтобы вы могли изменить его перед следующим шагом в сборке. Я признаю, что этот пункт является самым слабым.
  2. Иногда это происходит из-за того, что оригинальная работа была сделана не на каком-либо понятном человеку языке, а в инструменте моделирования GUI.
  3. Иногда вам нужно сделать что-то очень повторяющееся, и язык не должен заботиться о том, что вы делаете, потому что это такая нишевая вещь или такая сложная вещь, что он не имеет никакого дела, увеличивая сложность или грамматику языка программирования только для приспособления вы.
  4. Иногда вам нужно сделать что-то очень повторяющееся, и нет никакого способа получить то, что вы хотите, в язык в общем виде; либо он не может быть представлен, либо конфликтует с грамматикой языка.
  5. Одна из целей компьютеров состоит в том, чтобы уменьшить человеческие усилия, и иногда к коду, который вряд ли когда-либо коснется снова (низкая вероятность обслуживания), может быть написан метакод, который генерирует ваш более длинный код в десятую часть времени; если я могу сделать это в течение 1 дня вместо 2 -х недель , и это , вероятно, не будет поддерживаться постоянно, то я лучше генерировать его - и на случай, если кто - то через 5 лет раздражен , потому что они на самом деле действительно нужно поддерживать его, то они могут потратить 2 недели на полное его написание, если захотят, или быть недовольными 1 неделей поддержки неловкого кода (но у нас все еще впереди 1 неделя), и это в том случае , если это обслуживание вообще нужно делать. ,
  6. Я уверен, что есть и другие причины, по которым я не замечаю.

пример

До этого я работал над проектами, в которых нужно было генерировать код на основе данных или информации в каком-то другом документе. Например, один проект имел все свои сетевые сообщения и постоянные данные, определенные в электронной таблице, и инструмент, который будет проходить через электронную таблицу и генерировать много кода на C ++ и Java, который позволит нам работать с этими сообщениями.

Я не говорю, что это был лучший способ настроить этот проект (я не участвовал в его запуске), но это было то, что у нас было, и это были сотни (может быть, даже тысячи, не уверен) структур, объектов и констант которые генерировались; в этот момент, вероятно, уже слишком поздно, чтобы попытаться переделать что-то вроде Rhapsody. Но даже если бы он был переделан во что-то вроде Rhapsody, тогда у нас все равно есть код, сгенерированный из Rhapsody .

Кроме того, наличие всех этих данных в электронной таблице было хорошо с одной стороны: это позволило нам представлять данные способами, которые мы не могли бы иметь, если бы все они были просто в файлах исходного кода.

Пример 2

Когда я немного поработал над созданием компилятора, я использовал инструмент Antlr, чтобы выполнять лексинг и анализ. Я указал грамматику языка, затем использовал инструмент, чтобы выплюнуть тонну кода на C ++ или Java, затем я использовал этот сгенерированный код вместе со своим собственным кодом и включил его в сборку.

Как еще это должно было быть сделано? Возможно, вы могли бы придумать другой путь; Возможно, есть и другие способы. Но для этой работы другие способы были бы не лучше, чем сгенерированный код lex / parse, который у меня был.


Я использовал промежуточный код как своего рода формат файла и трассировку отладки, когда две системы были несовместимы, но имели какой-то стабильный API, в очень эзотерическом языке сценариев. Не предназначался для чтения вручную, но мог быть таким же, каким мог бы быть xml. Но это чаще, чем вы думаете, после того, как все веб-страницы работают таким образом, как кто-то указал.
joojaa

7

То, что вам не хватает, это повторное использование .

У нас есть удивительный инструмент для преобразования текста исходного кода в двоичный, называемый компилятором. Его входные данные четко определены (обычно!), И было проделано много работы, чтобы уточнить, как он выполняет оптимизацию. Если вы действительно хотите использовать компилятор для выполнения некоторых операций, вы хотите использовать существующий компилятор, а не писать свой собственный.

Многие люди изобретают новые языки программирования и пишут свои собственные компиляторы. Почти все без исключения они все делают это, потому что им нравится этот вызов, а не потому, что им нужны функции, которые предоставляет этот язык. Все, что они делают, может быть сделано на другом языке; они просто создают новый язык, потому что им нравятся эти функции. Но чего они не получат, так это хорошо настроенного, быстрого, эффективного, оптимизирующего компилятора. Это даст им что-то, что может превратить текст в двоичный файл, конечно, но это будет не так хорошо, как у всех существующих компиляторов .

Текст - это не просто то, что люди читают и пишут. Компьютеры тоже отлично сочетаются с текстом. Фактически форматы, такие как XML (и другие связанные форматы), успешны, потому что они используют простой текст. Бинарные форматы файлов часто неясны и плохо документированы, и читатель не может легко выяснить, как они работают. XML относительно самодокументируется, поэтому людям проще писать код, который использует файлы в формате XML. И все языки программирования настроены на чтение и запись текстовых файлов.

Итак, предположим, вы хотите добавить какое-то новое средство, чтобы сделать вашу жизнь проще. Возможно, это инструмент макета GUI. Возможно, это интерфейсы сигналов и слотов, которые предоставляет Qt . Возможно, именно так Code Composer Studio от TI позволяет настроить устройство, с которым вы работаете, и вставить нужные библиотеки в сборку. Возможно, он использует словарь данных и автоматически генерирует определения типов и определения глобальных переменных (да, это все еще очень важно во встроенном программном обеспечении). Как бы то ни было, самый эффективный способ использовать ваш существующий компилятор - это создать инструмент, который будет принимать вашу конфигурацию, какой бы она ни была, и автоматически генерировать код на выбранном вами языке.

Его легко разрабатывать и тестировать, потому что вы знаете, что происходит, и можете прочитать исходный код, который он выплевывает. Вам не нужно тратить человеко-годы на создание компилятора, чтобы конкурировать с GCC. Вам не нужно изучать совершенно новый язык или требовать от других людей. Все, что вам нужно сделать, это автоматизировать эту маленькую область, а все остальное остается прежним. Дело сделано.


Тем не менее, преимущество XML на основе текста заключается лишь в том, что при необходимости он может быть прочитан и написан людьми (обычно они не беспокоятся, когда он работает, но, безусловно, делают это во время разработки). С точки зрения производительности и эффективности использования пространства, двоичные форматы, как правило, намного лучше (хотя это часто не имеет значения, поскольку узкое место находится где-то еще).
оставил около

@leftaroundabout Если вам нужна такая производительность и экономия пространства, конечно. Причина, по которой многие приложения перешли на форматы на основе XML, заключается в том, что производительность и эффективность использования пространства не являются главными критериями, какими они были когда-то, и история показала, как плохо поддерживаются двоичные форматы файлов. (Старые документы MS Word для классического примера!) Однако суть остается неизменной - текст так же удобен для чтения, как и люди.
Грэм

Несомненно, плохо спроектированный двоичный формат может фактически работать хуже, чем правильно продуманный текстовый формат, и даже приличный двоичный формат часто не намного более компактен, чем XML, упакованный с некоторым универсальным алгоритмом сжатия. IMO лучшее из обоих миров - использовать удобочитаемую спецификацию через алгебраические типы данных и автоматически генерировать эффективное двоичное представление из AST этих типов. Смотрите, например, квартиру библиотеки .
оставил около

7

Немного более прагматичный ответ, сосредоточенный на том, почему, а не на том, что является и не является исходным кодом. Обратите внимание, что генерация исходного кода является частью процесса сборки во всех этих случаях - поэтому сгенерированные файлы не должны попадать в систему контроля версий.

Interoprability / простота

Возьмем основной пример протокола Google Buffers: вы пишете одно описание протокола высокого уровня, которое затем можно использовать для создания реализации на нескольких языках - часто разные части системы пишутся на разных языках.

Реализация / технические причины

Возьмем TypeScript - браузеры не могут его интерпретировать, поэтому в процессе сборки используется JavaScript ( преобразователь кода в код) для генерации JavaScript. Фактически, многие новые или эзотерические скомпилированные языки начинаются с переноса на C, прежде чем они получают правильный компилятор.

Простота использования

Для встроенных проектов (например, IoT), написанных на C и использующих только один двоичный файл (ОСРВ или без ОС), довольно просто сгенерировать массив C с данными, которые должны быть скомпилированы, как если бы это был нормальный исходный код, так как он предназначен для непосредственного их связывания. как ресурсы.

редактировать

Расширение на protobuf: генерация кода позволяет сгенерированным объектам быть первоклассными классами на любом языке. В скомпилированном языке универсальный синтаксический анализатор должен по необходимости возвращать структуру ключ-значение - это означает, что вам не нужно много шаблонного кода, вы пропускаете некоторые проверки во время компиляции (в частности, ключей и типов значений), получаете худшую производительность и нет завершения кода. Представьте себе все те, что void*в C или такие огромные std::variantв C ++ (если у вас есть C ++ 17), некоторые языки могут вообще не иметь такой возможности.


По первой причине, я думаю, что идея OP заключается в том, чтобы иметь общую реализацию на каждом языке (которая берет описание буферов протокола, а затем анализирует / использует формат на проводе). Почему это хуже, чем генерация кода?
Paŭlo Ebermann

@ PaŭloEbermann, помимо обычного аргумента производительности, такая универсальная интерпретация делает невозможным использование этих сообщений в качестве первоклассных объектов в скомпилированных (и, возможно, интерпретируемых) языках - например, в C ++ такой интерпретатор по необходимости должен возвращать структуру ключ-значение , Конечно, вы можете использовать это kv в своих классах, но это может превратиться во множество шаблонов кода. И есть также завершение кода тоже. И проверка времени компиляции - ваш компилятор не будет проверять, нет ли в ваших литералах опечаток.
Ян Дорняк

Я согласен ... не могли бы вы добавить это в ответ?
Paŭlo Ebermann

@ PaŭloEbermann готово
Ян Дорняк

6

Является ли генерация исходного кода анти-паттерном?

Это обходной путь для недостаточно выразительного языка программирования. Нет необходимости генерировать код на языке, который содержит адекватное встроенное метапрограммирование.


3
Это также обходной путь для того, чтобы написать полный компилятор исходного кода для более выразительного языка. Сгенерируйте C, пусть об остальном позаботится компилятор с хорошим оптимизатором.
Blrfl

Не всегда. Иногда у вас есть одна или несколько баз данных, содержащих некоторые определения, например, сигналов на шине. Затем вы хотите собрать эту информацию вместе, возможно, выполнить некоторые проверки согласованности, а затем написать код, который взаимодействует между сигналами, поступающими из шины, и переменными, которые вы ожидаете иметь в своем коде. Если вы можете показать мне язык с метапрограммированием, который упрощает использование некоторых листов Excel, базы данных и других источников данных, предоставляемых клиентом, и создает необходимый мне код с некоторыми необходимыми проверками на достоверность и согласованность данных, тогда все средства покажи мне.
CodeMonkey

@CodeMonkey: напоминает реализацию ActiveRecord в Ruby on Rails. Нет необходимости дублировать схему таблицы базы данных в коде. Просто сопоставьте класс с таблицей и напишите бизнес-логику, используя имена столбцов в качестве свойств. Я не могу представить какой-либо шаблон, который мог бы быть создан генератором кода, который также не мог бы управляться с помощью метапрограммирования Ruby. Шаблоны C ++ также очень мощные, хотя и немного загадочные. Макросы Lisp - еще одна мощная система метапрограммирования на языке.
Кевин Клайн

@kevincline я имел в виду код, основанный на некоторых данных из базы данных (может быть создан из нее), но не на самой базе данных. Т.е. у меня есть информация о том, какие сигналы я получаю в таблице A Excel. У меня есть база данных B с информацией об этих сигналах и т. Д. Теперь я хочу иметь класс, который получает доступ к этим сигналам. На компьютере, на котором выполняется код, нет соединения с базой данных или таблицей Excel. Использование действительно сложного C ++ Templating для генерации этого кода во время компиляции вместо простого генератора кода. Я выберу кодеген.
CodeMonkey

6

Генерация исходного кода не всегда является анти-паттерном. Например, я сейчас пишу фреймворк, который по заданной спецификации генерирует код на двух разных языках (Javascript и Java). Фреймворк использует сгенерированный Javascript для записи действий пользователя в браузере и использует код Java в Selenium для фактического выполнения действия, когда фреймворк находится в режиме воспроизведения. Если бы я не использовал генерацию кода, мне пришлось бы вручную убедиться, что оба они всегда синхронизированы, что обременительно и в некотором смысле является логическим дублированием.

Однако если кто-то использует генерацию исходного кода для замены таких функций, как дженерики, то это анти-паттерн.


Конечно, вы можете написать свой код один раз в ECMAScript и запустить его в Nashorn или Rhino на JVM. Или вы можете написать JVM на ECMAScript (или попытаться скомпилировать Avian в WebAssembly с помощью Emscripten) и запустить свой код Java в браузере. Я не говорю, что это отличные идеи (ну, это, вероятно, ужасные идеи :-D), но, по крайней мере, они возможны, если не осуществимы.
Йорг Миттаг

Теоретически это возможно, но это не общее решение. Что произойдет, если я не смогу запустить один из языков внутри другого? Например, дополнительная вещь: я только что создал простую модель Netlogo, используя генерацию кода, и у меня есть интерактивная документация по системе, которая всегда синхронизирована с рекордером и реплеером. И вообще, создание требования, а затем генерация кода поддерживает синхронизацию семантически выполняемых вещей.
Христо Вригазов

6

Я что-то здесь упускаю?

Может быть, хороший пример, где посреднический код оказался причиной успеха? Я могу предложить вам HTML.

Я считаю, что для HTML было важно быть простым и статичным - это облегчало создание браузеров, позволяло запускать мобильные браузеры на ранней стадии и т. Д. Как показали дальнейшие эксперименты (Java-апплеты, Flash) - более сложные и мощные языки приводят к большему количеству проблем. , Оказывается, что пользователи фактически подвергаются опасности из-за Java-апплетов, и посещение таких веб-сайтов было таким же безопасным, как и попытки взломать игру, загруженную через DC ++. Простой HTML, с другой стороны, достаточно безвреден, чтобы позволить нам проверить любой сайт с разумной уверенностью в безопасности нашего устройства.

Однако HTML был бы далеко не таким, как сейчас, если бы он не был сгенерирован компьютером. Мой ответ даже не появится на этой странице, пока кто-нибудь не переписает его вручную из базы данных в файл HTML. К счастью, вы можете использовать HTML практически на любом языке программирования :)

То есть, если для чего-то есть генератор кода, то почему бы не сделать это чем-то надлежащим образом, чтобы оно могло получать требуемые параметры и выполнять правильные действия, которые мог бы выполнить «сгенерированный» код?

Можете ли вы представить себе лучший способ отобразить вопрос и все ответы и комментарии для пользователя, чем с помощью HTML в качестве сгенерированного промежуточного кода?


Да, я могу представить себе лучший способ. HTML является наследием решения Тима Бернерса-Ли разрешить быстрое создание текстового веб-браузера. В то время это было совершенно нормально, но мы не будем делать то же самое с пользой задним числом. CSS сделал ненужными все различные типы элементов представления (DIV, SPAN, TABLE, UL и т. Д.).
Кевин Клайн

@kevincline Я не говорю, что HTML как таковой не имеет недостатков, я указал на то, что введение языка разметки (которое может быть сгенерировано программой) сработало очень хорошо в этом случае.
Джурис

Так что HTML + CSS лучше, чем просто HTML. Я даже написал внутреннюю документацию для некоторых проектов, над которыми работал непосредственно в HTML + CSS + MathJax. Но большинство веб-страниц, которые я посещаю, похоже, были созданы генераторами кода.
Дэвид К

3

зачем генерировать исходный код?

Потому что это быстрее и проще (и менее подвержено ошибкам), чем написание кода вручную, особенно для утомительных, повторяющихся задач. Вы также можете использовать высокоуровневый инструмент для проверки и подтверждения вашего дизайна перед написанием единой строки кода.

Общие случаи использования:

  • Инструменты моделирования, такие как Rose или Visual Paradigm;
  • Высоковольтный эр языки на уровне , как Embedded SQL или язык определения интерфейса , который должен быть препроцессор в нечто компилируемое;
  • Генераторы лексера и парсера, такие как flex / bison;

Что касается вашего «почему бы просто не сделать его функцией и напрямую передать ей параметры», обратите внимание, что ни одно из вышеперечисленного не является средой исполнения само по себе. Нет никакого способа связать ваш код с ними.


2

Иногда ваш язык программирования не имеет необходимых вам средств, что делает невозможным написание функций или макросов для выполнения того, что вы хотите. Или, может быть, вы могли бы делать то, что вы хотите, но код для его написания был бы уродливым. Простой сценарий Python (или аналогичный) может затем сгенерировать требуемый код как часть вашего процесса сборки, который вы затем вводите #includeв реальный исходный файл.

Откуда я это знаю? Потому что это решение, которое я достигал несколько раз при работе с различными системами, совсем недавно SourcePawn. Простой скрипт Python, который анализирует простую строку исходного кода и создает две или три строки сгенерированного кода, намного лучше, чем создание вручную сгенерированного кода, когда у вас получается две дюжины таких строк (создавая все мои cvars).

Демонстрационный / пример исходного кода доступен, если люди этого хотят.


1

Текстовая форма необходима для удобства потребления людьми. Компьютеры также довольно легко обрабатывают код в текстовом виде. Поэтому сгенерированный код должен быть сгенерирован в форме, которую легче всего генерировать и легче всего использовать на компьютерах, и это очень часто читаемый текст.

И когда вы генерируете код, сам процесс генерации кода часто должен быть отлажен - людьми. Это очень, очень полезно, если сгенерированный код удобочитаем, чтобы люди могли обнаруживать проблемы в процессе генерации кода. Кто-то должен написать код для генерации кода, в конце концов. Это не происходит из воздуха.


1

Генерация кода, только один раз

Не вся генерация исходного кода - это случай генерации некоторого кода, а затем его никогда не трогать; затем восстановить его из исходного источника, когда он нуждается в обновлении.

Иногда вы генерируете код только один раз, а затем отбрасываете исходный код и продвигаетесь вперед, поддерживая новый источник.

Это иногда происходит при переносе кода с одного языка на другой. В частности, если вы не ожидаете, что позже захотите перенести новые изменения в оригинал (например, старый языковой код не будет поддерживаться или будет фактически завершен (например, в случае некоторых математических функций)).

Одним из распространенных случаев является то, что написание генератора кода для этого может фактически только правильно перевести 90% кода. и затем эти последние 10% должны быть исправлены вручную. Что намного быстрее, чем перевод на 100% вручную.

Такие генераторы кода часто сильно отличаются от генераторов кода, которые f2cсоздают полноязычные переводчики (например, Cython или ). Поскольку цель состоит в том, чтобы один раз сохранить код. Они часто сделаны как 1, чтобы сделать именно то, что они должны. Во многих отношениях это версия следующего уровня использования regex / find-replace для кода порта. «Портирование с помощью инструмента», можно сказать.

Генерация кода, всего один раз, например, с сайта.

Близко связано, если вы генерируете код из какого-то источника, к которому вы не хотите обращаться снова. Например, если действия, необходимые для генерации кода, не могут быть повторяемыми, непротиворечивыми или выполнять их дорого. Сейчас я работаю над парой проектов: DataDeps.jl и DataDepsGenerators.jl .

DataDeps.jl помогает пользователям загружать данные (например, стандартные наборы данных ML). Для этого ему нужно то, что мы называем RegistrationBlock. Это некоторый код, указывающий некоторые метаданные, например, откуда загружать файлы, и контрольную сумму, и сообщение, объясняющее пользователю любые термины / кодировки / каков статус лицензирования данных.

Написание этих блоков может быть раздражающим. И эта информация часто доступна (структурированной или неструктурированной) с веб-сайтов, на которых хранятся данные. Таким образом, DataDepsGenerators.jl использует веб-браузер для генерации кода RegistrationBlockCode для некоторых сайтов, на которых размещено много данных.

Это может не генерировать их правильно. Поэтому разработчик, использующий сгенерированный код, может и должен проверить и исправить его. Скорее всего, они хотят убедиться, что он не пропустил информацию о лицензировании, например.

Важно отметить, что пользователям / разработчикам, работающим с DataDeps.jl, не нужно устанавливать или использовать веб-браузер для использования сгенерированного кода RegistrationBlock. (И не нужно загружать и устанавливать веб-скребок, это экономит немало времени. Особенно для запуска CI)

Генерация исходного кода один раз не является антипаттерном. и его обычно нельзя заменить метапрограммированием.


«report» - это английское слово, которое означает что-то иное, чем «port again». Попробуйте "re-port", чтобы сделать это предложение более понятным. (Комментируя, потому что слишком мал для предлагаемого редактирования.)
Питер Кордес

Хороший улов @PeterCordes я перефразировал.
Линдон Уайт

Быстрее, но потенциально гораздо менее ремонтопригодно, в зависимости от того, насколько ужасен сгенерированный код. Фортран на C был чем-то особенным в прошлом (компиляторы C были более широко доступны, поэтому люди использовали бы f2c+ cc), но полученный код не был действительно хорошей отправной точкой для C-версии программы AFAIK.
Питер Кордес

1
Потенциально, потенциально нет. Это не ошибка в концепции генераторов кода, что некоторые генераторы кода делают не поддерживаемый код. В частности, инструмент, созданный вручную, который не должен ловить каждый случай, часто может создать совершенно хороший код. Например, если 90% кода - это просто список констант массива, то генерация этих конструкторов массивов как единичного может быть тривиально выполнена очень легко и без особых усилий. (С другой стороны, вывод кода C на Cython не может поддерживаться людьми. Потому что он не предназначен для этого. Точно так же, как вы говорите об этом f2cв тот день)
Линдон Уайт

1
Большой стол был просто самым простым и наиболее редким аргументом. Подобное можно сказать, скажем, для преобразования циклов for или условий. На самом деле sedидет долгий путь, но иногда нужно немного больше выразительной силы. Граница между логикой программы и данными часто тонкая. Иногда различие не полезно. JSON - это (/ was) просто код конструктора объекта javascript. В моем примере я также генерирую код конструктора объекта (это данные? Может быть (возможно, нет, потому что иногда у него есть вызовы функций). Лучше ли это рассматривать как код? Да.)
Линдон Уайт

1

Генерация «исходного» кода является признаком недостатка генерируемого языка. Является ли использование инструментов для преодоления этого анти-паттерна? Абсолютно нет - позвольте мне объяснить.

Обычно используется генерация кода, поскольку существует определение более высокого уровня, которое может описать полученный код гораздо менее многословно, чем язык более низкого уровня. Таким образом, генерация кода способствует эффективности и краткости.

Когда я пишу на С ++, я делаю это, потому что это позволяет мне писать код более эффективно, чем с использованием ассемблера или машинного кода. Тем не менее машинный код генерируется компилятором. В начале, c ++ был просто препроцессором, который генерировал C-код. Языки общего назначения отлично подходят для генерации поведения общего назначения.

Таким же образом, используя DSL (предметно-ориентированный язык), можно писать кратко, но, возможно, код ограничен для конкретной задачи. Это облегчит генерацию правильного поведения кода. Помните, что код означает конец и конец . Разработчик ищет эффективный способ генерировать поведение.

В идеале генератор может создавать быстрый код из входных данных, которыми проще манипулировать и понимать. Если это выполняется, то использование генератора не является анти-паттерном . Это анти-модель , как правило , исходит из того , что «чистый» код является «чист», во многом таким же образом дерево рабочего или другой ремесленник может выглядеть при использовании электроинструментов, или использовании ЧПУ для «генерировать» заготовки (думает , что золотое молоток ).

С другой стороны, если источник сгенерированного кода сложнее поддерживать или генерировать код, который недостаточно эффективен, пользователь попадает в ловушку использования неправильных инструментов (иногда из-за того же золотого молотка ).


0

Генерация исходного кода абсолютно означает, что сгенерированный код является данными. Но это данные первого класса, данные, которыми может манипулировать остальная часть программы.

Два наиболее распространенных типа данных, которые мне известны и которые интегрированы в исходный код, - это графическая информация об окнах (количество и расположение различных элементов управления) и ORM. В обоих случаях интеграция с помощью генерации кода упрощает манипулирование данными, поскольку вам не нужно проходить дополнительные «специальные» шаги для их использования.

При работе с оригинальными компьютерами Mac (1984 г.) определения диалогов и окон создавались с помощью редактора ресурсов, который сохранял данные в двоичном формате. Использовать эти ресурсы в вашем приложении было сложнее, чем было бы, если бы «двоичным форматом» был Паскаль.

Итак, нет, генерация исходного кода не является анти-паттерном, она позволяет сделать часть данных приложения, что упрощает его использование.


0

Генерация кода - это анти-паттерн, когда он стоит больше, чем достигает. Эта ситуация возникает, когда генерация происходит из A в B, где A - почти тот же язык, что и B, но с некоторыми незначительными расширениями, которые можно сделать, просто кодируя в A с меньшими усилиями, чем все пользовательские инструменты и этапы сборки для A - B. ,

Компромисс является более запретительным по отношению к генерации кода в языках, которые не имеют средств метапрограммирования (структурных макросов) из-за сложностей и неадекватности достижения метапрограммирования посредством организации внешней обработки текста.

Плохой компромисс также может быть связан с количеством использования. Язык A может существенно отличаться от B, но весь проект с его собственным генератором кода использует A только в одном или двух небольших местах, так что общая сложность (маленькие биты A плюс генератор кода A -> B, плюс окружающая стадия сборки) превышает сложность решения, только что выполненного в B.

По сути, если мы берем на себя обязательство генерировать код, нам, вероятно, следует «пойти дальше или пойти домой»: сделать так, чтобы у него была существенная семантика, и использовать его часто, или не беспокоиться.


Почему вы удалили абзац "Когда Бьярне Страуструп впервые реализовал C ++ ..."? Я думаю, что это было интересно.
Утку

@Utku Другие ответы охватывают это с точки зрения составления всего сложного языка, на котором полностью написана остальная часть проекта. Я не думаю, что это представитель большинства того, что называется «генерация кода».
Каз

0

Я не видел этого ясно сформулированного (я видел, что это затронуто одним или двумя ответами, но это не казалось очень ясным)

Генерация кода (как вы сказали, как будто это данные) не является проблемой - это способ повторно использовать компилятор для вторичной цели.

Редактирование сгенерированного кода является одним из самых коварных, злых, ужасных анти-паттернов, которые вы когда-либо встречали. Не делай это.

В лучшем случае редактирование сгенерированного кода тянет кучу плохого кода в ваш проект (ВЕСЬ набор кода теперь действительно КОД ИСТОЧНИКА - больше не данные). В худшем случае код, загруженный в вашу программу, является сильно избыточным, плохо именуемым мусором, который почти полностью не поддерживается.

Я полагаю, что третья категория - это код, который вы используете один раз (генератор графического интерфейса?), А затем отредактируйте, чтобы помочь вам начать обучение. Это немного из каждого - это МОЖЕТ быть хорошим способом для начала, но ваш генератор GUI будет нацелен на использование генерируемого кода, который не будет хорошим началом для вас, как для программиста. Кроме того, вы можете Соблазн использовать его снова для второго GUI, что означает вытягивание избыточного кода SOURCE в вашу систему.

Если ваш инструментарий достаточно умен, чтобы запретить какие-либо изменения сгенерированного кода, сделайте это. Если нет, я бы назвал это одним из худших анти-паттернов.


0

Код и данные: информация.

Данные - это информация именно в той форме, которая вам нужна (и ценность). Код также является информацией, но в косвенной или промежуточной форме. По сути, код также является формой данных.

В частности, код - это информация для машин, которая сама по себе освобождает людей от обработки информации.

Разгрузка людей от обработки информации является наиболее важным мотивом. Промежуточные шаги приемлемы, если они облегчают жизнь. Вот почему существуют промежуточные инструменты отображения информации. Как генераторы кода, компиляторы, транспортеры и т. Д.

зачем генерировать исходный код? Почему бы не превратить это в функцию, которая может принимать параметры и воздействовать на них?

Допустим, кто-то предлагает вам такую ​​функцию отображения, реализация которой вам неизвестна. Пока функция работает так, как было обещано, вам будет интересно, генерирует ли она исходный код внутри или нет?


0

Если что-то может быть сгенерировано, то это данные, а не код.

Поскольку позже вы укажете, что код - это данные, ваше предложение сводится к «Если что-то может быть сгенерировано, то это не код». Не могли бы вы сказать, что ассемблерный код, сгенерированный компилятором C, не является кодом? Что, если это точно совпадает с кодом сборки, который я пишу от руки? Вы можете пойти туда, если хотите, но я не пойду с вами.

Давайте начнем с определения «кода». Не вдаваясь в технические подробности, довольно хорошим определением для целей этого обсуждения были бы «машинно-управляемые инструкции для выполнения вычислений».

Учитывая это, не является ли вся эта идея генерации исходного кода недоразумением?

Да, ваше начальное предложение состоит в том, что код не может быть сгенерирован, но я отклоняю это предложение. Если вы согласны с моим определением «кода», тогда не должно быть никаких концептуальных проблем с генерацией кода в целом.

То есть, если для чего-то есть генератор кода, то почему бы не сделать это чем-то надлежащим образом, чтобы оно могло получать требуемые параметры и выполнять правильные действия, которые мог бы выполнить «сгенерированный» код?

Что ж, это совершенно другой вопрос, касающийся причины создания кода, а не его природы. Вы предлагаете альтернативу, что вместо написания или использования генератора кода, вы пишете функцию, которая вычисляет результат напрямую. Но на каком языке? Прошли те времена, когда кто-то писал непосредственно в машинном коде, и если вы пишете свой код на любом другом языке, то вы зависите от генератора кода в виде компилятора и / или ассемблера для создания программы, которая фактически выполняется.

Почему же тогда вы предпочитаете писать на Java, C, Lisp или что-то еще? Даже ассемблер? Я утверждаю, что, по крайней мере, частично, потому что эти языки предоставляют абстракции для данных и операций, которые облегчают выражение деталей вычислений, которые вы хотите выполнить.

То же самое относится и к большинству генераторов кода более высокого уровня. Прототипными случаями, вероятно, являются сканеры и генераторы парсеров, такие как lexи yacc. Да, вы можете написать сканер и парсер непосредственно на C или на другом языке программирования по вашему выбору (даже необработанный машинный код), а иногда и так. Но для проблемы любой существенной сложности использование высокоуровневого языка специального назначения, такого как lex или yacc, облегчает написание, чтение и обслуживание рукописного кода. Обычно тоже намного меньше.

Вы должны также рассмотреть, что именно вы подразумеваете под «генератором кода». Я хотел бы рассмотреть предварительную обработку C и создание шаблонов C ++ как упражнения при генерации кода; Вы возражаете против этого? Если нет, то я думаю, что вам нужно выполнить некоторую умственную гимнастику, чтобы рационализировать принятие этих, но отвергая другие разновидности генерации кода.

Если это делается по соображениям производительности, то это звучит как недостаток компилятора.

Почему? Вы в основном утверждаете, что у вас должна быть универсальная программа, в которую пользователь передает данные, некоторые классифицируются как «инструкции», а другие как «ввод», и которая выполняет вычисления и генерирует больше данных, которые мы называем «выводом». (С определенной точки зрения такую ​​универсальную программу можно назвать «операционной системой».) Но почему вы полагаете, что компилятор должен быть столь же эффективен для оптимизации такой универсальной программы, как и для оптимизации более специализированной? программа? Две программы имеют разные характеристики и разные возможности.

Если это делается для объединения двух языков, то это звучит как отсутствие интерфейса библиотеки.

Вы говорите, что как будто наличие универсальной в некоторой степени библиотеки интерфейса обязательно будет хорошей вещью. Возможно, так и будет, но во многих случаях такая библиотека будет большой и трудной для написания и поддержки, а может быть, даже медленной. И если такого зверя на самом деле не существует для решения конкретной проблемы, то кто вы такой, чтобы настаивать на том, чтобы он был создан, когда подход генерации кода может решить проблему намного быстрее и проще?

Я что-то здесь упускаю?

Я думаю несколько вещей.

Я знаю, что код это тоже данные. Что я не понимаю, зачем генерировать исходный код? Почему бы не превратить это в функцию, которая может принимать параметры и воздействовать на них?

Генераторы кода преобразуют код, написанный на одном языке, в код на другом, обычно более низком уровне языка. Вы спрашиваете, почему люди хотят писать программы на нескольких языках, и особенно, почему они могут хотеть смешивать языки субъективно разных уровней.

Но я уже коснулся этого. Один выбирает язык для конкретной задачи, частично основываясь на его ясности и выразительности для этой задачи. Поскольку меньший код в среднем содержит меньше ошибок и его легче обслуживать, существует также и уклон в сторону языков более высокого уровня, по крайней мере, для крупномасштабной работы. Но сложная программа включает в себя много задач, и часто некоторые из них могут быть более эффективно решены на одном языке, тогда как другие более эффективно или более кратко решаются на другом языке. Использование правильного инструмента для работы иногда означает использование генерации кода.


0

Отвечая на вопрос в контексте вашего комментария:

Обязанность компилятора - взять код, написанный в удобочитаемой форме, и преобразовать его в машиночитаемую форму. Следовательно, если компилятор не может создать эффективный код, то компилятор не выполняет свою работу должным образом. Это неправильно?

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

В качестве примера я работал над программным обеспечением, в котором аналитику может понадобиться написать некоторый код. Они могли бы написать свой алгоритм на C ++ и добавить все проверки границ и трюки запоминания, от которых они зависят, но для этого нужно много знать о внутренней работе кода. Они скорее написали бы что-нибудь простое, и позвольте мне добавить алгоритм для генерации окончательного кода C ++. Затем я могу сделать экзотические трюки, чтобы максимизировать производительность, такие как статический анализ, который я бы никогда не ожидал от моих аналитиков. Генерация кода позволяет им писать в предметной области, что позволяет им вывести продукт на улицу проще, чем любой универсальный инструмент.

Я также сделал прямо противоположное. У меня есть еще одна часть работы, у которой был мандат «нет генерации кода». Мы все еще хотели упростить жизнь тем, кто использует программное обеспечение, поэтому мы использовали огромное количество шаблонов метапрограммирования, чтобы компилятор генерировал код на лету. Таким образом, мне нужен был только язык C ++ общего назначения, чтобы выполнять свою работу.

Тем не менее, есть подвох. Было чрезвычайно трудно гарантировать, что ошибки были читаемы. Если вы когда-либо использовали шаблонный метапрограммированный код ранее, вы знаете, что одна невинная ошибка может привести к ошибке, которая берет 100 строк непонятных имен классов и аргументов шаблона, чтобы понять, что пошло не так. Этот эффект был настолько выражен, что рекомендуемый процесс отладки для синтаксических ошибок был «прокручивать журнал ошибок до тех пор, пока в первый раз не обнаружится ошибка в одном из ваших собственных файлов. сделал не так. "

Если бы мы использовали генерацию кода, мы могли бы иметь гораздо более мощные возможности обработки ошибок с ошибками, читаемыми человеком. Такова жизнь.


0

Есть несколько разных способов использования генерации кода. Их можно разделить на три основные группы:

  • Генерация кода на другом языке в качестве вывода из шага в процессе компиляции. Для типичного компилятора это будет язык более низкого уровня, но это может быть другой язык высокого уровня, как в случае языков, которые компилируются в JavaScript.
  • Генерация или преобразование кода на языке исходного кода в качестве шага в процессе компиляции. Это то, что делает макрос .
  • Генерация кода с помощью инструмента отдельно от обычного процесса компиляции. Результатом этого является код, который хранится в виде файлов вместе с обычным исходным кодом и компилируется вместе с ним. Например, классы сущностей для ORM могут быть автоматически сгенерированы из схемы базы данных, или объекты передачи данных и сервисные интерфейсы могут быть сгенерированы из спецификации интерфейса, такой как файл WSDL для SOAP.

Я предполагаю, что вы говорите о третьем типе сгенерированного кода, так как это наиболее спорная форма. В первых двух формах сгенерированный код является промежуточным этапом, который очень четко отделен от исходного кода. Но в третьей форме нет формального разделения между исходным кодом и сгенерированным кодом, за исключением того, что сгенерированный код, вероятно, имеет комментарий, который говорит: «не редактируйте этот код». Это по-прежнему открывает риск для разработчиков, редактирующих сгенерированный код, что было бы действительно ужасно. С точки зрения компилятора сгенерированный код является исходным кодом.

Тем не менее, такие формы сгенерированного кода могут быть действительно полезны в статически типизированном языке. Например, при интеграции с объектами ORM действительно полезно иметь строго типизированные оболочки для таблиц базы данных. Конечно, вы можете динамически управлять интеграцией во время выполнения, но вы потеряете безопасность типов и поддержку инструментов (завершение кода). Основным преимуществом языка статических типов является поддержка системы типов в типе письма, а не только во время выполнения. (И наоборот, этот тип генерации кода не очень распространен в динамически типизированных языках, поскольку в таком языке он не дает никакой выгоды по сравнению с преобразованиями во время выполнения.)

То есть, если для чего-то есть генератор кода, то почему бы не сделать это чем-то надлежащим образом, чтобы оно могло получать требуемые параметры и выполнять правильные действия, которые мог бы выполнить «сгенерированный» код?

Поскольку безопасность типов и завершение кода - это функции, которые вам нужны во время компиляции (и при написании кода в IDE), но обычные функции выполняются только во время выполнения.

Хотя может быть и золотая середина: F # поддерживает концепцию провайдеров типов, которые в основном представляют собой строго типизированные интерфейсы, генерируемые программно во время компиляции. Эта концепция может заменить многие виды использования генерации кода и обеспечить более четкое разделение задач.


0

Наборы инструкций процессора принципиально необходимы , но языки программирования могут быть декларативными . Запуск программы, написанной на декларативном языке, неизбежно требует некоторого типа генерации кода. Как упоминалось в этом и других ответах , основной причиной генерации исходного кода на понятном человеку языке является использование преимуществ сложной оптимизации, выполняемой компиляторами.


-3

Если что-то может быть сгенерировано, то это данные, а не код.

Вы неправильно поняли. Следует читать

Если что-то может быть передано в генератор для интерпретируемых , то это код, а не данные.

Это исходный формат для этой стадии компиляции, а формат приемника все еще является кодом.


1
Неверное определение исходного кода . Исходный код в основном предназначен для людей, работающих над ним (и этот факт определяет его, см. Также, что такое FSF - свободное программное обеспечение ). Код ассемблера, сгенерированный с помощью gcc -fverbose-asm -O -S, не является исходным кодом (и не является единственным или главным образом данными), даже если это некоторая текстовая форма, всегда передаваемая в GNU asи иногда читаемая людьми.
Старынкевич

Кроме того, многие языковые реализации компилируются с кодом C , но этот сгенерированный C не является подлинным исходным кодом (например, не может быть легко обработан людьми).
Старынкевич

Наконец, ваше оборудование (например, чип AMD или Intel или материнская плата вашего компьютера) интерпретирует машинный код (который, очевидно, не является исходным кодом). КСТАТИ IBM1620 имел клавиатуры типизируем машинный код (BCD), но этот факт не делает его «исходный код». Весь код не является исходным.
Старынкевич

@BasileStarynkevitch Ах, ты меня туда доставил. Я не должен слишком сильно сжимать мои остроумные высказывания, иначе они меняют смысл. Правильно, исходный код должен быть самым оригинальным кодом, который подается на первом этапе компиляции.
Берги

Нет исходного кода для людей. Это так же сложно и субъективно определить как музыку (против звука). Не нужно пытаться найти программное обеспечение, которое его использует.
Старынкевич
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.