C++ Builder 的反射 (三) - 通用 Reflection Factory

C++ Builder 参考手册 ➙ C++ Builder 的反射 (三) - 通用 Reflection Factory


因为前面一篇文章 C++ Builder 获取任意一个类或对象的类名 已经找到了获取任意一个类或对象的类名的方法,只需要把 C++ Builder 的反射 (二) - Reflection Factory 的模板里面的 TClassType::ClassName() 改成 THsuanluClassName::GetClassName() 就可以了,这样无论是否从 TObject 继承的类都可以反射了。这个类模板需要 C++ 11 / clang 编译器。本文的程序和例子已经用 C++ Builder 10.2.3 版本 clang 32位 和 clang 64位编译器测试通过。

  1. 通用 Reflection Factory 类模板
  2. 测试控件的反射 - 控件都是 TObject 继承的类
  3. 测试自己写的类的反射 - 不是 TObject 继承的类

1. 通用 Reflection Factory 类模板

THsuanluReflectionFactory 类模板的参数:

  • TBaseClass 为可以反射的类的公共父类,通过类名字符串反射创建的类通过这个类型的指针返回;
  • TConstructorParams 为反射的类的构造函数的参数的类型,这不是单一的参数,而是一组随意个数和类型的参数,也可以没有。由于创建类的时候,必需调用类的构造函数,所以需要提供类的构造函数的每个参数的类型,请参考 C++ 可变参数的模板,构造函数参数相同的类,可以注册到这个 Reflection Factory 里面;

Register 方法为注册需要反射的类,模板参数 TClassType 为要注册的类;
Create 方法为通过类名字符串创建类,参数 sClassName 为类名字符串,参数 Params 为构造函数的可变参数,通过 new TClassType(Params...) 创建注册的类。

模板里面的 THsuanluClassName::GetClassName 是 C++ Builder 获取任意一个类或对象的类名 介绍的方法,即把 THsuanluClassName 类加入头文件,这个类的实现加入 cpp 文件。

#include 
template  // 公共父类, 构造函数参数类型
class THsuanluReflectionFactory
{
private:
    std::map_ClassMap;

public:
    template // 要注册的类
    void Register(void) // 注册 TClassType 类
    {
        _ClassMap[THsuanluClassName::GetClassName()] =
        [](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 ControlsFactory;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    ControlsFactory.Register();
    ControlsFactory.Register();
    ControlsFactory.Register();
    ControlsFactory.Register();
    ControlsFactory.Register();
    ControlsFactory.Register();
    ControlsFactory.Register();
}
//---------------------------------------------------------------------------
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 方法就是点击这个按钮执行的代码。

测试使用 Reflection Factory 控件反射 - 窗口设计

运行结果:

创建一个 TComboBox:

创建一个 TComboBox

再创建一个 TCheckBox 和一个 TRadioButton

再创建一个 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 HsuanluFactory;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    HsuanluFactory.Register();
    HsuanluFactory.Register<玄坴测试类>();
    HsuanluFactory.Register();
}
//---------------------------------------------------------------------------
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

你可能感兴趣的:(C++ Builder 的反射 (三) - 通用 Reflection Factory)