C++反射

1、反射是什么

假设我们已知某个类的名称,那么如何通过类名称字符串来动态生成类的对象?

在Java编程中,这并不是个问题。但是C++的语言特性决定了其并不支持通过类名称字符串”ClassXX”来生成对象的,也就是说在C++中我们可以使用ClassXX* object =new ClassXX来生成对象,但是不能通过ClassXX* object=new “ClassXX”来生成对象。这导致我们对节点和模块功能扩展的时候无法做到自适应扩展,也就是如果我们额外引入一个新的功能模块,仿真引擎无法动态感知到它的存在。

反射机制引入的就是为了解决仿真节点和功能模块的自适应扩充问题,从而避免代码之间的深度耦合。

2、C++反射原理

参考代码src/Framework/Object.h。

3、扩展模块如何支持反射

所有需要支持反射的类需要继承自Object。以src/Dynamics/ParticleSystem/SummationDensity.h中实现的类为例,其支持反射的流程包含两个步骤

  • 派生Object或者Object的子类

    template<typename TDataType>
    class SummationDensity : public virtual ParticleApproximation<TDataType>
    
  • 接口申明

    DECLARE_CLASS(SummationDensity)
    //或者
    DECLARE_TCLASS(SummationDensity, TDataType)
    

    其中SummationDensity对应类的名称,TDataType对应模板参数

  • 接口实现

    IMPLEMENT_TCLASS(SummationDensity)
    //或者
    IMPLEMENT_TCLASS(SummationDensity, TDataType)
    

    其中通过DECLARE_CLASS/DECLARE_TCLASS通过定义静态变量完成对SummationDensity的注册。

DECLARE_TCLASS和IMPLEMENT_TCLASS要求定义在同一头文件内,该方式对于实现文件为cpp后缀的类有效。然而当前NVCC的编译器依然存在一定问题,如果实现文件为.cu后缀,则定义的static变量无法完成初始化,需要通过额外的类来完成。

一个可行的解决方案是针对每一个静态链接库引入一个额外的类来完成初始化,在initializeParticleSystem.h中引入

class ParticleSystemInitializer : public Object
{
	public:
		ParticleSystemInitializer();
};

const static ParticleSystemInitializer particleSystemInitializer;

同时再initializeParticleSystem.cpp中定义实现如下构造函数来完成对.cu文件中定义的类进行显式调用。

ParticleSystemInitializer::ParticleSystemInitializer()
{
    TypeInfo::New<LinearDamping<DataType3f>>();
    TypeInfo::New<ParticleIntegrator<DataType3f>>();
    TypeInfo::New<ImplicitViscosity<DataType3f>>();
    TypeInfo::New<DensityPBD<DataType3f>>();
    TypeInfo::New<SummationDensity<DataType3f>>();
    TypeInfo::New<VariationalApproximateProjection<DataType3f>>();
}

最后在待运行的Qt项目的CMakeLists.txt中加入"WHOLEARCHIVE"(链接自动注册类的lib库)。例如,运行Qt_VtkVisualModule项目,并期望在项目中自动注册initializeParticleSystem.cpp中类。在{peridyno_Code_path}/examples/Qt_VtkVisualModule/CMakeLists.txt中添加 :