C#中的反射
- C#里的Type几乎提供了一切信息数据,简直就像是把编译器编译后的数据都给暴露出来了给你。
- 用代码声明定义出来的类型,通过一种数据结构完整的把它描述出来,并在运行时再得到。
- 在C#里,可获取类型信息:Type type = obj.GetType(); or typeof(MyClass)
- Assembly程序集,通常指的是一个dll。
- Module是程序集内部的子模块。
- Type就是Class对象,完整描述了一个对象的类型信息。
- ConstructorInfo描述了Type中的构造函数,可用它来调用特定的构造函数。
- EventInfo描述了Type中定义的event事件(UE中的delegate)
- FiedInfo描述了Type中的字段,就是C++的成员变量,可以动态读取修改值
- PropertyInfo描述了Type中的属性,get/set方法组合,得到后可以获取设置属性值。
- MethodInfo描述了Type中的方法。可以动态调用。
- ParameterInfo描述了方法中的参数。
- Attributes指的是Type之上附加的特性,为类上的定义的元数据信息(C++没有)。
C++RTTI反射库 rttr
- Github的C++ RTTI反射库(C++模板应用)
#include <rttr/registration>
using namespace rttr;
struct MyStruct { MyStruct() {}; void func(double) {}; int data; };
RTTR_REGISTRATION
{
registration::class_<MyStruct>("MyStruct")
.constructor<>()
.property("data", &MyStruct::data)
.method("func", &MyStruct::func);
}
QT的反射
- Qt利用基于moc(meta object compiler 元对象编译器,一个预处理器)实现,
- 用一个元对象编译器在程序编译前,分析C++源文件,
- 识别一些QT特殊的宏Q_OBJECT、Q_PROPERTY、Q_INVOKABLE等
- 然后生成相应的moc文件(转换为标准C++兼容的形式),再用C++编译器全部编译链接。
举个例子
1 比如MOC工具读取C++源文件,发现类的定义里有Q_OBJECT宏,
2 它就会为这个类生成另外一个包含有元对象支持代码的c++源文件,
3 这个生成的源文件连同类里的实现文件一起被编译和连接。
#include <QObject>
class MyClass : public QObject
{
Q_OBJECT
Q_PROPERTY(int Member1 READ Member1 WRITE setMember1 )
Q_PROPERTY(QString MEMBER3 READ Member3 WRITE setMember3 )
public:
explicit MyClass(QObject *parent = 0);
signals:
public slots:
public:
Q_INVOKABLE int Member1();
Q_INVOKABLE QString Member3();
Q_INVOKABLE void setMember1( int mem1 );
Q_INVOKABLE void setMember3( const QString& mem3 );
Q_INVOKABLE int func( QString flag );
private:
int m_member1;
QString m_member3;
};
UE里UHT的方案
- 在C++源文件中空的宏做标记
- 用UHT分析生成generated.h/.cpp文件,之后再一起编译。
- generated.h/.cpp文件中对宏展开
UCLASS()
class UTestObject : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite, Category = "Test")
FString CurPlayerName;
UPROPERTY(BlueprintReadWrite, Category = "Test")
int32 CurPlayerAge;
UFUNCTION(BlueprintCallable, Category = "Test")
FString GetOutInfo();
};
generated文件和UML图
- 1 (CURRENT_FILE_ID)_(LINE)_GENERATED_BODY
- 2 TestCall_Source_TestMyClass_MyActor_h_12_GENERATED_BODY
- 3 UFUNCTION(BlueprintNativeEvent)
UHT对函数的3个类别支持
- BlueprintCallable C++定义 蓝图调用
- BlueprintNativeEvent C++定义 蓝图可覆盖
- BlueprintImplementableEvent C++声明和调用 蓝图去实现
UFUNCTION(BlueprintCallable)
bool IsBoy(); // 需要函数定义
// UHT会自动声明exec前缀函数
DECLARE_FUNCTION(execIsBoy);
UFUNCTION(BlueprintNativeEvent)
bool IsGirl();
// 函数名定义为: bool UMyObject::IsGirl_Implementation()
// UHT会自动声明exec前缀函数
DECLARE_FUNCTION(execIsGirl);
......
// StaticClass中的注册函数
void UTestObject::StaticRegisterNativesUTestObject()
{
UClass* Class = UTestObject::StaticClass();
static const FNameNativePtrPair Funcs[] = {
{ "GetOutInfo", &UTestObject::execGetOutInfo },
{ "IsBoy", &UTestObject::execIsBoy },
{ "IsGirl", &UTestObject::execIsGirl },
};
FNativeFunctionRegistrar::RegisterFunctions(Class, Funcs, UE_ARRAY_COUNT(Funcs));
}
- 观察两个函数的cpp中不同:
DEFINE_FUNCTION(UMyObject::execIsBoy)
{
P_FINISH;
P_NATIVE_BEGIN;
*(bool*)Z_Param__Result=P_THIS->IsBoy();
P_NATIVE_END;
}
DEFINE_FUNCTION(UMyObject::execIsGirl)
{
P_FINISH;
P_NATIVE_BEGIN;
*(bool*)Z_Param__Result=P_THIS->IsGirl_Implementation();
P_NATIVE_END;
}
- BlueprintImplementableEvent 会调用FindFunctionChecked关联到蓝图
UFUNCTION(BlueprintImplementableEvent)
bool IsBaBy();
// UHT对IsBaBy 和 IsGirl
static FName NAME_UMyObject_IsBaBy = FName(TEXT("IsBaBy"));
bool UMyObject::IsBaBy()
{
MyObject_eventIsBaBy_Parms Parms;
ProcessEvent(FindFunctionChecked(NAME_UMyObject_IsBaBy),&Parms);
return !!Parms.ReturnValue;
}
static FName NAME_UMyObject_IsGirl = FName(TEXT("IsGirl"));
bool UMyObject::IsGirl()
{
MyObject_eventIsGirl_Parms Parms;
ProcessEvent(FindFunctionChecked(NAME_UMyObject_IsGirl),&Parms);
return !!Parms.ReturnValue;
}
Z_Construct_UClass_UTestObject_Statics 统计属性
static const UECodeGen_Private::FPropertyParamsBase* const PropPointers[];
const UECodeGen_Private::FIntPropertyParams Z_Construct_UClass_UTestObject_Statics::NewProp_CurPlayerAge =
{ "CurPlayerAge", nullptr, (EPropertyFlags)0x0010000000000004, UECodeGen_Private::EPropertyGenFlags::Int,
RF_Public|RF_Transient|RF_MarkAsNative, nullptr, nullptr, 1, STRUCT_OFFSET(UTestObject, CurPlayerAge),
METADATA_PARAMS(UE_ARRAY_COUNT(Z_Construct_UClass_UTestObject_Statics::NewProp_CurPlayerAge_MetaData),
Z_Construct_UClass_UTestObject_Statics::NewProp_CurPlayerAge_MetaData) };
const UECodeGen_Private::FPropertyParamsBase* const Z_Construct_UClass_UTestObject_Statics::PropPointers[] = {
(const UECodeGen_Private::FPropertyParamsBase*)&Z_Construct_UClass_UTestObject_Statics::NewProp_CurPlayerName,
(const UECodeGen_Private::FPropertyParamsBase*)&Z_Construct_UClass_UTestObject_Statics::NewProp_CurPlayerAge,
};
Z_Construct_UClass_UTestObject_Statics 统计函数
const FClassFunctionLinkInfo Z_Construct_UClass_UTestObject_Statics::FuncInfo[] = {
{ &Z_Construct_UFunction_UTestObject_GetOutInfo, "GetOutInfo" }, // 3640447053
{ &Z_Construct_UFunction_UTestObject_IsBaBy, "IsBaBy" }, // 271090006
{ &Z_Construct_UFunction_UTestObject_IsBoy, "IsBoy" }, // 369736746
{ &Z_Construct_UFunction_UTestObject_IsGirl, "IsGirl" }, // 4061052473
};
获取UObject中的成员变量
- 从生成的StaticClass上FindPropertyByName查询
- 再从UObject中的属性数组中获取属性指针
- 可修改 可查询
// 反射功能 获取反射生成的类StaticClass
UClass* pStaticClass = pObj->GetClass();
if(pStaticClass)
{
// 获取字段指针
FProperty* pProperty = pStaticClass->FindPropertyByName(FName(TEXT("CurPlayerName")));
// 转换为对应的字段类型 static_cast
FStrProperty* pStrProp = CastField<FStrProperty>(pProperty);
if(pStrProp)
{
// 从UObject中的属性数组中获取属性指针
void* pValue = pStrProp->ContainerPtrToValuePtr<void>(pObj);
// 获取属性值
FString Name = pStrProp->GetPropertyValue(pValue);
// 修改属性值
pStrProp->SetPropertyValue(pValue, TEXT("Bill"));
}
}
获取UObject中的成员函数
- pStaticClass->FindFunctionByName拿到指针
- struct Func_Parms 结构体包含传入和返回参数
// 获取函数指针
UFunction* pFunc = pStaticClass->FindFunctionByName(FName(TEXT("GetOutInfo")));
if(pFunc)
{
// 传入参数和返回值结构体
struct Func_Parms
{
FString Caller;
FString ReturnValue;
};
Func_Parms args;
args.Caller = TEXT("Test");
// 调用
pObj->ProcessEvent(pFunc, &args);
// 打印返回
UE_LOG(TestLog, Warning, TEXT("%s"), *args.ReturnValue);
}
网友评论