静态初始化

时间:2010-06-23 19:37:13

标签: c++

上下文

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

将来应该添加新模块。

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

我认为,如果未来的开发人员能够在一个简单的行中“注册”他的模块系统,那将会很棒。或多或少与他们在Google测试中注册测试的方式相同。

上下文²(技术)

我正在使用visual studio 2005构建项目。 代码完全在库中,除了exec项目中的main()。 我想保持这种方式。

我的解决方案

我从谷歌测试中找到了灵感。

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

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方法。 如果我将静态init放在一个使用过的.cpp文件中..它可以工作。

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

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

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

几乎解决了?

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

另外我认为这个问题类似于this one,但解决方案是针对g ++而不是视觉:'(

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

旁注

如果你不同意我正在尝试做的事情,请随意告诉,但我仍然对解决方案感兴趣

2 个答案:

答案 0 :(得分:2)

如果你想在你的“模块”中使用这种自我注册行为,你假设链接器正在优化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文件。

答案 1 :(得分:1)

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

可能产生影响的一点是,在wx中,以下等式是在类范围内定义的。

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

如下所示,创建Factory因为它是静态的,会导致它被加载到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);\