Можно ли использовать препроцессор C, чтобы определить, существует ли файл?

У меня очень большая база кода (читайте: тысячи модhive), которая имеет общий код для множества проектов, которые все работают в разных операционных системах с разными компиляторами на C ++. Разумеется, сохранение процесса сборки может быть довольно сложной задачей.

В кодовой базе есть несколько мест, где она могла бы существенно очистить код, если бы существовал способ заставить препроцессор игнорировать некоторые #includes если файл не существовал в текущей папке. Кто-нибудь знает, как это достичь?

В настоящее время мы используем #ifdef вокруг #include в общем файле со вторым файлом, специфичным для проекта, который #defines определяет, существует или нет #include в проекте. Это работает, но это уродливо. Люди часто забывают правильно обновлять определения при добавлении или удалении файлов из проекта. Я рассматривал возможность написания инструмента предварительной сборки, чтобы обновлять этот файл, но если это будет независимый от платформы способ сделать это с помощью препроцессора, я бы скорее сделал это именно так. Есть идеи?

Как правило, это делается с помощью скрипта, который пытается запустить препроцессор при попытке включить файл. В зависимости от того, препроцессор возвращает ошибку, скрипт обновляет сгенерированный файл .h с соответствующим #define (или #undef). В bash сценарий может выглядеть смутно следующим образом:

 cat > .test.h <<'EOM' #include  EOM if gcc -E .test.h then echo '#define HAVE_ASDF_H 1' >> config.h else echo '#ifdef HAVE_ASDF_H' >> config.h echo '# undef HAVE_ASDF_H' >> config.h echo '#endif' >> config.h fi 

Достаточно тщательная основа для портативной работы с такими проверками переносимости, как это (а также тысячи других), является autoconf .

Маленькое обновление

Некоторые компиляторы могут поддерживать __has_include ( header-name ) .

Расширение было добавлено к стандарту C ++ 17 ( P0061R1 ).

Поддержка компилятора

  • лязг
  • GCC от 5.X
  • Visual Studio от VS2015 Update 2 (?)

Пример (с сайта clang):

 // Note the two possible file name string formats. #if __has_include("myinclude.h") && __has_include() # include "myinclude.h" #endif 

источники

  • SD-6: рекомендации по функциональным испытаниям SG10
  • Расширения языка Clang

Создайте специальную папку для отсутствующих заголовков и сделайте поиск этой папки последней
(то есть специфичный для компилятора – последний элемент в переменной среды «INCLUDES», что-то вроде этого)

Тогда, если некоторые header1.h могут отсутствовать, создайте в этой папке заглушку

header1.h:

 #define header1_is_missing 

Теперь вы всегда можете написать

 #include  #ifdef header1_is_missing // there is no header1.h #endif 

Препроцессор сам по себе не может идентифицировать существование файлов, но вы, конечно, можете использовать среду сборки для этого. Я в основном знаком с make, который позволит вам сделать что-то подобное в вашем make-файле:

 ifdef $(test -f filename && echo "present") DEFINE=-DFILENAME_PRESENT endif 

Конечно, вам придется найти аналог в других средах сборки, таких как VisualStudio, но я уверен, что они существуют.

У вас может быть шаг заготовки, который создает файл include, содержащий список #defines, который представляет имена файлов, существующих в текущем каталоге:

 #define EXISTS_FILE1_C #define EXISTS_FILE1_H #define EXISTS_FILE2_C 

Затем EXISTS_* этот файл из исходного кода, а затем ваш источник может протестировать EXISTS_* чтобы определить, существует ли файл или нет.

Насколько я знаю, cpp не имеет директивы относительно существования файла.

Возможно, вы сможете выполнить это с небольшой помощью из Makefile, если вы используете один и тот же make на разных платформах. Вы можете обнаружить наличие файла в файле Makefile:

 foo.o: foo.c if [ -f header1.h ]; then CFLAGS+=-DHEADER1_INC 

Как упоминает @Greg Hewgill, вы можете сделать свой #includes условным:

 #ifdef HEADER1_INC #include  #endif 

Другая возможность: заполнить каталог где-нибудь нулевыми версиями всех заголовков, которые вы хотите включить. Передайте аргумент -I этому каталогу в качестве последнего такого параметра.

GCP cpp ищет свои включенные каталоги в порядке, если он найдет заголовочный файл в более раннем каталоге, который он будет использовать. В противном случае он в конечном итоге найдет файл с нулевой длиной и будет счастлив.

Я предполагаю, что другие реализации cpp также ищут свои каталоги include в указанном порядке.

Я должен был сделать что-то подобное для ОС Symbian. Вот как я это сделал: скажем, вы хотите проверить, существует ли файл «file_strange.h», и вы хотите включить некоторые заголовки или ссылку на некоторые библиотеки в зависимости от существования этого файла.

сначала создайте небольшой пакетный файл для проверки существования этого файла.

autoconf является хорошим, но более убитым для многих небольших проектов.

———- check.bat

 @echo off IF EXIST [\epoc32\include\domain\middleware\file_strange] GOTO NEW_API GOTO OLD_API GOTO :EOF :NEW_API echo.#define NEW_API_SUPPORTED>../inc/file_strange_supported.h GOTO :EOF :OLD_API echo.#define OLD_API_SUPPORTED>../inc/file_strange_supported.h GOTO :EOF 

———- check.bat заканчивается

то я создал файл gnumake

———-checkmedialist.mk

 do_nothing : @rem do_nothing MAKMAKE : check.bat BLD : do_nothing CLEAN : do_nothing LIB : do_nothing CLEANLIB : do_nothing RESOURCE : do_nothing FREEZE : do_nothing SAVESPACE : do_nothing RELEASABLES : do_nothing FINAL : do_nothing 

———-check.mk заканчивается

включить файл check.mk в ваш файл bld.inf, он ДОЛЖЕН быть перед вашими файлами MMP

 PRJ_MMPFILES gnumakefile checkmedialist.mk 

теперь во время компиляции файл file_strange_supported.h будет иметь соответствующий флаг. вы можете использовать этот флаг в своих файлах cpp или даже в файле mmp, например, в mmp

 #include "../inc/file_strange_supported.h" #ifdef NEW_API_SUPPORTED LIBRARY newapi.lib #else LIBRARY oldapi.lib #endif 

и в .cpp

 #include "../inc/file_strange_supported.h" #ifdef NEW_API_SUPPORTED CStrangeApi* api = Api::NewLC(); #else // .. #endif 
Давайте будем гением компьютера.