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

StackOverflow https://stackoverflow.com/questions/142877

  •  02-07-2019
  •  | 
  •  

Вопрос

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

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

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

Это было полезно?

Решение

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

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

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

Другие советы

Небольшое Обновление

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

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

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

  • Лязг
  • GCC от 5.X
  • Visual Studio из обновления 2 VS2015 (?)

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

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

Источники

Создайте специальную папку для отсутствующих заголовков и сделайте так, чтобы поиск в этой папке производился в последнюю очередь
(это зависит от комплайнера - последний элемент в переменной окружения "ВКЛЮЧАЕТ", что-то в этом роде)

Затем, если какой-то заголовок 1.h может отсутствовать, создайте в этой папке заглушку

заголовок 1.h:

#define header1_is_missing

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

#include <header1.h>
#ifdef header1_is_missing

   // there is no header1.h 

#endif

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

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

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

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

#define EXISTS_FILE1_C
#define EXISTS_FILE1_H
#define EXISTS_FILE2_C

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

Насколько я знаю, 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

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

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

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

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

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

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

----------проверь.

@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

----------проверка.бита заканчивается

затем я создал файл 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

Вопреки некоторым заявлениям здесь и в Интернете, Visual Studio 2015 НЕ поддерживает __has_include особенность - по крайней мере, судя по моему опыту.Протестировано с обновлением 3.

Слухи, возможно, возникли из-за того факта, что VS 2017 также упоминается как "Версия 15".;Версия 2015 вместо этого называется "Версией 14".Поддержка этой функции, похоже, была официально представлена в "Visual Studio 2017 версии 15.3".

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top