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

时间:2020-03-06 14:49:04  来源:igfitidea点击:

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

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

当前,我们在共享文件中的" #include"周围使用" #ifdef",还有第二个特定于项目的文件,该文件#定义项目中是否存在" #include"。这行得通,但是很丑。人们在项目中添加或者删除文件时,常常会忘记正确更新定义。我打算编写一个预构建工具来使该文件保持最新状态,但是如果有一种与平台无关的方法可以使用预处理器来执行此操作,则我宁愿以这种方式进行操作。有任何想法吗?

解决方案

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

#define EXISTS_FILE1_C
#define EXISTS_FILE1_H
#define EXISTS_FILE2_C

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

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

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

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

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

据我所知,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按顺序搜索其包含目录,如果在较早的目录中找到头文件,它将使用它。否则,它将最终找到零长度的文件并感到满意。

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

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

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

header1.h:

#define header1_is_missing

现在我们可以随时写

#include <header1.h>
#ifdef header1_is_missing

   // there is no header1.h 

#endif

我必须为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