【问题标题】:static initialization静态初始化
【发布时间】:2010-06-23 19:37:13
【问题描述】:

上下文

我正在做一个有一些“模块”的项目。 我在这里所说的模块是一个简单的类,它实现了一个特定的功能,并从一个抽象类 GenericModule 派生出来,它强制一个接口。

未来应该会添加新模块。

一个模块的多个实例可以同时加载,也可以不加载,具体取决于配置文件。

我认为,如果未来的开发人员可以通过简单的一行将他的模块“注册”到系统中,那就太好了。他们在谷歌测试中注册测试的方式或多或少相同。

上下文²(技术)

我正在使用 Visual Studio 2005 构建项目。 代码完全在一个库中,除了 main() 是在一个 exec 项目中。 我想保持这种状态。

我的解决方案

我从他们对谷歌测试所做的事情中找到了灵感。

我创建了一个模板化工厂。看起来或多或少像这样(我跳过了无趣的部分以保持这个问题的可读性):

class CModuleFactory : boost::noncopyable
{
public:
    virtual ~CModuleFactory() {};

    virtual CModuleGenerique* operator()(
        const boost::property_tree::ptree& rParametres ) const = 0;
};

template <class T>
class CModuleFactoryImpl : public CModuleFactory
{
public:
    CModuleGenerique* operator()(
        const boost::property_tree::ptree& rParametres ) const
    {
        return new T( rParametres );
    }
};

还有一个方法应该注册模块并将它的工厂添加到列表中。

class CGenericModule
{
    // ...
    template <class T>
    static int declareModule( const std::string& rstrModuleName )
    {
        // creation de la factory
        CModuleFactoryImpl<T>* pFactory = new CModuleFactoryImpl<T>();

        // adds the factory to a map of "id" => factory
        CAcquisition::s_mapModuleFactory()[rstrModuleName ] = pFactory;

        return 0;
    }
};

现在在一个模块中,我需要做的就是声明一个模块:

static int initModule =
acquisition::CGenericModule::declareModule<acquisition::modules::CMyMod>(
    "mod_name"
    );

( 将来它会被封装在一个允许做的宏中

DECLARE_MODULE( "mod_name", acquisition::modules::CMyMod );

)

问题

好了,问题来了。

问题是,它确实有效,但不完全是我想要的方式。

如果我将 initModule 的定义放在模块的 .cpp 中(我想要它的位置)(甚至在 .h 中),则不会调用方法 declareModule。 如果我将静态初始化放在使用过的 .cpp 文件中......它可以工作。

我的意思是:在其他地方调用代码。

问题是视觉工作室在构建库时似乎丢弃了整个 obj。我猜那是因为它没有在任何地方使用。

我激活了详细链接,并在第 n°2 通道中列出了库中的 .objs 并且模块的 .obj 不存在。

几乎解决了?

我找到了this 并尝试添加/OPT:NOREF 选项,但没有成功。 我没有尝试将函数放在模块的 .h 中并从其他地方调用它,因为重点在于能够在其文件的一行中声明它。

我也认为这个问题类似于this one,但解决方案是g++不是视觉的:'(

编辑:我刚刚阅读了这个问题的答案中的注释。好吧,如果我#include来自另一个.cpp的模块的.h,并将init放入模块的.h中。它有效并且初始化实际上进行了两次......在每个编译单元中一次?好吧,它似乎发生在模块的编译单元中......

旁注

如果您不同意我的尝试,请尽管说出来,但我仍然对解决方案感兴趣

【问题讨论】:

  • 如果模块不被使用,你为什么要关心它的注册?
  • '代码完全在库中'是静态的还是动态的?
  • @Georg 不是直接使用,而是通过 CModuleFactory 的接口使用。 @Greg 静态
  • @Georg Fritzsche;您可能不直接使用该模块,除了通过工厂注册您现在可以创建对象,然后通过抽象 API 使用它。
  • “我在这里所说的模块是一个简单的类”。那么,请称其为“班级”。如果是一堂课,请这样称呼它。请不要用似乎与实际问题无关的术语来混淆已经令人困惑的问题。

