2017-03-06 40 views
0

有什麼辦法可以在最終的二進制文件中合併模板類的相同成員函數嗎?我有可能支持特定的功能,並依賴於一個類,可能需要一些額外的步驟裏面的成員函數:根據實際g ++:合併模板成員函數實例

Driver<Feature::A>::setDriver<Feature::A | Feature::B>::set
template <uint32_t features> 
class Driver { 
    static bool set (uint32_t value) { 
     /* do something required for every feature */ 
     if (features & Feature::A) 
     /* do special things if Feature::A */ 
     /* do something required for every feature */ 
     if (features & Feature::B || features & Feature::C) 
     /* do something special for either Feature::B or Feature::C */ 
     return true; 
    } 
    /* more, similar methods */ 
}; 

在代碼後,我會再使用電話可用功能。到目前爲止,所有這些工作都很順利,只有使用g++-5.4.0 -std=c++11 -O3編譯的已發佈代碼纔會合併完全相同的方法,所以我希望使用它。在這個類中具有多個功能,其中主要的共同部分,真正打破了最終的二進制文件的大小。在使用私有函數用於公用部分減小了尺寸的開銷,但不是友好的在我看來閱讀:

template <uint32_t features> 
class Driver { 
    static void _set_common_0 (value) { 
     /* do something required for every feature */ 
    } 
    static bool set (uin32_t value) { 
     _set_common_0 (value); 
     if (features & Feature::A) 
      _set_special_0 (value); 
     _set_common_1 (value); 
     if (features & Feature::B || features & Feature::C) 
      _set_special_1 (value); 
     return true; 
    } 
}; 

而且這引入了問題,如果是在特殊的一個一個「早出」路徑部分,以前可以通過一個簡單的return現在可以完成從子功能升級,根據這些子功能的返回值進行進一步檢查...

我想要的是編譯器爲Driver<Feature::B>::setDriver<Feature::C>::set發出(使用時)符號,但讓它們通過代碼指向相同的代碼位置,因爲它們是相同的。任何想法我怎麼能做到這一點? (優選地,用C++ 11保持,且不需要從一個較新的標準功能)

編輯:爲了澄清,我想知道,爲什麼相同的指令序列(由模板實例此處產生)不被gcc組合。據我的理解,-ftree-tail-merge應該至少替換那些跳轉到相同的實現/代碼序列(例如Driver<Feature::B>::setDriver<Feature::C>::set)。

+1

您真的需要一個類模板嗎?因爲這不是應該如何使用模板:你正在運行時檢查編譯時間常量:'if(features&Feature :: A)' –

+0

它不是一個真正的編譯時間常量。應該使用的功能是運行時依賴和編譯過程中不知道的。在運行時環境的Dependend上,我可以使用'Feature :: A'或不能使用它。 '驅動程序 :: set'調用不是直接發出,而是通過在初始化期間分配的函數指針,依賴於檢測到的環境特徵。 – Jonas

回答

0

從您發佈的代碼中可以清楚地看到您濫用模板。正確的方法是定義的Driver部分特例:

template <uint32_t features> 
class Driver; // There may be some default implementation, but it is not necessary 

// Specialize for feature A only 
template <> 
class Driver<Feature::A> { 
    bool set (uint32_t value) { 
     /* do special things for Feature::A */ 
     /* do something required for every feature */ 
     return true; 
    } 
}; 

// Specialize for feature B only 
template <> 
class Driver<Feature::B> { 
    bool set (uint32_t value) { 
     /* do special things for Feature::B */ 
     /* do something required for every feature */ 
     return true; 
    } 
}; 

// Specialize for features A and B 
template <> 
class Driver<Feature::A | Feature::B> { 
    bool set (uint32_t value) { 
     /* do special things for Feature::A */ 
     /* do special things for Feature::B */ 
     /* do something required for every feature */ 
     return true; 
    } 
}; 

模板方法的好處是,你沒有運行時開銷由於如果某些功能被啓用檢查。當然,如果有很多功能,將會有大量的功能組合;因此輸出二進制的膨脹。這就是爲什麼我建議擺脫類模板並使用運行時函數參數;例如,將特徵傳遞給構造函數:

class Driver { 
private: 
    uint32_t features_; 

public: 
    Driver(uint32_t features) : features_(features) {} 
    bool set (uint32_t value) { 
     /* do something required for every feature */ 
     if (features_ & Feature::A) 
     /* do special things if Feature::A */ 
     /* do something required for every feature */ 
     if (features & Feature::B || features & Feature::C) 
     /* do something special for either Feature::B or Feature::C */ 
     return true; 
    } 
}; 
+0

我看不到您的第一個解決方案對於我的解決方案的好處。在不斷的摺疊之後,模板實例化的代碼將是相同的 - 隨着回退,公共部分必須通過解決方案多次寫入(在每個主體中)。隨着未來的變化,人們可能會錯過一個可能引入問題/錯誤的機構。 模板用於減少運行時間開銷,因爲我知道每次調用時可以使用哪些功能。我有*一個*檢查在開始選擇正確的控制流程。 問題是,爲什麼'gcc'不合並相同的代碼部分。 – Jonas

+0

你說得對。這就是爲什麼你不需要模板,而應該使用第二種解決方案。我剛剛舉了一個例子,說明如何使用模板*,如果你決定需要它們的話。 –

+0

使用第二種解決方案,我介紹了不需要/不需要的運行時檢查。我現在已經在編譯時調用了一個函數,這個函數具有我可用的功能,所以這應該是可能的(或者也是可能的)。仍然存在的問題是,我怎樣才能使'gcc'結合模板實例產生相同的代碼。例如。 '驅動程序 :: set'和'驅動程序 :: set'將是相同的,但會發出兩次 - 導致兩倍的大小。 對於你說我應該使用模板的方式:與我的解決方案相比,你的解決方案有什麼好處(以便我將使用你的解決方案)? – Jonas