Статьи / Свой препроцессор

Свой препроцессор


Отправить ссылку:

Свой препроцессор

Свой препроцессор

Иногда у нас есть необходимость в том чтобы "развернуть" директивы препроцессора в коде С/С++ программы. Например, у меня такая необходимость возникла при обработке кода 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 ", то препроцессор будет искать файл Test.h в директориях добавленных методом AddSystemIncludeDir.

Добавляет директорию для поиска обычных файлов, подключаемых директивой #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. Необходимо в заголовке указать название статьи.

Оставь свой отзыв


Ответьте на вопрос (ответ маленькими буквами)* :
10-1?(ответ цифрами)