可以使用C预处理器来判断文件是否存在吗?


72

我有一个非常大的代码库(阅读:数千个模块),该代码具有在多个项目上共享的代码,这些项目都在具有不同C ++编译器的不同操作系统上运行。不用说,维护构建过程可能会很麻烦。

如果只有一种方法可以使预处理器忽略#includes当前文件中不存在的文件,那么在代码库中有许多地方可以对代码进行实质性的清理。有谁知道实现这一目标的方法?

目前,我们使用了一个#ifdef围绕#include在共享文件,与第二项目特定文件#define语句是否#include存在于该项目。这有效,但是很丑。人们在项目中添加或删除文件时常常会忘记正确更新定义。我已经考虑过编写一个预构建工具来使该文件保持最新状态,但是如果有一种与平台无关的方法来对预处理器执行此操作,则我宁愿那样做。有任何想法吗?

Answers:


33

通常,这是通过使用一个脚本来完成的,该脚本尝试运行预处理器以尝试包含该文件。根据预处理器是否返回错误,脚本将使用适当的#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是一个非常全面的框架,可移植地与此类可移植性检查(以及其他数千种)一起使用。


84

小更新

一些编译器可能支持__has_include ( header-name )

该扩展已添加到C ++ 17标准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

资料来源


5
这将是C ++ 17的一部分!isocpp.org/files/papers/p0636r0.html
iFreilicht

2
如果我正确解释了WG14分钟,则此功能也将添加到C2X中。
Leushenko '18 -4-5

在Objective-C中有用,用于测试iOS中其他库/组件的有条件存在(看着您React Native)
DaveAlden

我猜这是标准“配置” unix / linux工具的结尾-编译器本身提供了查询是否有特定标头的机制。:-D
TarmoPikaro

50

创建一个缺少头文件的特殊文件夹,然后最后搜索该文件夹
(特定于编译器-环境变量“ INCLUDES”中的最后一项)

然后,如果可能缺少某些header1.h,请在该文件夹中创建一个存根

header1.h:

#define header1_is_missing

现在您可以随时写

#include <header1.h>
#ifdef header1_is_missing

   // there is no header1.h 

#endif

1
+1用于使源代码本身完全可移植,并将可移植性负担转移到编译器调用本身(在不同的编译器之间已经几乎不可移植)。
mtraceur

1
我想更准确地说,不仅仅是使源代码具有完全可移植性,而且还可以按原样进行编译(假设正确的选项,并且在某些情况下甚至可能没有任何特殊的选项)。
mtraceur

10

预处理器本身无法识别文件的存在,但是您当然可以使用构建环境来识别文件。我最熟悉make,它将允许您在makefile中执行以下操作:

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

当然,您必须在其他构建环境(例如VisualStudio)中找到类似的东西,但是我敢肯定它们存在。


+1用于使源代码本身完全可移植。我认为,这(或者在我的典型情况下,是默认情况下假定文件存在的修改,如果人们没有该文件,则可以发出信号),它甚至可能是我在这里看到的最喜欢的方法,因为它是对定义进行记录很简单,并且可以预期从源头开始的典型用户构建会知道或轻松弄清楚如何在其特定构建系统或编译器中定义预处理器变量。
mtraceur

我想更准确地说,不仅仅是使源代码具有完全可移植性,而且还可以按原样进行编译(假设正确的预处理器定义,在某些情况下甚至可能没有任何特殊选项)。
mtraceur

4

您可以运行一个预构建步骤,以生成一个包含文件,该包含文件包含#define列表,这些列表表示当前目录中现有文件的名称:

#define EXISTS_FILE1_C
#define EXISTS_FILE1_H
#define EXISTS_FILE2_C

然后,从源代码中包含该文件,然后您的源代码可以测试EXISTS_*定义以查看文件是否存在。


4

据我所知,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

4

另一种可能性:在目录中填充您希望有选择地包括的所有标头的零长度版本。将-I参数作为最后一个这样的选项传递给该目录。

GCC cpp按顺序搜索其包含目录,如果在较早的目录中找到头文件,它将使用它。否则,它将最终找到零长度的文件,并且很高兴。

我假设其他cpp实现也按指定的顺序搜索其include目录。


2

我必须为Symbian OS做类似的事情。这就是我的操作方式:假设您要检查文件“ 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结束

在您的bld.inf文件中包含check.mk文件,该文件必须在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

2

与此处和互联网上的某些声明相反,Visual Studio 2015不支持该__has_include功能-至少根据我的经验。经过Update 3测试。

VS 2017也被称为“版本15”这一事实可能引起了谣言。相反,VS 2015被称为“版本14”。似乎已随“ Visual Studio 2017版本15.3”正式引入了对该功能的支持。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.