2016-02-05 42 views
0

我有一個模板類,旨在幫助進行枚舉字符串轉換。它基於this解決方案來自動初始化靜態變量。滿級如下:模板上靜態成員的初始化程序不總是在靜態鏈接庫中調用

template <typename LabelType> 
class EnumHelper 
{ 
public: 

    static const char* ToString(LabelType label) 
    { 
     return enumBimap_.left.at(label); 
    } 

    static LabelType ToEnum(const char* name) 
    { 
     return enumBimap_.right.at(name); 
    } 

    static bool IsInitialized() { return initialized_; } 

private: 
    typedef boost::bimap<LabelType, const char*> EnumBimap; 

    EnumHelper() = delete; 
    EnumHelper(const EnumHelper&) = delete; 
    static void Init(int numLabels, const char* names[]) 
    { 
     for (int i = 0; i < numLabels; i++) 
     { 
      enumBimap_.insert(EnumBimap::value_type((LabelType)i, names[i])); 
     } 

     initialized_ = true; 
    } 

    class Initializer 
    { 
    public: 
     Initializer(); 
    }; 

    static EnumBimap enumBimap_; 
    static bool initialized_; 
    static Initializer initializer_; 

    friend class Initializer; 
}; 

template <typename LabelType> 
typename EnumHelper<LabelType>::EnumBimap EnumHelper<LabelType>::enumBimap_; 

template <typename LabelType> 
bool EnumHelper<LabelType>::initialized_ = false; 

template <typename LabelType> 
typename EnumHelper<LabelType>::Initializer EnumHelper<LabelType>::initializer_; 

用於初始化是專門爲特定的枚舉Init方法和實例爲枚舉模板的宏:

#define INIT_ENUM_HELPER(labelType, labelNames, count) \ 
template <> \ 
EnumHelper<labelType>::Initializer::Initializer() \ 
{ \ 
    EnumHelper<labelType>::Init(count, labelNames); \ 
} \ 
template class EnumHelper<labelType>; 

現在我成功地利用這個在靜態鏈接到我的應用程序的庫中有兩種情況,但在另一種情況下,Init永遠不會被調用。初始化完全相同(在庫中的.cpp中調用宏)我已經遇到了一些可能性,因爲它無法正常工作,包括它沒有在庫本身中引用,並且枚舉是最初是一個班級的內部(抓住吸管),但我現在真的有點虧了。

在什麼情況下不會構建靜態Initalizer對象,因此不會調用Init?

+0

靜跨模塊初始化有一個臭名昭着的聲譽:https://isocpp.org/wiki/faq/ctors#static-init-order – PaulMcKenzie

+0

這似乎並沒有得到初始化,但它不僅僅是順序。 – FlintZA

回答

0

不是一個答案,以得到這個數據跨庫自動初始化,但並不依賴自動靜態初始化,並根據需要爲延遲初始化的替代實現:

// Helper for easily obtaining string representations of enum values and vice versa 
    template <typename LabelType> 
    class EnumHelper 
    { 
    public: 
     typedef boost::bimap<LabelType, std::string > EnumBimap; 

     static const char* ToString(LabelType label) 
     { 
      auto const& left = Instance().enumBimap_.left; 
      auto iter = left.find(label); 
      if (iter != left.end()) 
      { 
       return iter->second.c_str(); 
      } 
      else 
      { 
       return "UNDEFINED_ENUM_VALUE"; 
      } 
     } 

     static LabelType ToEnum(const char* name) 
     { 
      auto const& right = Instance().enumBimap_.right; 
      std::string str(name); 
      auto iter = right.find(str); 
      CHECK_THROW(iter != right.end(), ERROR_MISSING_VALUE, "ENUM"); 

      return iter->second; 
     } 

     static bool IsInitialized() { return true; } 

    private: 
     EnumHelper() = delete; 
     EnumHelper(const EnumHelper&) = delete; 
     EnumHelper(int numLabels, const char* names[]) 
     { 
      for (int i = 0; i < numLabels; i++) 
      { 
       std::string str(names[i]); 
       enumBimap_.insert(EnumBimap::value_type((LabelType)i, str)); 
      } 
     } 
     static const EnumHelper& Instance(); 

     EnumBimap enumBimap_; 
    }; 

    // Sets up a specific enum helper by having its instance accessor init with appropriate label names 
    #define INIT_ENUM_HELPER(labelType, labelNames, count) \ 
     template <> \ 
     const EnumHelper<labelType>& EnumHelper<labelType>::Instance() \ 
     { \ 
      static EnumHelper<labelType> enumHelper(count, labelNames); \ 
      return enumHelper; \ 
     } 

這是很容易設置爲一個特定的枚舉:

// In .h 
enum class GameStateLabels 
{ 
    LOADING, 
    INTRO, 
    PLAY, 
    RESULTS, 
    COUNT 
}; 
const char* GAME_STATE_LABEL_NAMES[]; 

// In .cpp 
const char* VSE::GAME_STATE_LABEL_NAMES[] = 
{ 
    "LOADING", 
    "INTRO", 
    "PLAY", 
    "RESULTS" 
}; 

INIT_ENUM_HELPER(GameStateLabels, GAME_STATE_LABEL_NAMES, (int)GameStateLabels::COUNT) 

如果任何人有原始問題的實際解決方案,我很樂於嘗試並接受它,如果工程:)