虽然C++11引入了RTTI、Metaprogramming 等技术,但C++在Reflection方面依旧功能有限。作为反射机制的组成部分,类型系统(Type System)提供运行时类型信息(RTTI)、依据类名创建对象等功能。
在FreeCAD中,类型系统是插件式开发框架、分层架构等核心实现的基础,因此,有必要对这个基础问题予以研究分析。
本文涉及的主要知识点包括,
- Meta Object
- RTTI
- Factory Method Pattern
- Model-View-Presenter
- Layered Architecture
- Modular Development Framework
注1:限于研究水平,分析难免不当,欢迎批评指正。
注2:文章内容会不定期更新。
一、主要组件
1.1 Base::Type
Base::Type实际上是一般类型系统中的类型注册表,通过其静态成员变量存储了类型名称、对象构造器、类型继承关系等类型信息。
可以看到,FreeCAD实际上是通过TypeData来描述类型名称、父类名称、对象构造器等类型信息,并将所有的类型信息注册到Base::Type::typedata静态变量,而每个Type对象仅维护一个类型索引index。
1.2 Base::BaseClass
Base::BaseClass是一般类型系统中的接口类,定义了对象构造器,同时维护了类型信息索引,也就是提供了对元对象的访问。
二、关键流程
2.1 类型注册
相较于Qt、RTTR等反射机制,FreeCAD并没有提供类型自动注册的实现,因此,需要结合需要手动编写代码以完成类型注册。
在FreeCAD中,大体上有两种类型注册方式:
一是在FreeCADApp、FreeCADGui等软件主体启动时,便会调用App::Application::initTypes()完成一些类型的注册。
void Application::initTypes()
{
// Base types
Base::Type ::init();
Base::BaseClass ::init();
Base::Exception ::init();
Base::AbortException ::init();
Base::Persistence ::init();
// Complex data classes
Data::ComplexGeoData ::init();
Data::Segment ::init();
// ... ...
}
另外一种方式就是在加载各种Module时,比如对于Part模块,
PyMOD_INIT_FUNC(Part)
{
Base::Console().Log("Module: Part\n");
// ... ...
Part::TopoShape ::init();
Part::PropertyPartShape ::init();
Part::PropertyGeometryList ::init();
Part::PropertyShapeHistory ::init();
Part::PropertyFilletEdges ::init();
Part::PropertyTopoShapeList ::init();
// ... ...
}
2.2 对象创建
当完成类型注册之后,便可以依据类型名称调用对应构造器来创建对象。
在FreeCAD中,类型系统的一个主要应用就是分层系统的实现。
- App::DocumentObject/Gui::ViewProviderDocumentObject
void Document::slotNewObject(const App::DocumentObject& Obj)
{
// ... ...
//Base::Console().Log("Document::slotNewObject() called\n");
std::string cName = Obj.getViewProviderNameStored();
for(;;) {
if (cName.empty()) {
// handle document object with no view provider specified
FC_LOG(Obj.getFullName() << " has no view provider specified");
return;
}
Base::Type type = Base::Type::getTypeIfDerivedFrom(cName.c_str(), ViewProviderDocumentObject::getClassTypeId(), true);
pcProvider = static_cast<ViewProviderDocumentObject*>(type.createInstance());
// createInstance could return a null pointer
if (!pcProvider) {
// type not derived from ViewProviderDocumentObject!!!
FC_ERR("Invalid view provider type '" << cName << "' for " << Obj.getFullName());
return;
}
else if (cName!=Obj.getViewProviderName() && !pcProvider->allowOverride(Obj)) {
FC_WARN("View provider type '" << cName << "' does not support " << Obj.getFullName());
delete pcProvider;
pcProvider = nullptr;
cName = Obj.getViewProviderName();
}
else {
break;
}
}
// ... ...
}
三、演练: ACISE中的反射机制
参考文献
- Erich Gamma. Design Patterns:elements of reusable object-oriented software. Addison Wesley, 1994.
- Joseph Ingeno. Software Architect's Handbook. Packt Publishing, 2018.
网络资料
FreeCADhttps://www.freecad.org/
从RTTR谈Reflection机制https://blog.csdn.net/qq_26221775/article/details/138843452?spm=1001.2014.3001.5501大型CAx(CAD/CAE/CAM)软件研发中的职责编排
https://blog.csdn.net/qq_26221775/article/details/136975550?spm=1001.2014.3001.5501
SALOME源码分析:MDF框架https://blog.csdn.net/qq_26221775/article/details/139268234?spm=1001.2014.3001.5501