Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 1

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

cat > .test.h <<'EOM'
#include <asdf.h>
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.

Ответ 2

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

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

header1.h:

#define header1_is_missing

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

#include <header1.h>
#ifdef header1_is_missing

   // there is no header1.h 

#endif

Ответ 3

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

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

Расширение было добавлено в С++ 17 standard (P0061R1).

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

  • Clang
  • GCC от 5.X
  • Visual Studio из VS2015 Update 2 (?)

Пример (из веб-сайта clang):

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

Источники

Ответ 4

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

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

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

Ответ 5

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

#define EXISTS_FILE1_C
#define EXISTS_FILE1_H
#define EXISTS_FILE2_C

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

Ответ 6

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

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

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

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

#ifdef HEADER1_INC
#include <header1.h>
#endif

Ответ 7

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

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

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

Ответ 8

Мне пришлось сделать что-то подобное для ОС 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