[英]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.