美文网首页
17 UE5 UObject的反射实现和调用

17 UE5 UObject的反射实现和调用

作者: 游戏开发程序员 | 来源:发表于2024-03-14 11:30 被阅读0次

C#中的反射

  • C#里的Type几乎提供了一切信息数据,简直就像是把编译器编译后的数据都给暴露出来了给你。
  • 用代码声明定义出来的类型,通过一种数据结构完整的把它描述出来,并在运行时再得到。
  • 在C#里,可获取类型信息:Type type = obj.GetType(); or typeof(MyClass)
image.png
  1. Assembly程序集,通常指的是一个dll。
  2. Module是程序集内部的子模块。
  3. Type就是Class对象,完整描述了一个对象的类型信息。
  4. ConstructorInfo描述了Type中的构造函数,可用它来调用特定的构造函数。
  5. EventInfo描述了Type中定义的event事件(UE中的delegate)
  6. FiedInfo描述了Type中的字段,就是C++的成员变量,可以动态读取修改值
  7. PropertyInfo描述了Type中的属性,get/set方法组合,得到后可以获取设置属性值。
  8. MethodInfo描述了Type中的方法。可以动态调用。
  9. ParameterInfo描述了方法中的参数。
  10. 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)
image.png

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);
            }

相关文章

  • UE4对象系统_UObject&UClass

    UObject UObject为对象系统的基类。类层次为: UObjectBase和UObjectBaseUtil...

  • UE4之UFUNCTION介绍

    UFUNCTION UFunction 是虚幻引擎4(UE4)反射系统可识别的C++函数。 UObject 或蓝图...

  • go 的反射 reflect

    Golang语言实现了反射,反射机制就是在运行时动态的调用对象的方法和属性,官方自带的reflect包就是反射相关...

  • 日志体系梳理

    日志可以分为具体实现和接口,区别是如果直接调用接口,会使用多态,根据项目中添加的具体实现,根据反射进行调用 具体实...

  • 【Java 进阶】Java 反射

    反射:获取Class中所有字段(Field)与方法(Method),并实现调用(invoke) Java 反射简单...

  • Java使用invoke反射调用方法导致@Value、@Auto

    发生背景:开发过程中使用到invoke进行反射调用serviceImpl实现类的方法,在运行中发现采用反射方式调用...

  • JVM的反射调用实现

    如何执行反射调用 Java的反射调用是通过java.lang.reflect.Method的invoke调用执行,...

  • 深入理解Java虚拟机三

    一、Java反射的实现原理 1.反射调用的实现 反射是Java语言中一个相当重要的特性,它允许正在运行的Ja...

  • Java反射

    介绍 在运行状态中动态获取和调用对象方法的方式被称为反射。java.lang.Class是反射能够实现的基础,每一...

  • PHP学习代码片段

    反射和动态调用

网友评论

      本文标题:17 UE5 UObject的反射实现和调用

      本文链接:https://www.haomeiwen.com/subject/mwkvzdtx.html