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
等。