CPP反射分析和实现
本文不讨论代码预分析头文件生成反射信息的过程,在确定了runtime时传入的反射信息的结构后,很容易利用像libClang或者纯手写的简易语法分析,来处理源代码并生成文件。 本文着重于Runtime阶段反射信息的构建,第一部分简单分析UE反射系统的实现,第二部分简述一个自定义运行时反射系统的实现
当前版本比较潦草,仅仅为了记录,后续会陆续修改
一. UE的反射实现浅析
首先从全局上来思考一个runtime反射系统,我们需要获取反射的信息,这包括需要反射的类,类中需要反射的成员函数和成员变量,这些反射信息是是一组特殊的结构体,包含了对这些反射对象的详细描述。
其次我们需要有一个对象来保存上述的
1.反射生成代码注册信息调用
各个CPP文件的反射信息都是在编译器Preprocessor阶段分析头文件生成的,核心就是在生成文件中直接写入附加的发射信息,略过各个反射类型的结构定义,以及各种其他方法的生成(RPC,序列化),排除那些大量的宏定义 最核心的结构体就是FRegisterCompiledInInfo
例如下面的一个例子
static FRegisterCompiledInInfo XXXXX_NAME(TEXT("XXXXX"),
XXXX_REFLECT::ClassInfo,
UE_ARRAY_COUNT(XXXX_REFLECT::ClassInfo),
nullptr,
0,
XXXX_REFLECT::EnumInfo,
UE_ARRAY_COUNT(XXXX_REFLECT::EnumInfo)
);
static FRegisterCompiledInInfo会在程序启动时,静态创建这个结构体,从而实现了自动注册功能,但是此时还不能直接执行注册,主要是因为全局静态对象构建时机的不确定性, 所以我们先将注册信息保存起来,待后续合适的时机去处理待注册信息
2. 延迟注册
上文已经说了FRegisterCompiledInInfo会在构建时,将反射信息保存起来,简单的实现如下:
struct FRegisterCompiledInInfo
{
template <typename ... Args>
FRegisterCompiledInInfo(Args&& ... args)
{
RegisterCompiledInInfo(std::forward<Args>(args)...);
}
};
// Multiple registrations
void RegisterCompiledInInfo(const TCHAR* PackageName, const FClassRegisterCompiledInInfo* ClassInfo, size_t NumClassInfo, const FStructRegisterCompiledInInfo* StructInfo, size_t NumStructInfo, const FEnumRegisterCompiledInInfo* EnumInfo, size_t NumEnumInfo)
{
// 注册类信息
for (size_t Index = 0; Index < NumClassInfo; ++Index)
{
const FClassRegisterCompiledInInfo& Info = ClassInfo[Index];
RegisterCompiledInInfo(Info);
}
// 注册结构体信息
for (size_t Index = 0; Index < NumStructInfo; ++Index)
{
const FStructRegisterCompiledInInfo& Info = StructInfo[Index];
RegisterCompiledInInfo(Info);
}
// 注册枚举信息
for (size_t Index = 0; Index < NumEnumInfo; ++Index)
{
const FEnumRegisterCompiledInInfo& Info = EnumInfo[Index];
RegisterCompiledInInfo(Info);
}
}
// 全局单例将传入的反射信息保存起来
void RegisterCompiledInInfo(FPackageRegistrationInfo& InInfo)
{
FPackageDeferredRegistry::Get().AddRegistration(InInfo)
}
继续看RegisterCompiledInInfo(Info)方法