AGM::LibReflection 的更新: C++ 的反射库 






4.32/5 (8投票s)
2005年3月28日
4分钟阅读

116734

832
本文是对原始 AGM::LibReflection 库的更新。
引言
axilmar 编写的原始 LibReflection 存在不少限制,在本版本中已得到改进或放宽。本文仅描述新旧版本之间的区别。所有现有功能和语法仍然得到支持,并且在新版本中也能正常工作。因此,升级到新版本应与重新编译代码一样简单。读者应在继续阅读之前,先阅读 原始 LibReflection 文章。
新版本更改/改进了以下项目
- 构造函数信息现在可以捕获并存储在 Class对象中。此外,可以通过new运算符调用构造函数来创建对象。当默认构造函数通过反射进入Class时,Class::newInstance()方法可以作为创建实例的快捷方式。示例using namespace agm::reflection; using namespace std; class Widget { CLASS(Widget, NullClass); CONSTRUCTOR(public, Widget, ()) { cout << "default constructor called" << endl; } CONSTRUCTOR(public, Widget, (int y) ) { cout << "Widget(int) called with " << y << endl; } }; int main() { Widget w; const Class & cls = w.getClass(); Widget* wp = static_cast<Widget*>( cls.newInstance() ); delete wp; } 
- 在原始版本中,方法参数类型和返回类型未被捕获。这已得到更改。用户现在可以检索方法参数和返回类型的类型信息(以 std::type_info的形式)。使用上面Widget的类定义const Class::ConstructorList & constructors = cls.getConstructors(); for (Class::ConstructorList::const_iterator iter = constructors.begin(); iter != constructors.end(); ++iter) { const ConstructorMethod& cm = *iter; // we will look for Widget(int) constructor if (cm.getArgsCount() == 1 && *cm.getArgsTypes()[0] == typeid(int)) { cout << "Found Widget(int) constructor" << endl; Widget *wp2; cm.invoke(wp2, 12); delete wp2; } } 
- 类方法、静态方法和构造函数可以被重载。用户可以搜索并遍历所有重载的方法来选择他们想要的方法。这通常通过检查参数类型并返回方法候选项的类型来完成。构造函数重载的示例已在上面显示。静态方法和类方法的定义类似。不再需要使用不同的宏。
- 添加了简单的类注册表。这允许用户列出或搜索所有“已知”类的集合。使用上面的 Widget示例const Class * wcls = Class::forName("Widget"); if (wcls != 0) { cout << "Calling newInstance()" << endl; Widget* wp = static_cast<Widget*>( wcls->newInstance() ); } 
- 改进了对 TypeMismatchError的描述。当一个方法(无论是构造函数、静态方法还是类成员方法)被使用不兼容类型调用时,抛出的异常将包含有关哪个参数不兼容的详细信息。以下是抛出的TypeMismatchError异常的示例terminate called after throwing an instance of 'agm::reflection::TypeMismatchError' what(): static Label* Label::self(Label*): (WARN only: type castable)return type mismatched: expected (Label*) passed (Widget*); Parameter 1 mismatched: expected (Label*) passed (Widget*);
- 完整的类名(包括命名空间)现在可以被捕获,并通过 Class::getFullName()查询。类似地,方法(构造函数、静态、成员)现在包含一个新方法getSignature(),它返回人类可读的类和参数类型。
- 自动转换兼容指针和引用类型的对象类。例如,如果一个方法(构造函数、静态方法、成员方法)接受 Base *,但传递了Derived *作为参数,则Derived *将被自动转换为Base *。原始版本在参数类型不完全匹配时会直接抛出TypeMismatchError异常。这同样适用于返回值。要求是Base和Derived都必须使用反射进行定义。class Label : public Widget { CLASS(Label, Widget); CONSTRUCTOR(public, Label, ()) { cout << "Label:: default constructor called" << endl; } STATIC_METHOD(public, Label *, self, (Label *that) ) { cout << "The object type is " << that->getClass().getFullName() << endl; return that; } }; int main() { Label l; Widget* wp = &l; Widget* wp_result; l.getClass().getStaticMethod("self").invoke(wp_result, &l); /* wp_result pointer to a Label * object */ } 在此示例中, self()返回一个Label *,但传递的返回对象是Widget *。返回的指针已自动转换。
- 动态类型转换为其对象类(向下转型),适用于兼容的指针类型。当一个方法期望 Derived *但传递的参数是Base *时,反射库将尝试通过 C++ 的dynamic_cast<>运算符将Base *转换为Derived *。如果转换不成功,将抛出TypeMismatchError异常;否则将进行调用。使用前面的示例l.getClass().getStaticMethod("self").invoke(wp_result, wp); /* wp is of type Widget *, but the argument requires a Label * */ 
- LibReflection 库现在也适用于模板类。然而,模板化方法函数不能被反射。这是从 template.cpp 文件中的示例代码中提取的template <class T> class Widget { CLASS(Widget, NullClass); CONSTRUCTOR(public, Widget, ()) : storage() { cout << "default constructor called" << endl; } CONSTRUCTOR(public, Widget, (const Widget& x) ) : storage(x.storage) { cout << "copy constructor called" << endl; } CONSTRUCTOR(public, Widget, (const T& y) ) : storage(y) { cout << "Widget(T) called with " << y << endl; } METHOD(public, void, store, (T x)) { cout << "calling store with " << x << endl; this->storage = x; } virtual ~Widget() {} private: T storage; }; template <class X, class Y> class Label : public Widget<X> { CLASS(Label, Widget<X>); CONSTRUCTOR(public, Label, ()) : Widget<X>(), y_store() { cout << "default constructor called" << endl; } CONSTRUCTOR(public, Label, (const Label& x) ) : Widget<X>(x), y_store(x.y_store) { cout << "copy constructor called" << endl; } CONSTRUCTOR(public, Label, (const X & x, const Y & y) ) : Widget<X>(x), y_store(y) { cout << "Label(x,y) called with " << y << endl; } METHOD(public, void, store2nd, (Y y)) { cout << "calling store2nd with " << y << endl; y_store = y; } private: Y y_store; }; 
- 添加了对无 MACRO的动态反射的支持。任何公共类都可以使用模板类ReflectiveClass添加到 LibReflection 中。在 LibReflection 中定义的类与使用MACRO方法添加的类是相同的。这种反射方法适用于无法修改类定义的任何类(例如供应商库)。以我们喜欢的 Widget / Label 示例为例,这是使用动态反射定义的相同代码class Label : public Widget { public: Label() { cout << "Label:: default constructor called" << endl; } static Label * self (Label *that) { cout << "The object type is " << that->getClass().getFullName() << endl; return that; } }; // dynamic class reflection to LibReflection static const ReflectiveClass<Label> label_clsdef = ReflectiveClass<Label>((Widget*)0) .constructor() .static_method(&Label::self, "self"); 唯一细微的区别是,由于类未被修改, getClass()和getStaticClass()不再是Label类的一部分。可以通过label_clsdef.get()或使用类注册表Class::forType(const std::type_info&)静态成员函数来获取agm::reflection::Class *。
- 添加了自定义用户定义宏。用户可以在包含反射头文件之前定义宏 REFLECTION_TYPE_CUSTOMx,其中x是 1 到 4。如果定义了,该宏应计算为一个const std::type_info&对象。自定义类型宏在用户必须能够查找模板类中的特定类时很有用。例如,使用 boost 库的智能指针,我们可以有以下定义:#define REFLECTION_TYPE_CUSTOM1(C) typeid(boost::shared_ptr<C>)。然后我们可以通过使用Class::findType(const std::type_info& t)来测试相应的类型。当参数的确切类型未知时,这尤其有用。
- 添加了对放置实例化的支持(也称为 placement new)。ConstructorMethod可以通过invoke()或invokePlacement()调用,后者接受一个额外的void *用于对象的放置。
- 移植到 GCC。这可以被视为一个非改进,因为新版本尚未在 MSVC 或 VS.NET 下进行测试。
限制(与原始版本相同)
- 在类“已知”之前必须创建该类的一个实例。
- 仅支持单重继承,不支持虚基类。
- 编译器最初进行的一些隐式转换将不会执行。例如:long->int,const char *->string等。
