繁体   English   中英

当路径中存在同名文件时,跨平台方式包含系统头文件?

[英]Cross platform way to include system header files, when there is an identically named file in path?

我正在尝试让彭博(Bloomberg)的BDE库在Visual Studio 2015中进行编译。由于它们重新实现了通常由编译器提供的标准库,因此有些头文件的名称与标准库名称完全匹配,例如stddef.h 它们可选地允许您关闭标准库的覆盖,并且为了方便起见,重新实现的文件将可选地仅包括原始编译器提供的版本(例如stddef.h 它们通过以下宏来执行此操作:

#   if defined(BSLS_COMPILERFEATURES_SUPPORT_INCLUDE_NEXT)
#     include_next <stddef.h>
#   else
#     include BSL_NATIVE_C_LIB_HEADER(stddef.h)
#   endif

资源

BSL_NATIVE_C_LIB_HEADER扩展为以下内容:

#if defined(BSLS_PLATFORM_CMP_SUN) // Sun Compiler
#   define BSL_NATIVE_C_LIB_HEADER(filename) <../include/filename>

#elif defined(BSLS_PLATFORM_CMP_CLANG) || defined(BSLS_PLATFORM_CMP_GNU)
  // Clang and GCC use 'include_next'

#elif defined(BSLS_PLATFORM_CMP_HP) // HP Compiler
#   define BSL_NATIVE_C_LIB_HEADER(filename) <../include_std/filename>

#else
  // Most other compilers
#   define BSL_NATIVE_C_LIB_HEADER(filename) <../include/filename>

#endif

资源

问题是Visual Studio 2015 引入了一些重构 ,该重构一些 C标准库头文件移动到如下路径: C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.10150.0\\ucrt\u003c/code> 。 显然,这意味着<../include/filename>将不再找到移动的文件。 问题是所有文件都没有移动。 例如, iso646.h仍在C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\include ,并将被C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\include拾取。

因此,这是我的问题 :可以继续支持BSL_NATIVE_C_LIB_HEADER宏,同时在../include根据文件确定导入是来自../ucrt/还是../include ,这是我的问题 ../include名称? 我知道我可以创建两个单独的宏,但是如果可能的话,我宁愿保持相同的接口。

理想情况下,类似于BSL_NATIVE_C_LIB_HEADER()的宏将继续工作,而在幕后有一种机制可以根据文件名覆盖某些宏,以指向其他目录。 最初,这似乎是不可能的,因为宏的参数是文件名,并且可以包含“。” 要么 ”/”。 但是我认为,有了足够的宏欺骗性,就可以在不更改构建脚本或文件系统的情况下完成此操作。

顺便说一句,我不确定这对C预处理器有什么看法,但是这里...

想法是构造一系列宏,这些宏将插入到MSVC 2015特定块中的bsl_stdhdrs_incpaths.h中。 这些宏中有几个基本上可以完成相同的操作,因此我已经考虑了解决方案,以便每个用户宏(例如BSL_NATIVE_C_LIB_HEADER)都可以通过通用宏FINDER实现。 实现宏的名称简短易读,它们应具有某种前缀。

请注意:这些示例仅在GCC 4.8.4,unstable,clang-3.5和MSVC 2015上进行了测试。

简单的想法

第一个线索是,即使BSL_NATIVE_C_LIB_HEADER(stddef.h)的参数包含奇数字符,它也不是单个标记。 它是列表{'stddef','。','h'}。

因此我们可以将stddef定义为../include/stddef,因为'。 并在扩展后附加“ h”! 但是更好的是,我们可以使用##将第一个标记粘贴到具有唯一名称的宏中,例如SELECTOR_stddef 然后可以根据每个文件名使用唯一路径#defined来定义它:

#define ANGLES(f) <f>
#define BSL_NATIVE_C_LIB_HEADER(file) ANGLES(SELECTOR_##file)

/* each can now have a different prefix */
#define SELECTOR_stddef ../ucrt/stddef
#define SELECTOR_stdarg ../include/stdarg

/* later on, down in the user code... */
#include BSL_NATIVE_C_LIB_HEADER(stddef.h) /* #include <../ucrt/stddef.h> */
#include BSL_NATIVE_C_LIB_HEADER(stdarg.h) /* #include <../include/stddef.h> */

所以这很好用! 唯一的问题是,每个被调用的文件都必须具有选择器。 如果不是这样,它将尝试包含一些看起来像#include <SELECTOR_stdio.h>愚蠢文件名,它将无法正常工作。 一些维护程序员可能会过来,并通过在/ usr / include目录中的SELECTOR_stdio.h中添加一堆符号链接来“修复”它,这对每个人来说都会很糟糕。

如果大多数文件都可以使用默认值,那将是非常不错的选择。 多数似乎已改组为... / ucrt,而仅编译器特定的部分留在... / include中。 因此,最好只是覆盖我们想要的那些。 但是即使以这种第一种形式,它也可以正常工作。 它具有简单的优点。

一堆SO问题帮助了这一点:

更完整的解决方案

/* presumably within an #if MSVC 2015 conditional in bsl_stdhdrs_incpaths.h */

#define DELIMITER(a) a

/* same as DELIMITER, but named to distinguish the MSVC __VA_ARGS__ bug */
/* workaround is fine to leave in place for standard compilers */
#define MSVCFIXER(a) a

/* add the angle brackets and re-attach the "rest" tokens */
#define FORMATER(x1, x2, pre, rest, ...) <DELIMITER(pre)rest>

/* if __VA_ARGS__ only has one argument, shift so that pre is the default
 * otherwise if __VA_ARGS__ has two, pre is the override */
#define SHIFTER(pre, rest, def, ...) MSVCFIXER(FORMATER(__VA_ARGS__, pre, rest, def))

/* expand the commas */
#define EXPANDER(...) MSVCFIXER(SHIFTER(__VA_ARGS__))

/* main implementation - pass both the selector override and default */
#define FINDER(file, defloc) \
    EXPANDER(HEAD_LOC_OVERRIDE_##file, DELIMITER(defloc)file,,)

/* now implement the top level macros */
#define BSL_NATIVE_C_LIB_HEADER(file) FINDER(file, HEAD_LOC_DEFAULT_PREFIX)

#define BSL_NATIVE_SYS_TIME_HEADER(file) FINDER(file, HEAD_LOC_DEFAULT_PREFIX)

#define BSL_NATIVE_CISO646_HEADER(file) FINDER(file, /tmp/)

/* maybe define a common default prefix, or hard code it like iso646
 * since most files appear to be in ucrt, make this the default
      (file.h) will become <../ucrt/file.h> */

#define HEAD_LOC_DEFAULT_PREFIX ../ucrt/

/* override any other files NOTE: the commas
 *   (stdarg.h) will become <../include/stdarg.h>
 *   (stdint.h) will become <../include/stdint.h> */

#define HEAD_LOC_OVERRIDE_stdarg ../include/stdarg,
#define HEAD_LOC_OVERRIDE_stdint ../include/stdint,

/* and you can even override the name part too, or remove or add the .h
 *   (where.h) will become <../somewhere/when> (note: use two commas)
 *   (sys/*.h) will become <../include/sys/*.h>
 *   (cstdio)  will become <windows.h> */

#define HEAD_LOC_OVERRIDE_where  ../somewhere/when,,
#define HEAD_LOC_OVERRIDE_sys    ../include/sys,
#define HEAD_LOC_OVERRIDE_cstdio windows.h,

/* later on, down in the user code... */

#include BSL_NATIVE_C_LIB_HEADER(stdarg.h)   /* <../include/stdarg.h */
#include BSL_NATIVE_C_LIB_HEADER(stdio.h)    /* <../ucrt/stdio.h */
#include BSL_NATIVE_C_LIB_HEADER(cstdio)     /* <windows.h> */
#include BSL_NATIVE_C_LIB_HEADER(what.h)     /* <../ucrt/what.h> */
#include BSL_NATIVE_C_LIB_HEADER(where.h)    /* <../somewhere/when> */
#include BSL_NATIVE_CISO646_HEADER(iso646.h) /* </tmp/iso646.h> */

您可以创建一个单独的目录../VC14-include-compat ,其中包含虚拟的包含文件,这些文件会重定向到每个标准包含文件的实际位置。

一个更简单的技巧是修补您的VC14包含目录以添加丢失的文件,并使它们明确地从新目录包含。

另外,为什么不使用另一种方法,包括在BSL包括来自文件bsl/bsl+bslhdrs目录而不是bsl/bsl+stdhdrs 您将需要修改源文件以包括例如<bsl_c_stddef.h>而不是<stddef.h> ,但这将使您的项目更具可移植性。

BDE库有170万行代码,祝您修复任何其他可移植性问题,祝您好运!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM