C++ Builder 参考手册 ➙ C++ Builder 的反射 (三) - 通用 Reflection Factory
因为前面一篇文章 C++ Builder 获取任意一个类或对象的类名 已经找到了获取任意一个类或对象的类名的方法,只需要把 C++ Builder 的反射 (二) - Reflection Factory 的模板里面的 TClassType::ClassName()
改成 THsuanluClassName::GetClassName<TClassType>()
就可以了,这样无论是否从 TObject 继承的类都可以反射了。这个类模板需要 C++ 11 / clang 编译器。本文的程序和例子已经用 C++ Builder 10.2.3 版本 clang 32位 和 clang 64位编译器测试通过。
1. 通用 Reflection Factory 类模板
THsuanluReflectionFactory 类模板的参数:
- TBaseClass 为可以反射的类的公共父类,通过类名字符串反射创建的类通过这个类型的指针返回;
- TConstructorParams 为反射的类的构造函数的参数的类型,这不是单一的参数,而是一组随意个数和类型的参数,也可以没有。由于创建类的时候,必需调用类的构造函数,所以需要提供类的构造函数的每个参数的类型,请参考 C++ 可变参数的模板,构造函数参数相同的类,可以注册到这个 Reflection Factory 里面;
Register 方法为注册需要反射的类,模板参数 TClassType 为要注册的类;
Create 方法为通过类名字符串创建类,参数 sClassName 为类名字符串,参数 Params 为构造函数的可变参数,通过 new TClassType(Params...) 创建注册的类。
模板里面的 THsuanluClassName::GetClassName 是 C++ Builder 获取任意一个类或对象的类名 介绍的方法,即把 THsuanluClassName 类加入头文件,这个类的实现加入 cpp 文件。
#include <map>
template <class TBaseClass, class... TConstructorParams> // 公共父类, 构造函数参数类型
class THsuanluReflectionFactory
{
private:
std::map<UnicodeString, TBaseClass*(*)(TConstructorParams...)>_ClassMap;
public:
template<class TClassType> // 要注册的类
void Register(void) // 注册 TClassType 类
{
_ClassMap[THsuanluClassName::GetClassName<TClassType>()] =
[](TConstructorParams... Params) -> TBaseClass*
{
return new TClassType(Params...);
};
}
TBaseClass *Create(UnicodeString sClassName, TConstructorParams... Params)
{
auto iter = _ClassMap.find(sClassName);
if(iter == _ClassMap.end())
throw Exception(L"类 \"" + sClassName + L"\" 未注册");
return iter->second(Params...);
}
};
2. 测试控件的反射 - 控件都是 TObject 继承的类
- 这个例子在程序开始运行时,即在主窗口的构造函数里面,注册了 TLabel、TButton、TMemo、TEdit、TCheckBox、TRadioButton、TComboBox 等几个控件类,可以使用他们的类名字符串来创建控件对象。
- 由于控件类的构造函数都有一个 TComponent* Owner 参数,在定义 ControlsFactory 的时候,模板参数除了公共父类 TControl 之外,还有一个 TComponent* 参数。
THsuanluReflectionFactory<TControl, TComponent*> ControlsFactory;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
ControlsFactory.Register<TLabel>();
ControlsFactory.Register<TButton>();
ControlsFactory.Register<TMemo>();
ControlsFactory.Register<TEdit>();
ControlsFactory.Register<TCheckBox>();
ControlsFactory.Register<TRadioButton>();
ControlsFactory.Register<TComboBox>();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonCreateClick(TObject *Sender)
{
try
{
TControl *p = ControlsFactory.Create(EditClassName->Text, this);
p->Parent = this;
p->SetBounds(EditLeft->Text.ToIntDef(0), EditTop->Text.ToIntDef(0), EditWidth->Text.ToIntDef(80), EditHeight->Text.ToIntDef(30));
}
catch(Exception &e)
{
ShowMessage(e.Message);
}
}
编辑框 EditClassName 输入控件的类名,
编辑框 EditLeft, EditTop, EditWidth, EditHeight 分别用于输入控件的位置和大小,
按钮 ButtonCreate 创建控件,ButtonCreateClick 方法就是点击这个按钮执行的代码。
运行结果:
创建一个 TComboBox:
再创建一个 TCheckBox 和一个 TRadioButton
3. 测试自己写的类的反射 - 不是 TObject 继承的类
THsuanluBase 为其他几个类的公共父类,反射的结果是通过这个类型的指针返回通过类名字符串创建的对象。要注意:通过父类指针访问子类的方法要定义为虚函数。
class THsuanluBase
{
public:
THsuanluBase(){}
virtual ~THsuanluBase(){}
virtual void PrintMessage(void){ Form1->Memo1->Lines->Add(L"这是父类的 PrintMessage 方法"); }
};
//---------------------------------------------------------------------------
class THsuanluTest1 : public THsuanluBase
{
public:
virtual void PrintMessage(void){ Form1->Memo1->Lines->Add(L"这是 THsuanluTest1 的 PrintMessage 方法"); }
};
//---------------------------------------------------------------------------
class 玄坴测试类 : public THsuanluBase
{
public:
virtual void PrintMessage(void){ Form1->Memo1->Lines->Add(L"这是 玄坴测试类 的 PrintMessage 方法"); }
};
//---------------------------------------------------------------------------
class THsuanluTest2 : public 玄坴测试类
{
public:
virtual void PrintMessage(void){ Form1->Memo1->Lines->Add(L"这是 THsuanluTest2 的 PrintMessage 方法"); }
};
- 定义 HsuanluFactory 时,由于自己写的这些类的构造函数没有参数,所以模板参数只有一个公共父类 THsuanluBase
- 在程序开始执行的时候,即主窗口的构造函数里面注册所有需要反射的类,然后就可以使用类名字符串来创建这些类的对象了。
THsuanluReflectionFactory<THsuanluBase> HsuanluFactory;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
HsuanluFactory.Register<THsuanluTest1>();
HsuanluFactory.Register<玄坴测试类>();
HsuanluFactory.Register<THsuanluTest2>();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
THsuanluBase *p1 = nullptr, *p2 = nullptr, *p3 = nullptr;
try
{
p1 = HsuanluFactory.Create(L"THsuanluTest1");
p2 = HsuanluFactory.Create(L"玄坴测试类");
p3 = HsuanluFactory.Create(L"THsuanluTest2");
p1->PrintMessage();
p2->PrintMessage();
p3->PrintMessage();
}
catch(Exception &e)
{
ShowMessage(e.Message);
}
delete p1;
delete p2;
delete p3;
}
运行结果:
相关:
- C++ Builder 的反射 (二) - Reflection Factory
- C++ Builder 的反射 (Reflection) (一)
- C++ Builder 获取任意一个类或对象的类名
- 枚举控件所有的属性、事件和方法
- 枚举窗口内所有的控件
- C++ Builder 的枚举类型
- C / C++ 可变参数的函数
- C / C++ 可变参数的宏,__VA_ARGS__,...
- C++ 可变参数的模板
- C++ Builder 的 PME 架构
C++ Builder 参考手册 ➙ C++ Builder 的反射 (三) - 通用 Reflection Factory