簡體   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