У меня есть следующее требование:
Например:
void fn(param-list)
{
ENTRY_TEXT (param-list)
//some code
EXIT_TEXT
}
Но не только в таком простом случае, он также работает с директивами препроцессора!
Пример:
void fn(param-list)
#ifdef __WIN__
{
ENTRY_TEXT (param-list)
//some windows code
EXIT_TEXT
}
#else
{
ENTRY_TEXT (param-list)
//some any-os code
if (condition)
{
return; //should become EXIT_TEXT
}
EXIT_TEXT
}
Итак, мой вопрос: есть ли правильный способ сделать это?
Я уже пробовал некоторую работу с парсерами, используемыми компиляторами, но, поскольку все они полагаются на запуск препроцессора перед анализом, они бесполезны для меня.
Также некоторые из синтаксических анализаторов, генерирующих токены, которые не нуждаются в препроцессоре, в некоторой степени бесполезны, потому что они генерируют отображение памяти токенов, что затем приводит к полному новому исходному коду, а не просто к вставке текста.
Одна вещь, над которой я работаю, это попробовать это с FLEX (или JFlex), если это допустимый вариант, я был бы признателен за некоторые замечания. 😉
РЕДАКТИРОВАТЬ:
Чтобы уточнить немного: цель состоит в том, чтобы позволить что-то вроде трассировки стека.
Я хочу отследить каждый вызов функции, и для того, чтобы следовать иерархии вызовов, мне нужно поместить макрос в точку входа функции и в точку выхода функции.
Это создает трассировку вызова функции. 🙂
РЕДАКТИРОВАТЬ 2: специфичные для компилятора опции не совсем подходят, так как у нас есть много разных компиляторов, и многие из них, вероятно, не очень хорошо поддерживаются какими-либо инструментами.
К сожалению, ваша идея не только непрактична (C ++ сложно анализировать), но и обречена на провал.
Основная проблема, с которой вы столкнулись, заключается в том, что исключения EXIT_TEXT
макрос целиком
У вас есть несколько решений.
Как было отмечено, первым решением было бы использовать платформо-зависимый способ вычисления трассировки стека. Это может быть несколько неточным, особенно из-за встраивания: то есть, когда небольшие функции встроены в своих вызывающих объектах, они не появляются в трассировке стека, так как на уровне сборки не было сгенерировано ни одного вызова функции. С другой стороны, он широко доступен, не требует каких-либо операций с кодом и не влияет на производительность.
Вторым решением было бы только ввести что-то на входе и использовать RAII, чтобы сделать работу выхода. Гораздо лучше, чем ваша схема, так как она автоматически обрабатывает несколько возвратов и исключений, она страдает от одной и той же проблемы: как выполнить вставку автоматически. Для этого вы, вероятно, захотите работать на уровне AST и изменить AST, чтобы представить свой маленький драгоценный камень. Вы можете сделать это с помощью Clang (посмотрите на инструменте миграции c ++ 11 для примеров переписывания в целом) или с помощью gcc (используя плагины).
Наконец, у вас также есть ручные аннотации. Хотя это может показаться недостаточным (и много работы), я хотел бы подчеркнуть, что вы не оставляете ведение журнала для инструмента … Я вижу 3 преимущества сделать это вручную: вы можете избежать введения этих накладных расходов в чувствительные к производительности части, вы можете сохранить только «сводку» больших аргументов, и вы можете настроить сводку на основе того, что интересно для текущая функция.
Я бы предложил использовать Библиотеки LLVM & лязг для начала.
Вы также можете использовать язык C ++ для упрощения вашего процесса. Если вы просто вставляете небольшой объект в код, который построен на входе в область действия функции & полагаться на то, что он будет уничтожен при выходе. Это должно значительно упростить запись «выхода» из функции.
Это на самом деле не отвечает на ваш вопрос, однако, для вашей первоначальной необходимости, вы можете использовать функцию backtrace () из execinfo.h
(если вы используете GCC).
Как создать трассировку стека при сбое моего приложения gcc C ++