作为一款开源跨平台的数据可视化代码库,VTK以其清晰的流水线工作方式、丰富的后处理算法、异种渲染/交互方式,而被众多CAx软件选作后处理实施方案。而异种渲染/交互方式的实现,主要是倚重于VTK的类型系统,因此,有必要对这个问题予以研究分析。
另外,结合前面对FreeCAD类型系统的分析,本文旨在导出类型系统在设计与实现等方面的(非)技术约束。
本文涉及的主要知识点包括,
- Factory Method Pattern
- Type Registration
注1:限于研究水平,分析难免不当,欢迎批评指正。
注2:文章内容会不定期更新。
一、相关组件
1.1 vtkObjectBase
vtkObjectBase定义了类型信息、引用计数等相关接口,而类型信息主要是基于其内部维护的类型名称字符串。
1.2 vtkObject
vtkObject派生于vtkObjectBase,增加了对象动态创建接口。
// Macro to use when you are a direct child class of vtkObjectBase, instead
// of vtkTypeMacro. This is required to properly specify NewInstanceInternal
// as a virtual method.
// It is used to determine whether a class is the same class or a subclass
// of the named class.
#define vtkBaseTypeMacro(thisClass, superclass) \
vtkAbstractTypeMacro(thisClass, superclass); \
\
protected: \
virtual vtkObjectBase* NewInstanceInternal() const { return thisClass::New(); } \
\
public:
// Same as vtkTypeMacro, but adapted for cases where thisClass is abstract.
#define vtkAbstractTypeMacro(thisClass, superclass) \
vtkAbstractTypeMacroWithNewInstanceType(thisClass, superclass, thisClass, #thisClass); \
\
public:
// Allows definition of vtkObject API such that NewInstance may return a
// superclass of thisClass.
#define vtkAbstractTypeMacroWithNewInstanceType( \
thisClass, superclass, instanceType, thisClassName) \
protected: \
const char* GetClassNameInternal() const override { return thisClassName; } \
\
public: \
typedef superclass Superclass; \
static vtkTypeBool IsTypeOf(const char* type) \
{ \
if (!strcmp(thisClassName, type)) \
{ \
return 1; \
} \
return superclass::IsTypeOf(type); \
} \
vtkTypeBool IsA(const char* type) override { return this->thisClass::IsTypeOf(type); } \
static thisClass* SafeDownCast(vtkObjectBase* o) \
{ \
if (o && o->IsA(thisClassName)) \
{ \
return static_cast<thisClass*>(o); \
} \
return nullptr; \
} \
VTK_NEWINSTANCE instanceType* NewInstance() const \
{ \
return instanceType::SafeDownCast(this->NewInstanceInternal()); \
} \
static vtkIdType GetNumberOfGenerationsFromBaseType(const char* type) \
{ \
if (!strcmp(thisClassName, type)) \
{ \
return 0; \
} \
return 1 + superclass::GetNumberOfGenerationsFromBaseType(type); \
} \
vtkIdType GetNumberOfGenerationsFromBase(const char* type) override \
{ \
return this->thisClass::GetNumberOfGenerationsFromBaseType(type); \
}
1.2 vtkObjectFactory
vtkObjectFactory定义了对象实例化的接口,同时将所有的对象工厂存储到静态成员变量RegisteredFactories。
二、关键流程
2.1 类型注册
在VTK中,CMake/vtkObjectFactory.cmake 定义了 vtk_object_factory_configure用于自动生成对应的vtkObjectFactory,
// CMake/vtkObjectFactory.cxx.in
@_vtk_object_factory_configure_EXPORT_MACRO@ void @_vtk_object_factory_library_name@_AutoInit_Construct()
{
if(++@_vtk_object_factory_library_name@Count == 1)
{
@_vtk_object_factory_configure_INITIAL_CODE@
@_vtk_object_factory_library_name@ObjectFactory* factory = @_vtk_object_factory_library_name@ObjectFactory::New();
if (factory)
{
// vtkObjectFactory keeps a reference to the "factory",
vtkObjectFactory::RegisterFactory(factory);
factory->Delete();
}
}
}
然后,借助于VTK_MODULE_INIT宏完成vtkObjectFactory的注册。
// Common/Core/vtkAutoInit.h
// Description:
// Initialize the named module, ensuring its object factory is correctly
// registered. This call must be made in global scope in the
// translation unit of your executable (which can include a shared library, but
// will not work as expected in a static library).
//
// @code{.cpp}
// #include "vtkAutoInit.h"
// VTK_MODULE_INIT(vtkRenderingOpenGL2);
// @endcode
//
// The above snippet if included in the global scope will ensure the object
// factories for vtkRenderingOpenGL2 are correctly registered and unregistered.
#define VTK_MODULE_INIT(M) \
VTK_AUTOINIT_DECLARE(M) namespace \
{ \
static struct M##_ModuleInit \
{ \
/* Call <mod>_AutoInit_Construct during initialization. */ \
M##_ModuleInit() { VTK_AUTOINIT_CONSTRUCT(M) } \
} M##_ModuleInit_Instance; \
}
2.2 对象创建
vtkObject* vtkObjectFactory::CreateInstance(const char* vtkclassname, bool)
{
if (!vtkObjectFactory::RegisteredFactories)
{
vtkObjectFactory::Init();
}
vtkObjectFactory* factory;
vtkCollectionSimpleIterator osit;
for (vtkObjectFactory::RegisteredFactories->InitTraversal(osit);
(factory = vtkObjectFactory::RegisteredFactories->GetNextObjectFactory(osit));)
{
vtkObject* newobject = factory->CreateObject(vtkclassname);
if (newobject)
{
return newobject;
}
}
return nullptr;
}
三、总结
由上述对VTK类型系统的分析,同时结合<FreeCAD源码分析:Type System>一文,可以总结出类型系统实现的要点,
- 定义对象基类
- 定义构造器及其接口
- 将构造器(手动/自动)注册到类型注册表
- 根据类型名字获取构造器,然后实例化对象
VTK | FreeCAD | |
基类 | vtkObjectBase | Base::BaseClass |
构造器 | vtkObjectFactory | Base::Type |
类型注册表 | vtkObjectFactory::RegisteredFactories | Base::Type::typedata |
参考文献
- Erich Gamma. Design Patterns:elements of reusable object-oriented software. Addison Wesley, 1994.