77

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

#includes如果只有在当前文件夹中不存在文件的情况下,如果有办法让预处理器忽略某些情况,代码库中有几个地方会大量清理代码。有谁知道实现这一目标的方法?

目前,我们在共享文件中使用了一个#ifdef环绕的#include,第二个特定于项目的文件 #defines 是否#include存在于项目中。这有效,但它很丑。人们在从项目中添加或删除文件时经常忘记正确更新定义。我已经考虑编写一个预构建工具来保持这个文件是最新的,但是如果有一种独立于平台的方式来使用预处理器来做到这一点,我宁愿这样做。有任何想法吗?

4

9 回答 9

107

小更新

一些编译器可能支持__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

来源

于 2015-10-21T13:03:23.107 回答
51

为缺少的标题创建一个特殊文件夹,并使该文件夹最后被搜索
(这是编译器特定的 - “INCLUDES”环境变量中的最后一项,类似的东西)

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

header1.h:

#define header1_is_missing

现在你可以随时写

#include <header1.h>
#ifdef header1_is_missing

   // there is no header1.h 

#endif
于 2008-09-27T04:34:08.533 回答
32

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

于 2008-09-27T03:47:02.220 回答
11

预处理器本身无法识别文件的存在,但您当然可以使用构建环境来做到这一点。我最熟悉make,它可以让你在你的makefile中做这样的事情:

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

当然,您必须在 VisualStudio 等其他构建环境中找到类似的东西,但我确信它们存在。

于 2008-09-27T03:48:16.147 回答
5

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

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

我认为其他 cpp 实现也会按照指定的顺序搜索它们的包含目录。

于 2008-09-27T04:31:23.050 回答
4

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

#define EXISTS_FILE1_C
#define EXISTS_FILE1_H
#define EXISTS_FILE2_C

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

于 2008-09-27T03:27:29.397 回答
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
于 2008-09-27T03:54:43.887 回答
2

与此处和 Internet 上的一些说法相反,Visual Studio 2015 不支持该__has_include功能 - 至少根据我的经验。使用更新 3 测试。

谣言可能源于 VS 2017 也被称为“版本 15”;VS 2015 被称为“版本 14”。“Visual Studio 2017 Version 15.3”似乎已经正式引入了对该功能的支持。

于 2018-09-11T11:59:59.490 回答
1

我不得不为 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
于 2009-07-02T11:38:12.487 回答