模板上静态成员的初始化程序并不总是在静态链接库中调用

时间:2016-02-05 15:44:33

标签: c++ templates enums

我有一个模板类,旨在帮助进行枚举字符串转换。它基于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?

1 个答案:

答案 0 :(得分: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)

如果有人对原始问题有实际解决方案,我很乐意尝试并接受它,如果它有效:)