Элегантный способ генерации кода на С++

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

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

Я искал довольно много, но все решения, которые я нашел до сих пор, довольно сложны/обширны, недостаточно эффективны или недостаточно гибки.

Какие библиотеки вы используете в своих проектах на С++ для генерации кода?

Бест, Moritz

Ответ 1

Вы можете использовать систему преобразования программ (PTS) для надежного определения и компоновки шаблонов кода.

Большинство PTS позволяют определять грамматику, а затем анализировать исходный код в AST с помощью этой грамматики. Что еще более важно, они принимают шаблоны: фрагменты исходного кода (обычно нетерминала или список нетерминалов) с заполнителями, которые соответствуют хорошо сформированным суб-фрагментам (нетерминалы, представляющие поддеревья). Эти шаблоны обычно настаивают на том, что именованный заполнитель совпадает одинаково (см. Пример ниже). Такие шаблоны могут использоваться для сопоставления с анализируемым AST как способ найти фрагменты кода с использованием синтаксиса поверхности.

Итак, можно использовать шаблон:

   pattern x_squared(t: term): product
      = " \t * \t ";

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

   (p + q[17])*(p+q[17)

но не

    2 * (x-3)

Но так же интересно, что такие шаблоны могут использоваться как генераторы кода, создавая экземпляр шаблона с привязанным значением (деревьями) для переменных. Так, "экземпляр x_squared (2 ^ x)" производит

     (2^x)*(2^x)

Сама по себе это просто причудливая схема макросов. Это намного лучше, поскольку он может сказать вам "во время компиляции" (для шаблонов), имеет ли смысл то, что вы составляете, или нет. Таким образом, вы получаете проверку типов композиции фрагментов кода. Например, вы можете случайно закодировать "экземпляр x_squared (int q)", но хорошая PTS будет возражать, что "int q" не является "термином"; вы обнаружите ошибку при создании генератора кода.

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

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

 ... P * Q ...

и ваш генератор кода так или иначе знает, что Q - единичная матрица. Тогда следующая оптимизация может удалить дорогостоящую матрицу:

  rule optimize_matrix_times_unit(m: term, n: term): product -> product
       " \m * \q "
   ->  " \m "
    if is_identity_matrix(q)

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

Вам нужен PTS, способный обрабатывать синтаксический анализ С++; их немного сложно найти. Тот, который я разработал (DMS Software Reengineering Toolkit), делает это. Примеры в этом ответе - DMS-стиль.

Здесь технический документ, в котором описывается крупномасштабная задача реинжиниринга, выполняемая DMS на С++-коде. Ряд примеров в документе на самом деле довольно сложные шаблоны, используемые для создания кода; задача реинжиниринга должна была создать новый набор API для существующего фрагмента кода.