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