使用静态初始化的副作用进行一次性初始化

Use side effect of static initialization for one-time initialization

本文关键字:初始化 一次性 副作用 静态      更新时间:2023-10-16

我想初始化共享库中的一些查找表,我想看看这是否是一种有效的方法,以及它是否继续在我将要编写的更多库中使用它。

typedef std::map<std::string, int> NAME_LUT;
NAME_LUT g_mLUT;
namespace
{
    bool OneTimeInit() 
    {
        ::g_mLUT.insert(NAME_LUT::value_type("open",          1));
        ::g_mLUT.insert(NAME_LUT::value_type("close",         2));
        return true;
    }
    bool bInit = OneTimeInit(); // Just to make initialization happen
}

它似乎在Visual Studio和gcc(Linux)上都能正常工作。只有 gcc 抱怨bInit没有在任何地方使用。

  1. 是否有可能初始化被优化(bInit不使用),或者语言不允许(因为副作用)。
  2. 它确实看起来像是处理一次性初始化的良好跨平台方式,但我不确定这是否是最佳方法。
  3. OneTimeInit声明为静态有意义吗?(即使用 static bool OneTimeInit() {...} ),或单独使用命名空间是使其对该编译单元唯一的更好方法

我不太喜欢使用静态存储的变量的想法,但是如果您要这样做,实际上可以通过编写一个将初始化对象的函数来简化代码:

typedef std::map<std::string, int> NAME_LUT;
namespace {
   NAME_LUT create_lut() {
        NAME_LUT table;
        table.insert(NAME_LUT::value_type("open",          1));
        table.insert(NAME_LUT::value_type("close",         2));
        return table;
   }
}
NAME_LUT g_mLut = create_lut();

请注意,这具有所有常见的初始化顺序问题(跨不同的翻译单元,特别是动态库)

是的,这是合法的,但由于您提到它在库中,因此必须确保在其中创建此翻译的翻译将被链接:

如何在库中强制包含"未使用"的对象定义

如果您有 C++11 个初始值设定项列表,这对您来说会更好吗?

NAME_LUT g_mLUT = { {"open", 1}, {"close", 2}, };

如果bInit被优化出来(可能,特别是如果你的库被dlopen编辑),那么你的初始化代码将不会运行。

让用户在理智的地方(main启动后)对库进行设置调用是否有问题?这样做可以消除代码中出现许多可能的难以调试的初始化顺序错误之一的任何可能性。如果这不是一个选项,我真的建议将 init 隐藏到具有静态本地的函数调用中,如下所示:

typedef std::map<std::string, int> NAME_LUT;
namespace
{
    bool OneTimeInit(NAME_LUT& mLUT) 
    {
        ::mLUT.insert(NAME_LUT::value_type("open",          1));
        ::mLUT.insert(NAME_LUT::value_type("close",         2));
        return true;
    }
    NAME_LUT& get_global_mLUT()
    {
        static NAME_LUT g_mLUT;
        static bool bInit = OneTimeInit(g_mLUT); // Just to make initialization happen
        return g_mLUT;
    }
}