2012-03-17 81 views
6

我正在用C++ 11編寫一個小內核,並且有兩個相同類型的實例必須在創建任何其他靜態對象之前構建。控制靜態對象的構造函數的順序

我寫的代碼如下:

// test.hpp 
class test { 
    // blahblah... 
}; 

// test.cpp 
typedef char fake_inst[sizeof(test)] __attribute__((aligned(alignof(test)))); 

fake_inst inst1; 
fake_inst inst2; 

// main.cpp 
extern test inst1; 
extern test inst2; 

int kmain() { 
    // copy data section 

    // initialize bss section 

    new (&inst1) test(); 
    new (&inst2) test(); 

    // call constructors in .init_array 

    // kernel stuffs 
} 

它建立並如預期沒有任何警告信息,但不能與LTO工作。

我得到了大量的警告消息,抱怨類型匹配,我不知道是否有一個解決方法,因爲它混淆我找到其他'真正'的警告或錯誤消息。

有什麼建議嗎?

+0

你究竟在做什麼?在下面對Pubby的回答的評論中,你說你想要初始化幾個相同類型的對象。對我來說,在讓實現調用其他構造函數之前,你似乎想要初始化與C++支持相關的結構,比如堆。但是,這聽起來不像你想要的。另外,你會得到什麼樣的「類型匹配」警告? – Potatoswatter 2012-03-17 09:24:52

+0

@Potatoswatter很多警告:'xxx'類型與原始聲明[默認啓用]不匹配。這是因爲實際類型不是測試,而是char []。我以爲我可以禁用靜態構造函數,但似乎沒有辦法做到這一點。 – kukyakya 2012-03-17 10:37:36

+0

啊,我明白了。你應該做的是'char fake_inst1 [sizeof(test)]; test * const inst1 = reinterpet_cast < test * >(fake_inst1);' – Potatoswatter 2012-03-17 11:44:57

回答

0

也許這樣?

// ... .h 
template<typename T> 
union FakeUnion { 
    FakeUnion() {} 
    ~FakeUnion() {} 

    T inst; 
}; 

extern FakeUnion<test> inst1_; 
extern FakeUnion<test> inst2_; 
static constexpr test& inst1 = inst1_.inst; 
static constexpr test& inst2 = inst2_.inst; 
// ... .h end 

// ... .cpp 
FakeUnion<test> inst1_; 
FakeUnion<test> inst2_; 
// ... .cpp end 

main,你就可以說new (&inst1) test;。它現在不應再給出有關類型不一致違規的警告,因爲與代碼中不同,此代碼不包含在不同文件中具有不同類型的變量。

+0

雖然這確實解決了名稱衝突問題,但它並不能確保「inst1」和「inst2」的構造函數在任何其他之前被調用。 – 2012-03-17 15:52:09

+0

@Matt如果他在'main'中用新的位置調用它們,它確實可以確保所有其他的ctors都以受控的方式在'main'之後被調用。 – 2012-03-17 18:03:06

+0

您能否就這裏發生的「魔術」提供建議?即爲什麼我們需要一個工會,爲什麼工會有工會? – Farcaller 2013-11-11 16:13:29

1

您可以使用GCC的init_priority屬性嗎?

Some_Class A __attribute__ ((init_priority (2000))); 
Some_Class B __attribute__ ((init_priority (543))); 

http://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html#C_002b_002b-Attributes

+0

我已經閱讀過關於這方面的內容,但由於優先級事件遍佈源文件,因此很難管理構造函數的順序。 – kukyakya 2012-03-17 07:12:45

+0

@kukyakya所以問題是首先初始化這兩個對象,或者一般地命令所有全局構造函數? – Potatoswatter 2012-03-17 08:47:07

+0

@Patatoswatter實際上它不是關於兩個實例,而是同一個類型的多個實例,所以我不能使用singleton模式。其他靜態對象的構建順序無關緊要。 – kukyakya 2012-03-17 08:59:47

0
// in an .h file 

typedef char fake_inst[sizeof(test)] __attribute__((aligned(__alignof__(test)))); 

extern fake_inst fake_inst1; 
extern fake_inst fake_inst2; 

inline test& f_inst1() { return *reinterpret_cast<test*>(fake_inst1); } 
inline test& f_inst2() { return *reinterpret_cast<test*>(fake_inst2); } 

// and for readability 

static test& inst1 (f_inst1()); 
static test& inst2 (f_inst2()); 

希望既inst1f_inst1()將被優化的。

+0

這正是我使用的方式,但我想知道是否有一個更好的方法。我想要像'std :: cout'或'std :: cin'這樣的全局對象,因爲它比實例返回函數更具可讀性。 – kukyakya 2012-03-17 09:06:00

+0

查看更新版本。 – 2012-03-17 15:02:19

+0

也許嘗試一個全局引用變量,初始化爲'inst1'的retval?這也是優化的(在msvc中) – valdo 2012-03-17 15:14:46

2

C++不提供管理跨多個文件的全局對象初始化順序的方法。如果你需要管理這些對象的初始化順序這個緊緊的話,那麼我強烈建議你而不是使它們成爲全局對象。使它們成爲包含靜態對象並返回指向它們的全局函數。

但即使如此,這比全手動初始化更不危險。只要指出那些需要它們的對象(最好不是全局的)就可以使用這些對象,並且你會沒事的。

+0

我考慮過使用全局函數返回引用或指向函數範圍的靜態對象的指針,但是編譯器生成獲取/釋放的東西來創建實例,並且獲取/釋放的東西用內核互斥體實現,而內核互斥體不能在內核中使用初始化。 – kukyakya 2012-03-17 09:02:29

+0

@kukyakya:然後你將不得不手動初始化這些對象。這是確保沒有內核互斥的唯一方法。 – 2012-03-17 09:08:23