可以使用C预处理器来判断文件是否存在吗?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了可以使用C预处理器来判断文件是否存在吗?相关的知识,希望对你有一定的参考价值。
我有一个非常大的代码库(读取:数千个模块),它们在许多项目中共享代码,这些代码都运行在具有不同C ++编译器的不同操作系统上。毋庸置疑,维护构建过程可能相当繁琐。
如果只有一种方法可以使预处理器忽略某些#includes
(如果当前文件夹中不存在该文件),那么代码库中有几个地方可以清理代码。有谁知道实现这一目标的方法?
目前,我们在共享文件中使用围绕#ifdef
的#include
,以及第二个项目特定的文件#defines #include
是否存在于项目中。这有效,但很难看。人们在添加或删除项目中的文件时经常忘记正确更新定义。我已经考虑过编写一个预构建工具来保持这个文件是最新的,但是如果有一个独立于平台的方法来对预处理器做这个,我宁愿那样做。有任何想法吗?
通常,这是通过使用尝试运行预处理器的脚本来尝试包含该文件来完成的。根据预处理器是否返回错误,脚本会使用适当的#define(或#undef)更新生成的.h文件。在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
一个非常彻底的框架,可以像这样(以及其他数千个)可移植性检查,是autoconf。
Little Update
一些编译器可能支持__has_include ( header-name )
。
扩展被添加到C++17 standard(P0061R1)。
编译器支持
- 铛
- 来自5.X的GCC
- 来自VS2015 Update 2的Visual Studio(?)
示例(来自clang网站):
// Note the two possible file name string formats.
#if __has_include("myinclude.h") && __has_include(<stdint.h>)
# include "myinclude.h"
#endif
Sources
为缺少的标题创建一个特殊文件夹,并使该文件夹最后被搜索 (具体是compliler - “INCLUDES”环境变量中的最后一项,类似于此)
然后,如果某些header1.h可能丢失,请在该文件夹中创建一个存根
那么header1.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列表的包含文件,这些#defines表示当前目录中存在的文件的名称:
#define EXISTS_FILE1_C
#define EXISTS_FILE1_H
#define EXISTS_FILE2_C
然后,在源代码中包含该文件,然后您的源可以测试EXISTS_*
定义以查看文件是否存在。
据我所知,cpp没有关于文件存在的指令。
如果您在跨平台使用相同的make,那么您可以通过Makefile的一些帮助来完成此任务。您可以在Makefile中检测文件的存在:
foo.o: foo.c
if [ -f header1.h ]; then CFLAGS+=-DHEADER1_INC
正如@Greg Hewgill所提到的那样,你可以使你的#includes成为有条件的:
#ifdef HEADER1_INC
#include <header1.h>
#endif
另一种可能性:使用您希望可选择包含的所有标头的零长度版本填充某个目录。将-I参数传递给此目录作为最后一个此类选项。
GCC cpp按顺序搜索其include目录,如果它在早期目录中找到一个头文件,它将使用它。否则,它最终会找到零长度文件,并且很开心。
我假设其他cpp实现也按指定的顺序搜索其include目录。
我不得不为Symbian OS做类似的事情。这就是我做的方式:假设您要检查文件“file_strange.h”是否存在,并且您希望包含一些标题或链接到某些库,具体取决于该文件的存在。
首先创建一个小批处理文件,以检查该文件是否存在。
autoconf很好,但对许多小项目来说都是过度杀戮。
---------- check.bat
@echo off
IF EXIST [epoc32includedomainmiddlewarefile_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结束
在你的bld.inf文件中包含check.mk文件,它必须在你的MMP文件之前
PRJ_MMPFILES
gnumakefile checkmedialist.mk
现在在编译时,文件file_strange_supported.h
将具有适当的标志集。您可以在cpp文件中使用此标志,甚至可以在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
功能 - 至少根据我的经验。使用Update 3进行测试。
谣言可能源于VS 2017也被称为“第15版”; VS 2015被称为“版本14”。似乎已经在“Visual Studio 2017版本15.3”中正式引入了对该功能的支持。
以上是关于可以使用C预处理器来判断文件是否存在吗?的主要内容,如果未能解决你的问题,请参考以下文章