【问题标题】:Cross platform way to include system header files, when there is an identically named file in path?当路径中有同名文件时,跨平台方式包含系统头文件?
【发布时间】:2016-02-21 00:15:26
【问题描述】:

我正在尝试让Bloomberg's BDE library 在 Visual Studio 2015 中编译。因为它们重新实现了通常由编译器提供的标准库,所以有些头文件的名称与标准库名称完全匹配,例如 @ 987654325@。它们可选地允许您关闭标准库的覆盖,并且为了促进这一点,它们重新实现的文件将可选地仅包含原始编译器提供的版本,例如stddef.h。他们这样做包括通过以下宏:

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

Source

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

Source

问题在于 Visual Studio 2015 introduces some refactoring一些 C 标准库头文件移动到如下路径:C:\Program Files (x86)\Windows Kits\10\Include\10.0.10150.0\ucrt。这显然意味着&lt;../include/filename&gt; 将不再找到移动的文件。问题是所有文件都没有移动。例如,iso646.h 仍然在 C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include 中,将被包含。

简而言之,这是我的问题:有没有一种方法可以让我继续支持正在使用的 BSL_NATIVE_C_LIB_HEADER 宏,同时在幕后确定导入是否应该来自 ../ucrt/../include,基于文件名?我知道我可以创建两个单独的宏,但如果可能的话,我宁愿保持界面相同。

【问题讨论】:

  • “他们重新实现了编译器通常提供的标准库”实现库的愚蠢方式。

标签: c++ c visual-studio macros


【解决方案1】:

理想情况下,类似 BSL_NATIVE_C_LIB_HEADER() 的宏将继续工作,而在幕后有一种机制可以根据文件名覆盖它们以指向不同的目录。起初这似乎是不可能的,因为宏的参数是一个文件名并且可能包含“。”要么 ”/”。但是有了足够的宏傻瓜,我认为它仍然可以在不更改构建脚本或文件系统的情况下完成。

顺便说一句,我不确定这对 C 预处理器有什么意义,但这里是……

这个想法是构建一系列宏,这些宏将插入到 MSVC 2015 特定块中的 bsl_stdhdrs_incpaths.h 中。有几个这样的宏基本上做同样的事情,所以我已经考虑了解决方案,以便每个用户宏(如 BSL_NATIVE_C_LIB_HEADER)都是根据通用宏 FINDER 实现的。为了便于阅读,实现宏具有短名称,它们应该有某种前缀。

警告:这些示例仅在 GCC 4.8.4 和不稳定、clang-3.5 和 MSVC 2015 上进行了测试。

简单的想法

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

所以我们可以将 stddef #define 为 ../include/stddef 并且它会起作用,因为 '.'和 'h' 在扩展后被附加!但更好的是,我们可以使用## 将第一个标记粘贴到具有唯一名称的宏中,例如SELECTOR_stddef。然后可以使用基于每个文件名的唯一路径#定义:

#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 &lt;SELECTOR_stdio.h&gt; 的愚蠢文件名,它不会工作得那么好。一些维护程序员可能会通过在 /usr/include 目录中添加一堆符号链接到 SELECTOR_stdio.h 来“修复”它,这对每个人来说都会很糟糕。

如果可以为大多数文件设置默认值,真正会更好。大多数似乎已被改组到 .../ucrt 中,并且只有编译器特定的留在 .../include 中。所以最好覆盖我们想要的。但即使是第一种形式,它也可以正常工作。它的优点是简单。

一堆帮助解决这个问题的问题:

更完整的解决方案

/* 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> */

【讨论】:

  • 咦,为什么在代码块中高亮了“when”和“where”?
  • 仅供参考 - 对 SHIFT 的参数名称进行小幅编辑。最初他们都被命名为“滑稽”。当我将所有内容重命名为“rest”时,第三个参数没有意义。行为没有区别,但应该明确哪个项目在哪个参数中。
【解决方案2】:

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

一个更简单的技巧是修补您的 VC14 包含目录以添加丢失的文件,并使它们显式地包含在 new 目录中。

另外,为什么不使用替代方法并包含来自bsl/bsl+bslhdrs 目录而不是bsl/bsl+stdhdrs 的bsl 包含文件。您需要修改您的源文件以包含例如&lt;bsl_c_stddef.h&gt; 而不是&lt;stddef.h&gt;,但这将使您的项目更具可移植性。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-12
    • 2014-06-09
    • 2014-10-21
    • 1970-01-01
    • 2013-05-25
    • 2021-07-11
    相关资源
    最近更新 更多