Можно ли использовать препроцессор C, чтобы определить, существует ли файл?
-
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".