Если вы можете реализовать AST на C ++, STL предоставляет std::type_info::name()
, который возвращает имя типа T
. В некоторых компиляторах, включая MSV C, это возвращает удобочитаемое имя. На других, включая gcc
, он возвращает искаженное имя. G CC предоставляет встроенную функцию abi::__cxa_demangle
, чтобы распутать его, а Boost предоставляет более переносимую функцию для этого, boost::core::demangle
.
Для достижения полиморфизма времени выполнения вы можете использовать typeid
, чтобы получить объект std::type_info
во время выполнения, или вы можете унаследовать каждый тип узла AST от абстрактного базового класса. Это может быть чистая виртуальная функция ::name()
, реализация которой может быть чем-то вроде
#include <boost/core/demangle.hpp>
#include <typeinfo>
template<typename T>
const char* AST<T>::name() {
return boost::core::demangle( typeid(T).name() );
}
Или что-то еще, использующее кучу блоков #ifdef
для поддержки разных компиляторов.
Если хотите Чтобы свернуть свой собственный, эти ответы дают несколько способов конкатенации постоянных строк во время компиляции с использованием метапрограммирования шаблонов. Было бы несколько неудобных битов, таких как: размещение квалификаторов, таких как const
, volatile
и signed
, в каноническом порядке, выражение указателя на T(Args...)
как T(*)(Args...)
и эквивалент для указателей на массивы. Если вы хотите устранить все накладные расходы времени выполнения с помощью метапрограммирования шаблонов, вы, вероятно, определите свой собственный шаблон type_name<T>
, выделите его отдельно, используя std::enable_if
и библиотеку <type_traits>
, и обеспечите переопределение для составных типов, которые объединяют имена типов компоненты с *
, (
, )
, [
и ]
надлежащим образом. Если вы можете жить с небольшими накладными расходами, вы можете просто сделать их const std::string
и объединить их с +
.
. Вы можете сделать это снизу вверх, начиная с имен для простых типы, а затем предоставление реализаций для array-of-t, указатель-на-array-of-T, et c. которые рекурсивно ищут имена строительных блоков. Это очень похоже на паттерны на функциональном языке.
Выполнить это на C было бы намного, намного сложнее.
Редактировать
Сделать это на C было бы не обязательно быть намного сложнее. (В нашем чате вы говорите, что на самом деле делаете это в Kotlin.)
Вам следует обратиться к языковому стандарту (или недавнему проекту ) для формальной грамматики, но подмножество интересующего вас языка имеет три случая:
- Простые типы, где вы формируете указатель, добавляя
*
, - Производные типы, в которых вы формируете указатель, добавив
(*)
посередине и - указатели на производные типы, где у вас уже есть что-то вроде
(**)
, и вы добавляете к нему еще одну звездочку. (На самом деле никогда не пишите трехзвездочный код!)
Это можно записать как алгоритм, в котором типы имеют left , middle и правая часть. Для простых типов left - это весь тип, а два других пустые. Для массивов left - это тип элемента, а right - границы. Для прототипов функций left - это возвращаемый тип, а right - это список аргументов. В любом случае средний пуст. Для указателей на производные типы средний - это звезды внутри средних скобок. Так, например, double(*)[4][4]
имеет левый из double
, средний из *
и правый из [4][4]
.
Большинство операций имеют два особых случая, в зависимости от того, является ли средний пустым или непустым. (Они могут быть реализованы как специализации обобщений, сопоставления с образцом, перегруженных методов объекта или блока case. Поскольку мы гипотетически делаем это в C, мы, вероятно, написали бы это strlen(middle) ? ... : ...
.)
С этого момента я буду писать конкатенацию строк ⧺.
Если у данного типа есть пустое среднее , его имя в C будет left ⧺ справа , а объявление будет left ⧺ "" ⧺ name ⧺ right .
Если у данного типа есть непустое среднее , его имя в C будет left ⧺ "(" ⧺ middle ⧺ ")" ⧺ справа и объявление с его использованием: left ⧺ "(" ⧺ middle ⧺ name ⧺ ")" ⧺ right . Например, в объявлении int(*callback)(handle)
, left is int
, middle is *
, name is callback
и right равно (handle)
.
Формирование указателей на тип имеет три случая: если есть непустая середина, добавьте *
к middle . Если есть пустая середина и непустая правая , установите середину на *
. Если оба средний и правый пусты, добавьте *
к левому .
Есть угловые случаи, которые требуют немного больше возиться, если вы хотите поддерживать указатели const
и volatile
, неполные типы функций в стиле K&R или ссылки C ++.