2014-10-06 55 views
1

我有一個類X,在x.h中聲明並在x.cpp中定義,它必須在靜態初始化階段運行一些代碼(在集中位置註冊一些類的元數據)宏INIT(X)。對於X的任何子類Y(在y.h中的聲明,包括x.h,在y.cpp中的定義)都是相同的 - 它必須在全局範圍內運行INIT(Y)。現在我想創建一個靜態檢查,如果每個子類已經初始化。另外我不知道我會鏈接多少個X子類。當子類沒有使用另一個宏時,C++宏將失敗

我要定義x.h宏如果存在的X一個​​(或X任何其他後裔),將產生一個編譯器錯誤已經不叫INIT(SubClass)。怎麼做?

要求:

  • C++ 11。
  • 如果需要,可能需要在subclass.cpp文件中調用它。
  • 可能需要​​將其全部定義在subclass.cpp中。
  • 我想支持的編譯器至少有gccmsvc
  • 它應該不會在導入x.hsomeotherclass.cpp中生成錯誤,除非它定義了X的子類。
  • 該錯誤可能是任何種類的編譯器錯誤,它不一定是#error,例如,未定義的變量也很好。
  • 此宏的代碼可能需要對X類進行其他更改,但不包括其任何子類。
  • INIT必須在放入subclass.cpp之後的任何地方都有效。

定義一個新的基類的X一個虛擬抽象方法,如果只有我沒有不修改任何X的子類的放在那裏的聲明和定義在INIT的要求會工作。

下面是該設計的模板代碼,其中INIT僅用於計算鏈接的X子類+ 1的數量。只要它有效,/*???*/可用任何東西代替。

x.h

#include <functional> 

int &someGlobalInt(); 

class XInit { 
public: 
    XInit(std::function<void()> init) { 
     init(); 
    } 
}; 

#define INIT(cls) static XInit X_INIT_ ## cls = XInit([](){ \ 
    ++someGlobalInt(); \ 
    /*???*/ \ 
}) 

class X { 
    /*???*/ 
}; 
/*???*/ 

x.cpp

#include "x.h" 

int &someGlobalInt() { 
    static int x = 0; 
    return x; 
} 

INIT(X); // error without it 

y.h

#include "x.h" 
class Y: public X {}; 

y.cpp

#include "y.h" 
INIT(Y); // error without it 

main.cpp

#include <cstdio> 
#include "x.h" 
// no error, since no new X subclass is defined 
int main() { 
    printf("%d\n", someGlobalInt()); // should print "2" 
    return 0; 
} 
+0

fyi,根據標準項目以雙下劃線開頭保留。 '17.6.3.3.2全局名稱[global.names]' ' - 包含雙下劃線_ _的每個名稱或以下劃線開頭並帶有大寫字母(2.12)的字符保留給實現以供任何用途。' – lcs 2014-10-06 18:19:35

+0

感謝您的信息,我會改變它。但主要問題仍然存在。 – Xilexio 2014-10-06 19:43:14

+0

@Xilexio我現在沒有時間,但如果今晚晚些時候這個問題仍然沒有答案,我會盡力爲你提供一個解決方案。 – Serge 2014-10-06 19:52:12

回答

1

由於N.M.在評論中提到,您可以使用奇怪的循環模板模式。你可以做這樣的事情,我認爲這是不是與這些lambda表達式亂搞更清晰:

XInit.h

template <class T> 
class XInit { 
    private: 
    static bool initialized; 
    static std::once_flag flag; 
    static void base_init<T>(){ std::call_once(flag, T::reserved_init);} 
... 
} 

XInit.cpp

template <class T> 
XInit<T>::initialized = XInit<T>::base_init(); 

現在,您可以定義宏作爲:

#define INIT(cls) static void reserved_init() {++someGlobalInt();} 

現在你繼承:

class X : public XInit<X> ... 

注意,如果你需要的層次,這很好,只是做:

class Y: public X, public XInit<Y> 

注意,沒有這裏的任何多重繼承,因爲XInit<X>XInit<Y>屬於不同類別。實際的工作是在我們繼續產卵的新基類中完成的。現在,基類將總是試圖調用它的派生類的靜態reserved_init成員。所以如果這個成員沒有定義,你會得到一個錯誤。因此,您需要將該宏放入類定義中。

一如既往,如果用戶真的想要,他們可以解決這個問題。例如,他們可以定義另一個具有相同名稱的函數。最終,我不認爲你可以創建一個故意濫用的系統,但它應該對付真實的錯誤。讓我知道你對這個解決方案的看法,也許我可以引入修改來修復它。

+0

但是這並不強制你從'Y'中的'XInit '繼承,是嗎?如果用戶不知道'XInit'和'INIT',只是不加它,編譯器就會很好地編譯它。然後它沒有達到它的目的 - 提醒開發者關於'INIT'或'XInit '的子類。如果你只記得這個事實,我已經有了可行的代碼(儘管我同意你有比我更清潔的解決方案)。 – Xilexio 2014-10-07 18:17:08

+0

我需要這個功能。提到:「強制ABC的每個派生類別做一次XYZ」 - 「力量」部分仍然缺失。 – Xilexio 2014-10-07 18:24:28

+1

我的意思是,我設置它的方式,它強制註冊XInit的所有孩子。如果你想強制X的所有孩子,那麼完全擺脫XInit並在X內部實現它的功能。然後X將是一個模板類,當你從X繼承時,你將需要在派生類型上模板X 。 – 2014-10-07 18:26:11