Свой препроцессор
Свой препроцессор
Свой препроцессор
Иногда у нас есть необходимость в том чтобы "развернуть" директивы препроцессора в коде С/С++ программы. Например, у меня такая необходимость возникла при обработке кода GLSL шейдеров. Мне захотелось добавить обработку директивы #include, анализ кода с получением списка uniform переменных и пару других вещей. Правильно развернуть директивы препроцессора далеко не тривиальная задача. Но с этим может справиться компонент wave библиотеки boost. (http://www.boost.org/doc/libs/1_43_0/libs/wave/index.html).
Но идея привязать свой проект к громоздкой библиотеке boost (которую после этого придется таскать с собой) устроит далеко не каждого. К тому же данный компонент требует подключения к проекту не только заголовочных, но и бинарных файлов.
Более удобное решение - это поместить библиотеку в отдельный dll-файл и написать удобный интерфейс-надстройку.
Что и было мной сделано.
Кое-что можно было бы сделать оптимальнее, избежав лишних аллокаций памяти и копирования. Но для меня в первую очередь нужно было, чтобы библиотека была удобной, корректно работала и была понятно написана.
Также данная библиотека написана только под Windows, но при желании её без труда можно портировать под другие платформы (под все, которые поддерживает boost). Буду очень признателен, если у кого-нибудь будет время и желание, чтобы сделать это.
К статье прикреплены два архива.
Готовую скомпилированную библиотеку вы можете найти в первом архиве: http://unick-soft.ru/art/files/archive1.zip
Он содержит в себе файлы PreProcessor.h, PreProcessor.cpp и PreProcessor.dll.
Чтобы использовать библиотеку просто подключите к коду программы файл PreProcessor.cpp (в нем находится код автоматической подгрузки файла PreProcessor.dll) и проследите, чтобы файл PreProcessor.dll находился в одной папке с экзешником. Интерфейсы для использования библиотеки находятся в файле PreProcessor.h.
Второй архив содержит тоже самое что и первый + проект Visual Studio 2008 с исходным кодом библиотеки и еще один проект с тестовым приложением: http://unick-soft.ru/art/files/archive2.zip
Проект для сборки библиотеки называется "PreProcessorLib.vcproj". Проект тестового приложения - "PreProcessorTest.vcproj".
Чтобы скомпилировать исходный код библиотеки вам понадобится boost (я использовал версию 1.44). О том, как его правильно проинсталлировать и скомпилировать вы можете найти на официальном сайте http://www.boost.org/.
После установки boost-а вам нужно будет в проекте PreProcessorLib.vcproj указать правильные пути в следующих настройках проекта:
C/C++ > General > Additional Include Directories (путь к заголовочникам boost-а)
Linker > General > Additional Library Directories (пути к lib файлам boost-а, разделенные точкой с запятой)
Далее идет пример использования:
Данная программа обрабатывает файл и выводит результат на экран. В случае ошибки лог препроцессора тоже выводится на экран.
Основные интерфейсы:
IPreProcFileReader - нужен для перехвата операций чтения из файла. Не обязателен. Но если стандартное чтение из файла чем-то не устраивает, то можно написать свою реализацию этого интерфейса, создать экземпляр и установить его методом IPreProcessor::SetFileReader(IPreProcFileReader *).
Методы:
Выделить буфер и прочитать в него файл. В случае невозможности прочитать файл функция должна вернуть false, а в случае успеха true.
Параметр ppOutContentData - в этот указатель должен быть записан указатель на выделенный буфер. Буфер должен быть в формате текущей локали системы либо в кодировке latin1.
Параметр pOutContentSize - по этому указателю нужно записать размер буфера.
Параметр FileName - это имя файла, который нужно прочитать.
Освободить буфер, выделенный в функции PreProcReadFileToBuffer.
IpreProcLexerIterator - нужен для обработки выходных лексем препроцессора (поступающих после обработки входного кода). Для корректной работы достаточно выводить эти лексемы в файл или буффер. Реализация ставится методом IPreProcessor::SetFileReader(IPreProcFileReader *).
Методы:
Обработать лексему. Параметр cInput - это указатель на строку, содержащую лексему.
IPreProcessor - интерфейс препроцессора. Экземпляр препроцессора создается функцией CreatePreProcessor, а удаляется функцией FreePreProcessor.
Методы:
Ставит/возвращает максимальную глубину рекурсивного включения файлов директивой #include.
Добавляет директорию для поиска системных файлов, подключаемых директивой #include <...> (с угловыми скобочками). То есть, если в обрабатываемом файле будет строка "#include
Добавляет директорию для поиска обычных файлов, подключаемых директивой #include "..." (с двойными кавычками). То есть, если в обрабатываемом файле будет строка "#include "Test.h"", то препроцессор будет искать файл Test.h в директориях добавленных методом AddCommonIncludeDir.
Эквивалентно одновременному вызову AddSystemIncludeDir и AddCommonIncludeDir для передаваемой директории.
Очистить список директории для поиска подключаемых файлов.
Добавить definition для препроцессора. Возможные формы записи:
> MACRO define MACRO as 1
> MACRO= define MACRO as nothing (empty)
> MACRO= definition define MACRO as definition
> MACRO(x) define MACRO(x) as 1
> MACRO(x)= define MACRO(x) as nothing (empty)
> MACRO(x)=definition define MACRO(x) as definition
Очистить список definition-ов препроцессора.
Разрешает обработку входных файлов так, будто в конце каждого добавлена пустая строка (символ '\n'). Отсутствие пустой строки приводит к ошибке. По умолчанию включена.
Включает/выключает добавление директивы #line в выходной блок данных. По умолчанию включена. Помогает, к примеру, найти точное место ошибки во входном файле (при дальнейшем обработке полученного буфера компилятором).
Устанавливает последовательность символов, используемой в качестве конца строки для выходного блока данных. По умолчанию "\n".
Получить выходной лог препроцессора. Следует помнить, что указатель на возвращаемый буфер хранится внутри объекта и удаляется при уничтожении препроцессора. Так же буфер пересоздается после каждого вызова ProcessFile и ProcessBuffer (указатель становится невалидным).
Установить свой обработчик чтения из файла.
Установить свой обработчик выходных лексем (обработчик по умолчанию не делает ничего).
Обработать файл. В случае ошибки подробный отчет можно получить функцией GetInfoLog().
Обработать буфер данных. В случае ошибки подробный отчет можно получить функцией GetInfoLog(). Параметр cFileName - не обязательный, нужен для корректного отображения места ошибки в выходном логе.
Надеюсь, моя работа кому-то поможет. Успехов!
При полном или частичном копировании необходимо указывать прямую ссылку на данную статью.
Юрий (Дата )
Все вопросы и предложения высылайте на адрес soft_support@list.ru. Необходимо в заголовке указать название статьи.
Оставь свой отзыв