C++ 中的自动类注册是一项常见任务,也是 StackOverflow 上的一个常见问题:
在对象工厂中注册对象创建者 https://stackoverflow.com/questions/1310214/register-an-object-creator-in-object-factory
以某种方式在列表中注册我的课程 https://stackoverflow.com/questions/1691473/somehow-register-my-classes-in-a-list
使用宏自动注册对象创建器函数 https://stackoverflow.com/questions/6137706/automatic-registration-of-object-creator-function-with-a-macro
C++派生类型的自动工厂注册 https://stackoverflow.com/questions/9975672/c-automatic-factory-registration-of-derived-types
基本目标是使用某些注册表或工厂自动注册类,以便稍后可以对每个类进行一些工作。
这是一项成熟的技术,被 Google Test 等库使用(http://code.google.com/p/googletest http://code.google.com/p/googletest),它会自动注册Test类的子类,以便每个测试都可以在测试执行过程中自动实例化并运行。
注册可以通过实例化一个静态注册器类(其构造函数执行注册)来完成,或者通过巧妙地使用 CRTP 并将注册代码放入基类构造函数中,或者您喜欢的任何方式(上面的链接提供了几种不同的可能技术)。
然而,当我实现这些技术中的任何一种时,我发现它们的扩展性非常差。如果我在 Google Test 中有 10,000 个 TEST 宏调用,编译和链接研磨就会停止(MSVC 2010)并且二进制大小会爆炸。如果我以另一种方式实现这一点,使用 10,000 个带有静态注册器的子类,我会看到相同的行为。
例如,考虑一个简化的例子:
#include <iostream>
#include <string>
class Base {
public:
Base( const std::string& Name_ ) : Name( Name_ ) { ; }
~Base() { ; }
virtual std::string GetName() const { return Name; }
virtual void DoSomething() = 0;
private:
std::string Name;
};
class Registry {
public:
static Registry& GetInstance() {
static Registry* Instance = new Registry();
return *Instance;
}
void Register( const Base* b ) {
std::cout << "Registered class " << b->GetName() << std::endl;
}
private:
Registry() { ; }
~Registry() { ; }
};
class Registrar {
public:
Registrar( const Base* b ) {
Registry::GetInstance().Register( b );
}
~Registrar() { }
};
#define REGISTER( CLASS ) \
class CLASS : public Base { \
public: \
CLASS( const std::string& Name ) : Base( Name ) { ; } \
virtual void DoSomething(); \
private: \
static Registrar m_Registrar; \
}; \
Registrar CLASS::m_Registrar( new CLASS( #CLASS ) ); \
void CLASS::DoSomething()
int main( int argc, char** argv )
{
return 0;
}
REGISTER( Class1 )
{
std::cout << "Doing something in Class1" << std::endl;
}
REGISTER( Class2 )
{
std::cout << "Doing something in Class2" << std::endl;
}
[...]
总共 10000 个自动生成的 REGISTER 调用。
是否存在无法很好扩展的根本原因?编译器会因为 10000 个类而窒息吗?在 MSVC 2010 下,在相当快的机器上编译上述内容大约需要两分钟,并生成大小超过 5 MB 的二进制文件。如果我对 Google Test 进行类似的操作,我会看到相同的结果。