Я смотрю на возможность реализации высокоуровневого (подобного Лиспу) языка путем компиляции через C (или, возможно, C ++, если исключения оказываются достаточно полезными); Это стратегия, которую уже использовали несколько проектов. Конечно, это сгенерирует код на C, в отличие от, возможно, в некоторых измерениях, превышающих сложность, всего, что вы напишите вручную.
Современные компиляторы C очень надежны при обычном использовании, но трудно понять, какие ошибки могут скрываться в крайних случаях при необычных нагрузках, особенно если вы говорите о том, что «ни один программист никогда не напишет скрытый предел X больше, чем Y».
Мне кажется, совпадение этих фактов может привести к несчастью.
Существуют ли какие-либо известные случаи, или есть хороший способ найти случаи, когда сгенерированный код работает с ошибками / ограничениями крайних случаев в достаточно свежих версиях основных компиляторов (например, GCC, Microsoft C ++, Clang)?
Возможно, это не совсем тот ответ, который вы искали, но несколько лет назад я работал над проектом, где части системы были написаны на каком-то языке более высокого уровня, где было легко создавать конечные автоматы в процессах. Этот язык генерирует C-код, который затем компилируется. Компилятор, который мы использовали для этого проекта, был gcc (версия около 2.95 — не цитируйте меня, но наверняка pre-3.0). Мы столкнулись с парой ошибок генерации кода, но это было из моей памяти больше связано с использованием не очень популярного процессора [показывая, какой процессор может показать что-то, что я не должен рассказывать о проекте, поэтому я бы предпочел не сказать, что это было, даже если это было очень давно].
Близкий мне коллега занимался расследованием одной из тех ошибок генерации кода, которая была в функции около 200 тыс. Строк, каждая из которых была большим оператором switch, причем каждый случай в операторе switch составлял около 50-1000 строк каждая (с несколько слоев операторов под-переключателя внутри него).
Насколько я помню, код зависал, потому что он произвел недопустимую операцию или сохранил что-то в регистре, уже занятом для чего-то другого, поэтому, как только вы нажмете нужную часть кода, произойдет сбой из-за несанкционированного доступа к памяти — и это не имел никакого отношения к длинному размеру кода, потому что мой коллега сумел сократить его до примерно 30 строк кода в конце концов (после большого количества «давайте вырезать это и посмотреть, все ли идет не так»), и после Несколько дней у нас была новая версия компилятора с исправлением. Приятно осознавать, что ваши тысячи долларов на оплату контракта на обслуживание компилятора стоят хотя бы иногда …
Я хочу сказать, что современные компиляторы переносят много большого кода. Существуют также минимальные ограничения, которые «совместимый компилятор должен поддерживать как минимум из». Например, я верю (опять же из памяти), что компилятор должен поддерживать 127 уровней вложенных операторов (то есть комбинацию 127 if, switch, while и do-while) внутри функции. И из обсуждения где-то (откуда берется «компилятор должен поддерживать 127 уровней вложенных операторов»), мы обнаружили, что MSVC и GCC поддерживают гораздо больше (достаточно, чтобы мы отказались от его поиска … )
Короткий ответ:
У вас нет выбора, если вам нужна производительность компилятора, а не простота жизни интерпретатора (или прекомпилятора + интерпретатора). Вы будете компилировать в какой-то язык более низкого уровня, и C является сегодняшним языком ассемблера, с C ++ примерно таким же доступным и подходящим для задачи, как C. Нет никаких причин, почему вы должны бояться этого пути. На самом деле, в некотором смысле, это очень распространенный маршрут.
Длинный ответ:
Сгенерированный код ни в коем случае не является необычным. Даже относительно скромное использование сгенерированного кода приводит к тому, что исходный код на языке C «не похож на то, что когда-либо писал бы любой программист», с точки зрения количества (тривиальные шаблоны кода повторяются с небольшими вариациями миллионы раз) или качества (комбинации языковых функций, которые человек никогда бы не использовал, но это все еще, скажем, легальный C ++). Есть также немало компиляторов, которые компилируются в C или C ++, какой-то известный и хорошо известен людям, которые написали стандарты языка C и C ++.
Наиболее распространенные компиляторы C и C ++ хорошо справляются с генерируемым кодом. Да, они никогда не бывают идеальными.
Часто это хорошее стремление к переносимости между компиляторами, а также знание и отслеживание ограничений переносимости. Если вы не очень хорошо протестировали конкретный компилятор C или C ++, не утверждайте, что он будет работать как часть вашего набора инструментов.
Вы задаете скрытый вопрос между C и C ++. Ну, здесь больше оттенков серого. C ++ — очень богатый язык. Вы можете использовать почти все функции C ++ для хороших целей в своем генераторе, но в некоторых случаях вы должны спросить себя, может ли конкретная важная функция стать пассивом, стоившим вам больше, чем она приносит вам. Например, разные компиляторы используют разные стратегии для создания шаблона. Неявная реализация может привести к непредвиденной сложности с переносимым сгенерированным кодом. Если шаблоны действительно помогают вам создать генератор, не стесняйтесь использовать их; но если у вас есть только предельный вариант их использования, помните, что у вас есть более веская причина, чем у большинства людей, ограничивать язык, на котором вы генерируете.
Существуют всевозможные ограничения, определенные реализацией в C. Некоторые хорошо определены и видимы программисту (представьте числовые ограничения), другие — нет. В моем экземпляре проекта стандарта раздел 5.2.4.1 подробно описывает нижние границы этих пределов:
5.2.4 Экологические ограничения
Обе среды перевода и выполнения ограничивают реализацию
языковые переводчики и библиотеки. Следующее суммирует язык, связанный
экологические ограничения на соответствующую реализацию; связанные с библиотекой ограничения
обсуждается в п. 7.5.2.4.1 Пределы перевода
Реализация должна иметь возможность переводить и выполнять по крайней мере одну программу, которая содержит по крайней мере один экземпляр каждого из следующих ограничений:18)
— 127 уровней вложенности блоков
— 63 уровня вложенности условного включения
— 12 указателей, массивов и функций объявления (в любых комбинациях), изменяющих арифметику, структуру, объединение или тип void в объявлении
[…]
18) Реализации должны по возможности избегать введения фиксированных лимитов перевода.
Я не могу с уверенностью сказать, может ли ваш переводчик поразить кого-либо из них или у компилятора (ов), на который вы нацеливаетесь, будут проблемы, даже если вы это сделаете, но я думаю, что вы, вероятно, будете в порядке.
Что касается ошибок:
Лязг — http://llvm.org/bugs/buglist.cgi?bug_status=все&Продукт = лязг
GCC — http://gcc.gnu.org/bugzilla/buglist.cgi?bug_status=все&Продукт = GCC
Visual Studio — https://connect.microsoft.com/VisualStudio/feedback
Это может показаться очевидным, но единственный способ узнать это — это тестирование. Если вы не можете приложить все усилия самостоятельно, по крайней мере, сделайте свой продукт кроссплатформенным, чтобы люди могли легко проверить вас! Если людям нравится ваш проект, они обычно готовы отправлять отчеты об ошибках или даже патчи бесплатно 🙂