简体   繁体   English

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

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

I am trying to get Bloomberg's BDE library to compile in Visual Studio 2015. Because they re-implement the standard libraries typically provided by the compiler, there are header files that have names that exactly match the standard library names, such as stddef.h . 我正在尝试让彭博(Bloomberg)的BDE库在Visual Studio 2015中进行编译。由于它们重新实现了通常由编译器提供的标准库,因此有些头文件的名称与标准库名称完全匹配,例如stddef.h They optionally allow you to turn off the overriding of the standard library, and to facilitate this, the files they re-implemented will optionally just include the original compiler supplied version such as stddef.h . 它们可选地允许您关闭标准库的覆盖,并且为了方便起见,重新实现的文件将可选地仅包括原始编译器提供的版本(例如stddef.h They do this include through macros such as the following: 它们通过以下宏来执行此操作:

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

Source 资源

Where BSL_NATIVE_C_LIB_HEADER expands to something like this: 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 资源

The issue is that Visual Studio 2015 introduces some refactoring that moves some of the C Standard Library header files to a path like this: C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.10150.0\\ucrt\u003c/code> . 问题是Visual Studio 2015 引入了一些重构 ,该重构一些 C标准库头文件移动到如下路径: C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.10150.0\\ucrt\u003c/code> 。 This obviously means that <../include/filename> will no longer find the moved files. 显然,这意味着<../include/filename>将不再找到移动的文件。 The issue is that all files have not moved. 问题是所有文件都没有移动。 For example, iso646.h is still in C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\include and will be picked up by the include. 例如, iso646.h仍在C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\include ,并将被C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\include拾取。

So here's my question in a nutshell : Is there a way I can continue to support the BSL_NATIVE_C_LIB_HEADER macro being used, while behind the scenes figuring out whether the import should be from ../ucrt/ or ../include , based on the file name? 因此,这是我的问题 :可以继续支持BSL_NATIVE_C_LIB_HEADER宏,同时在../include根据文件确定导入是来自../ucrt/还是../include ,这是我的问题 ../include名称? I know I could create two separate macros, but I'd rather keep the interface the same if possible. 我知道我可以创建两个单独的宏,但是如果可能的话,我宁愿保持相同的接口。

Ideally, the BSL_NATIVE_C_LIB_HEADER()-like macros would continue to work, while behind the scenes there is a mechanism to override some them based on filename to point at a different directory. 理想情况下,类似于BSL_NATIVE_C_LIB_HEADER()的宏将继续工作,而在幕后有一种机制可以根据文件名覆盖某些宏,以指向其他目录。 At first it seems impossible because the argument to the macros is a filename and may contain "." 最初,这似乎是不可能的,因为宏的参数是文件名,并且可以包含“。” or "/". 要么 ”/”。 But with enough macro-foolery I think it can still be done without changing the build scripts or the filesystem. 但是我认为,有了足够的宏欺骗性,就可以在不更改构建脚本或文件系统的情况下完成此操作。

BTW, I'm not sure what this says about the C preprocessor, but here goes... 顺便说一句,我不确定这对C预处理器有什么看法,但是这里...

The idea is to construct a series of macros that would be inserted into bsl_stdhdrs_incpaths.h in a MSVC 2015 specific block. 想法是构造一系列宏,这些宏将插入到MSVC 2015特定块中的bsl_stdhdrs_incpaths.h中。 There are several of these macros that do basically the same thing, so I have factored the solution so that each user macro like BSL_NATIVE_C_LIB_HEADER is implemented in terms of a common macro FINDER. 这些宏中有几个基本上可以完成相同的操作,因此我已经考虑了解决方案,以便每个用户宏(例如BSL_NATIVE_C_LIB_HEADER)都可以通过通用宏FINDER实现。 The implementation macros have short names for readability, they should get some sort of prefix. 实现宏的名称简短易读,它们应具有某种前缀。

Caveat: these examples have only been tested on GCC 4.8.4 and unstable, clang-3.5, and MSVC 2015. 请注意:这些示例仅在GCC 4.8.4,unstable,clang-3.5和MSVC 2015上进行了测试。

Simple Idea 简单的想法

The first clue is that even if the argument to BSL_NATIVE_C_LIB_HEADER(stddef.h) contains odd characters, it is not a single token. 第一个线索是,即使BSL_NATIVE_C_LIB_HEADER(stddef.h)的参数包含奇数字符,它也不是单个标记。 It is the list {'stddef', '.', 'h'}. 它是列表{'stddef','。','h'}。

So we could #define stddef to ../include/stddef and it would work because the '.' 因此我们可以将stddef定义为../include/stddef,因为'。 and 'h' get appended after expansion! 并在扩展后附加“ h”! But better, we can use ## to paste the first token into macro with a unique name, say SELECTOR_stddef . 但是更好的是,我们可以使用##将第一个标记粘贴到具有唯一名称的宏中,例如SELECTOR_stddef This can then be #defined with a unique path based on each filename: 然后可以根据每个文件名使用唯一路径#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> */

So this is working great! 所以这很好用! The only problem is that every single file that is called must have a selector. 唯一的问题是,每个被调用的文件都必须具有选择器。 If not, it will try to include some stupid filename that looks like #include <SELECTOR_stdio.h> and it won't work that well. 如果不是这样,它将尝试包含一些看起来像#include <SELECTOR_stdio.h>愚蠢文件名,它将无法正常工作。 Some maintenance programmer will probably come around and "fix" it by adding a bunch of symlinks to SELECTOR_stdio.h in the /usr/include directory and it will just end badly for everyone. 一些维护程序员可能会过来,并通过在/ usr / include目录中的SELECTOR_stdio.h中添加一堆符号链接来“修复”它,这对每个人来说都会很糟糕。

What would be really nice is if there could be a default for most files. 如果大多数文件都可以使用默认值,那将是非常不错的选择。 Most appear to have been shuffled into .../ucrt and only compiler specific ones are left in .../include. 多数似乎已改组为... / ucrt,而仅编译器特定的部分留在... / include中。 So it would be nice to just override the ones we want. 因此,最好只是覆盖我们想要的那些。 But even in this first form, it may work just fine. 但是即使以这种第一种形式,它也可以正常工作。 It has the advantage of simplicity. 它具有简单的优点。

A bunch of SO questions that helped with this: 一堆SO问题帮助了这一点:

A More Complete Solution 更完整的解决方案

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

You can create a separate directory ../VC14-include-compat that contains dummy include files that redirect to the actual location of each standard include file. 您可以创建一个单独的目录../VC14-include-compat ,其中包含虚拟的包含文件,这些文件会重定向到每个标准包含文件的实际位置。

A simpler hack is to patch your VC14 include directory to add the missing files and make them include from the new directory explicitly. 一个更简单的技巧是修补您的VC14包含目录以添加丢失的文件,并使它们明确地从新目录包含。

Also, why not use the alternative method and include the bsl include files from the bsl/bsl+bslhdrs directory instead of the bsl/bsl+stdhdrs . 另外,为什么不使用另一种方法,包括在BSL包括来自文件bsl/bsl+bslhdrs目录而不是bsl/bsl+stdhdrs You will need to modify your source files to include for example <bsl_c_stddef.h> instead of <stddef.h> , but that will make your project more portable. 您将需要修改源文件以包括例如<bsl_c_stddef.h>而不是<stddef.h> ,但这将使您的项目更具可移植性。

The BDE library has 1.7 million lines of code, good luck trying to fix any other portability issues! BDE库有170万行代码,祝您修复任何其他可移植性问题,祝您好运!

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

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