2014-03-27 135 views
3

我想初始化一個全球地圖CPP文件將被忽略

std::map<long, std::string> Global_ID_Mapper; 

擁有一批像「初始化」類:

struct AGlobalMapperInitializer 
{ 
    AGlobalMapperInitializer() 
    { 
     Global_ID_Mapper.insert(std::make_pair(1, "Value1")); 
     Global_ID_Mapper.insert(std::make_pair(2, "Value2")); 
    } 
}; 

我想,以填補在應用程序啓動時自動映射所以在我的一個cpp文件中,我只是定義了一個「init」類的全局變量。

// AGlobalMapperInitializer.cpp 

AGlobalMapperInitializer AGlobalMapperInitializer_Value; 

映射器填充是AGlobalMapperInitializer_Value創建的副作用。

問題是,如果cpp不包含除此全局變量之外的任何內容,則cpp明顯被鏈接器忽略。當我將一些有用的其他代碼放入cpp中(或者在一些非空cpp中定義全局初始化器時),調用構造函數並填充全局映射器。但是,如果cpp只包含沒有在其他文件中引用的全局變量,則會編譯cpp,obj文件包含該變量,但鏈接程序在鏈接期間未提及它,並且在exe中未被提及。

我該如何堅持將cpp鏈接到exe? 是否有一些編譯指示或虛擬代碼放入cpp中以使其不被忽略? 我使用Visual Studio 2012

+1

一個常見的選擇是首次使用初始化全局/單例。 – MooseBoys

+0

您可能(Dll)導出整個類AGlobalMapperInitializer並使全局成爲該類的靜態成員,或者將標題中的全局聲明爲extern(Dll)在類外部導出。 (否則,全局是一個本地化的翻譯單元,並進行了優化) –

+0

鏈接器會拋棄所有未引用的對象。除了聲明不應該導出的導出外,您可以在任何函數中引用它。請注意編譯器無法優化此引用,例如'main(){printf(「」,&AGlobalMapperInitializer_Value)); }' – harper

回答

1

感謝Angew和harper幫助找到解決方案。

有2個可能的解決方案:

  1. 便攜式一個(但不是因爲其性能好)。

    初始化器實例可以不在專用cpp文件中定義,但在h中使用單詞static

    // AGlobalMapperInitializer.h 
    // It wasn't mentioned before that this h file is included in many cpp files 
    struct AGlobalMapperInitializer 
    { 
        AGlobalMapperInitializer() 
        { 
         if(!Global_ID_Mapper.insert(std::make_pair(1, "Value1")).second) 
          return; 
         Global_ID_Mapper.insert(std::make_pair(2, "Value2")); 
        } 
    }; 
    
    static AGlobalMapperInitializer AGlobalMapperInitializer_Value; 
    

    由於變量是static,所述AGlobalMapperInitializer_Value在所有的CPP文件,其中包括第h文件單獨創建。這些變量中的每一個都試圖爲全局映射器添加值。當然,只有其中一個成功。通過檢查第一個插入的結果部分解決了性能問題。如果第一個值已經插入 - 不需要嘗試其他值。

    注意:假設所有這些實例將在同一線程內創建,否則全局映射器應實現插入調用的同步。

  2. 特定於編譯器的解決方案(Visual Studio)。

    鏈接器可以通過添加#pragma comment (linker, "/include:<decorated name>")來強制保留課程。裝飾名稱可以是類構造函數或任何其他函數。問題在於指定裝飾名稱硬編碼不好也不方便。裝飾方法可以隨編譯器升級而改變。所以,這裏可以使用__FUNCDNAME__

    struct AGlobalMapperInitializer 
    { 
        AGlobalMapperInitializer() 
        { 
         // Make sure the class will not be threw away by linker 
         #pragma comment (linker, "/include:"__FUNCDNAME__) 
    
         Global_ID_Mapper.insert(std::make_pair(1, "Value1")); 
         Global_ID_Mapper.insert(std::make_pair(2, "Value2")); 
        } 
    }; 
    

    幸運的是,#pragma可以在類的構造函數中指定。

2

C++不需要一個全局變量x的初始化過程的地方,如果沒有來自同一個文件(實際上是翻譯單元)作爲x函數和變量是不斷引用。

見C++ 11 [basic.start.init]§4

這是實現定義的非本地變量,靜態存儲持續時間的動態是否初始化的main的第一條語句之前完成。如果初始化被推遲到main的第一條語句之後的某個時間點,它應該在第一個odr-use(3.2)之前出現,這個函數或變量在同一個翻譯單元中定義的變量被初始化。

如此給力的變量初始化,你必須把它送到他們的其他內容你實際使用的文件,或直接某處使用變量。

+0

很好,它完全解釋了我的問題。 但是,你對這個問題有任何想法: '是否有一些編譯指示或虛擬代碼放入cpp以使其不被忽略?' – Kunis

+0

@ user940014'#編譯指示是依賴於編譯器的,因此請參閱編譯器的文檔。至於「虛擬代碼」,我的答案列出了選項:使用變量,或將它們與你使用的某些東西(也包括「放入其他東西並使用它」)相關聯。 – Angew

+0

@Andew我抓住了你的權利:我不能避免改變一些其他的CPP? 我的意思是,如果我甚至在第一個cpp中放置了一些東西(虛擬代碼),我仍然需要在一些將使用這個虛擬代碼的cpp中添加一些代碼。 – Kunis

1

鏈接器不包含變量,並且它在未被引用時是初始值設定項。您可以將您的代碼中創建這個變量的引用:

int main() 
{ 
    printf("", &AGlobalMapperInitializer_Value)); 
} 

如果你想避免的源代碼這種污染你會與鏈接器的/INCLUDE參數相同的效果。當你嘗試上面的黑客時,你必須添加你可以從.map文件中獲取的裝飾名稱。

VS2010在項目屬性中提供了Force Symbol Reference這個選項:Configuration Properies - > Linker - > Input。我希望VS2012也一樣。

+0

謝謝。我錯過了'/ INCLUDE'選項應該與裝飾名稱一起使用(儘管很明顯應該是這種方式)。 所以現在我在我的h文件中添加了'#pragma comment(linker,「/ include:?AGlobalMapperInitializer_Value @@ 3VAGlobalMapperInitializer @@ A」)'並且得到了我想要的。然而,如果將'#pragma'添加到忽略的cpp中,則不起作用。 – Kunis

+0

但是用#pragma你會污染你的源代碼。我會考慮將其隱藏在項目屬性中。 ;-) – harper

+0

問題是,我有一些靜態庫,最近編譯成幾個exe文件。更改lib項目的'強制符號引用'不會有幫助。無論如何,鏈接器會拋出該符號,爲了保持它,我必須更改最終鏈接項目的'強制符號引用'。但是我想在lib的級別上保留lib的所有細節。此外,我無法訪問鏈接庫的一些項目,我將改變。 我發現工作的唯一方法 - '#pragma'。而且我也可以避免使用'__FUNCDNAME__'指定裝飾名稱硬編碼。 – Kunis