标签: c++


【解决方案1】:

如果您希望在您的“模块”中实现这种自注册行为,您假设链接器正在优化 initModule 因为它没有被直接引用可能是不正确的(尽管它也可能是正确的 :-)。

当您注册这些模块时,您是否修改了在文件范围内定义的另一个静态变量?如果是这样,您至少有一个初始化顺序问题。这甚至可能仅在发布版本中表现出来(初始化顺序可能因编译器设置而异),这可能会让您相信链接器正在优化这个 initModule 变量,即使它可能没有这样做。

如果你想以这种方式做事,模块注册类型的变量(无论是注册人列表还是其他)应该是惰性构造的。示例:

static vector<string> unsafe_static; // bad

vector<string>& safe_static()
{
    static vector<string> f;
    return f;
} // ok

请注意,上述内容存在并发问题。多个线程调用 safe_static 需要一些线程同步。

我怀疑您的真正问题与初始化顺序有关,即使链接器似乎排除了 initModule 定义。通常,链接器不会省略具有副作用的引用。

如果您发现这不是初始化顺序问题并且链接器省略了代码,那么强制它的一种方法是导出 initModule(例如:MSVC 上的 dllexport)。您应该仔细考虑这种自我注册行为是否真的超过了添加到函数调用列表以初始化“模块”的简单过程。如果每个“模块”都在单独的共享库/DLL 中定义,您也可以更自然地实现这一点,在这种情况下,您的宏可能只是定义要导出的函数,该函数可以由主机应用程序自动添加。当然,这需要为您创建的每个“模块”定义一个单独的项目,而不是仅仅将一个自注册的 cpp 文件添加到现有项目中。

【讨论】:

  • 在我的代码中,唯一的其他静态变量完全按照您通过 s_mapModuleFactory() 的建议进行访问。而且我知道函数 declareModule 是否被调用,因为它向控制台输出了一些文本
  • 在这种情况下,这是一种非常严厉的方法来尝试强制编译器包含 MSVC 的模块: namespace { __declspec(dllexport) static int initModule = acquire::CGenericModule::declareModule("mod_name");其他编译器/平台的想法相同:导出符号。 “而且我知道函数 declareModule 是否被调用,因为它输出一些文本 [...]” 如果您使用 cout,这可能会调用未定义的行为。这就像在文件范围内写这个: static bool success = cout
  • 我使用的不是 cout,而是 ACE 日志记录机制,我相信它会在首次使用时被初始化
【解决方案2】:

我根据 wxWidgets 的代码得到了类似的东西,但是我只将它用作 DLL。但是 wxWidgets 代码适用于静态库。

可能有所不同的是,在 wx 中,以下的等价物是在类范围内定义的。

static int initModule =
acquisition::CGenericModule::declareModule<acquisition::modules::CMyMod>(
    "mod_name"
    );

类似于以下内容,其中 Factory 的创建因为它是静态的,导致它被加载到工厂列表中。

#define DECLARE_CLASS(name)\
class name: public Interface { \
    private: \
        static Factory m_reg;\
        static std::auto_ptr<Interface > clone();

#define IMPLEMENT_IAUTH(name,method)\
    Factory name::m_reg(method,name::clone);\

【讨论】:

  • 我想我已经尝试将 initModule 放在类范围内,但它没有用。那是前段时间,代码已经“进化”了,一旦我可以访问我的计算机,我可能会再试一次。谢谢。
猜你喜欢
  • 2020-04-14
  • 2011-01-26
  • 2020-02-05
  • 1970-01-01
  • 2023-03-26
  • 1970-01-01
  • 1970-01-01
  • 2014-10-06
  • 2012-11-07
相关资源
最近更新 更多