Condition compilation相信大家都很熟了,相信不用多介紹了,以下是一個示範案例
1 2 3 4 5 6 7 8 9 10
| struct Proxy { #ifdef FEATURE_ENABLE int v; #endif void do_something() { #ifdef FEATURE_ENABLE v = 42; #endif } };
|
不過在C++20 Module的世界中,要求有穩定,一致的ABI
Preprocessor格格不入,必須想個方法轉換,以下是我的思路
Step1: Create static constexpr variable
1 2 3 4 5
| #ifdef FEATURE_ENABLE static constexpr bool enable_feature_v = true; #else static constexpr bool enable_feature_v = false; #endif
|
這一步就是將Compiler所收到的Definition變成constexpr variable
Step2: Failed attempt
試著翻譯上面的程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| template <bool> struct ProxyImpl {};
template <> struct ProxyImpl<true> { int v; };
struct Proxy : public ProxyImpl<enable_feature_v> { void do_something() { if constexpr (enable_feature_v) { v = 42; } } };
|
當enable_feature_v
為false
的時候會報錯
1 2 3 4 5 6 7
| **<source>:** In member function '**void Proxy::**do_something****()':
**<source>:14:25:** **error:** '**v**' was not declared in this scope
14 | **v** = 42;
|
|
不管enable_feature_v
值為何,Proxy底下一定要有個member v
存在,就算繼承改成member也是同樣的情形
Step3: std::optional
在Runtime決定狀態,多了一點Runtime overhead,不過至少可行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| struct ProxyImpl { int v; };
struct Proxy { std::optional<ProxyImpl> v; Proxy() { if constexpr (enable_feature_v) v.emplace(); }
void do_something() { if constexpr (enable_feature_v) { (*v).v = 42; } } };
|
Step4: Revisit template specialization
重回失敗的第二步,不過這次是定義函數為操作單位
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| template <bool> struct ProxyImpl { void update(int) {} };
template <> struct ProxyImpl<true> { int v; void update(int newV) { v = newV; } };
struct Proxy : public ProxyImpl<enable_feature_v> { void do_something() { if constexpr (enable_feature_v) { update(42); } } };
|
根據輸出的Assembly Code,空的update
會被編譯器整個偵測到,完全消失
不過這方法也很麻煩,要同時維護好幾份的Function Set,就算是空的也要維護
Step5: Condtional Proxy
如果將feature flag帶入Type裡面,則可以解決上面的問題,且帶來其他的問題
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| template <bool> struct ProxyImpl {};
template <> struct ProxyImpl<true> { int v; };
template <bool enable_feature> struct Proxy { ProxyImpl<enable_feature> v; void do_something() { if constexpr (enable_feature) { v.v = 42; } } };
int main() { Proxy<enable_feature_v> p; p.do_something(); }
|
因為他是template class,所以在傳統的C++,只能放在Header Unit,如果是Module世界的話,只能放在Module interface unit,不能放到Module Implementation Unit
Conclusion
目前沒有什麼十全十美的好方法,可能需要進一步的探索