LINUX.ORG.RU

Генерация кода по кастомным compile-time аттрибутам в C++

 


2

5

Небольшой вопрос к C++-волшебникам.

Мне хочется на функции навесить что-то вроде аттрибутов (compile-time аннотаций из C#/Java), чтобы потом в момент компиляции/сборки их проанализировать и что-то сделать.

Например так: `int [[static_boundary(«immutable»)]] foo()`

В момент компиляции оно должно будет увидеть этот аттрибут и обернуть foo в небольшую функцию-обёртку (например, сгенерив слегка видоизмененный файл, или напрямую запатчив AST в компиляторе)

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

Эта фича нужна скорей не по виду, а по смыслу. Другие формы записи того же самого тоже подойдут (если они кроссплатформенно работают с clang).

Проблема в том, что если просто так навесить мою [[static_boundary]], то в AST (по крайней мере в clang) её не будет. Нужно пересобирать комплиятор, запатчив список аттрибутов в Attr.td. Ну и потом вообще придётся напрямую эксплуатировать clang, пусть даже он и умеет работать как библиотека. Все это как-то мерзко и анально.

Есть ли какие-нибудь более красивые решения?

★★★★☆

Последнее исправление: stevejobs (всего исправлений: 2)

C++
красивые решения

Смешная шутка

SZT ★★★★★
()

посмотри как сделан moc и все вопросы отпадут.

anonymous
()

Ещё менее красивый способ, чем патчить Clang — это самому допарсивать нужные атрибуты, зная SourceLocation у FunctionDecl.

Но учти, что Clang AST в достаточно большом смысле ReadOnly.

vzzo ★★★
()
Последнее исправление: vzzo (всего исправлений: 1)

А задавать параметры шаблонов и использовать constexpr if не то? Или я вообще не понял о чём речь?

Ivan_qrt ★★★★★
()
Ответ на: комментарий от vzzo

Но учти, что Clang AST в достаточно большом смысле ReadOnly.

а вот поясни это, пожалуйста

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

stevejobs ★★★★☆
() автор топика
Ответ на: комментарий от stevejobs

это как write-only код, только наоборот

anonymous
()
Ответ на: комментарий от i36_zubov

подойдет, но это выглядит мерзко.

я хочу использовать C++ как человеко-читаемый DSL (чтобы потом из него генерить нечто совершенно другое)

и вот эта конструкция, «__attribute__((pure))» действительно остается в IR, но в ней из 21 буквы только 4 имеют смысл, а ещё 19% - мусор. Это как раз то, чего хотелось бы избежать.

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

stevejobs ★★★★☆
() автор топика
Ответ на: комментарий от maxis11

клевая, спасибо!

в этой презентации предлагают сделать то же самое, что в посте - запатчить Attr.td и пересобрать шланг. И потом, вероятно, всё что зависит от шланга - в моем случае это Emscripten, потому что надо собирать WASM.

это... костыли?

вот если бы экстеншены складывались в папочку plugins и просто подхватывались в момент старта шланга (обычного, не форкнутого и не патченного)

stevejobs ★★★★☆
() автор топика
Последнее исправление: stevejobs (всего исправлений: 1)
Ответ на: комментарий от stevejobs

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

Хотелось бы послушать, как ты читаешь свой исходный код, чтобы оценить привычную тебе легкость и красоту. Можно на Java, не на C++.

Virtuos86 ★★★★★
()
Ответ на: комментарий от stevejobs

я хочу использовать C++ как человеко-читаемый DSL (чтобы потом из него генерить нечто совершенно другое)

Взаимоисключающие параграфы. DSL должен оперировать категориями целевого домена а не подражать С++. Особенно беря во внимание что с читаемостью у С++ дела могут обстоять похуже чем с perl ...

cvv ★★★★★
()

Есть ли какие-нибудь более красивые решения?

Помещать код требующий модификации в какие нибуть «виртуальные» секции а потом модифицировать перед перенесением в реальные секции

cvv ★★★★★
()
Ответ на: комментарий от SR_team

ты хочешь D

Плюсую этому господину. Проще будет на D сгенерировать код для плюсов или на питоне.

anonymous
()

Решено

Буду использовать вот такую конструкцию.

#define a(S) __attribute__((annotate(S)))

int a("boundary=immutable") main()
{
    cout << "Hello, World!" << endl;
    return 0;
}

Проверил, что это остается в AST - пробежался курсором, нашёл.

Строки парсить.

Сам синтаксис работает и для функций, и для классов.

Source-to-source transformation, похоже, придется делать с помощью libTooling :( Про это будет отдельная серия вопросов.

stevejobs ★★★★☆
() автор топика
Ответ на: комментарий от anonymous

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

SR_team ★★★★★
()
Ответ на: комментарий от stevejobs

Там очень много зависимостей между разными узлами и инвариантов, которые в лучшем случае покрыты assert-ами, а в худшем просто роняют компилятор где-то позже. Если изменение нетривиальное, то проще с нуля по старому AST генерировать код или новое AST.

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

vzzo ★★★
()
Ответ на: комментарий от vzzo

то проще с нуля по старому AST генерировать код

а это вообще возможно? Как это сделать? Из AST получить C++-файл (желательно выбросив из него мусор в виде того, что там в инклудах)

stevejobs ★★★★☆
() автор топика
Ответ на: Решено от stevejobs

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

Пара из них изобретена автором языка Ragel. (Сам Ragel - из другой оперы)

cvv ★★★★★
()
Ответ на: комментарий от cvv

Так мне эти «аннотации» зачем - я тоже хочу написать язык программирования, для преобразования одних языков в другие, но используя C++ с аннотациями как DSL описания трансформации :)

Нужно это исключительно в целях изучения уже готовой такой штуки - фреймворка Truffle из GraalVM.

stevejobs ★★★★☆
() автор топика
Ответ на: комментарий от stevejobs

clang-format это как-то делает.

Но я не пробовал: на этом этапе меня настолько это задолбало, что я переписывал IR (что на порядок приятнее и документированнее).

vzzo ★★★
()
Ответ на: комментарий от SR_team

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

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

Там AST макросов нет, правда, как в том же Nim, потому что Уолтер Брайт против их введения так как это даст слишком много свободы и он (на мой взгляд обоснованно) опасается, что люди начнут творить на грани извращения (более чем уверен что так и будет) и в итоге получится куча кода на D который не совместим друг с другом и действительно превратит его в маргинальный язык. Язык и так довольно медленно продвигается в массы (но продвигается уверенно).

В общем то, что нужно ТС на D решается легко (свои проблемы конечно тоже есть). Если уж человек освоил libclang и может бегать курсором, то на D он смог бы намного больше с теми же усилиями. Есть интересная библиотека для построения PEG парсеров - pegged - на ней может школьник DSL сделать. Но цель я вижу у ТС конкретная и другой язык ему не подходит.

anonymous
()
Ответ на: комментарий от anonymous

Да, это чисто учебный пример. Я на C++ хочу сделать аналог того, что уже сделано на Java с помощью run+compile time reflection и байткодовой магии. Причем на Java он работает уже достаточно хорошо, вряд ли C++ может что-то улучшить. Делается это только для того, чтобы проверить мое понимание механики работы исходного продукта, не более.

stevejobs ★★★★☆
() автор топика
Ответ на: комментарий от stevejobs

лул, вот что с людьми делает жабка. осиль gdb.

anonymous
()
Ответ на: комментарий от stevejobs

Нужно это исключительно в целях изучения уже готовой такой штуки - фреймворка Truffle из GraalVM.

Забудь С++ и все что с ним связано - иначе ты не получишь конечный результат вообще.

cvv ★★★★★
()

всё, что в других языках делается через reflection, в С++ делается кодогенерацией на этапе сборки, например, make + самописные утилиты

*compile time reflection

next_time ★★★★★
()
Последнее исправление: next_time (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.