找回密码
 立即注册
查看: 317|回复: 0

unreal的几个数据类型的反射

[复制链接]
发表于 2022-3-12 11:20 | 显示全部楼层 |阅读模式
背景交代

因为是demo使用,不想接入太多第三库,但是网络通讯的需要的一个格式,方便两端通讯;
ue有一套json解释,所以计划使用json当作通讯流。
项目使用的消息体大致格式如下:
USTRUCT()
struct FTestStruct
{
    GENERATED_BODY()
    UPROPERTY()
    FString Field1;
    UPROPERTY()
    FString Field2;
};
因为消息体的格式是自定义的,又不想每次都写Encode和Decode,最好的办法就是写一个通用的模板。
知识点

想要做到上述的方法,有几个点需要了解的:

  • 模板函数限定
  • unreal的反射
模板函数限定

这里就不细说,相关内容可以自行google;
// template<typename T, typename = typename TEnableIf<TIsClass<T>::Value>::Type> // 两种都可以!
template<typename T, typename = std::enable_if_t<std::is_class_v<T>, void> >
class NetUtility
{
public:
    static void Test(T* value, TArray<uint8>&  content)
    {
        auto file = value->StaticStruct()->ChildProperties;
        while (file != nullptr)
        {
            UE_LOG(LogDemo, Display, TEXT("%s"), *(file->GetName()));
            file = file->Next;
        }
    }
};
或者将消息体统一继承一个Parent struct,然后使用std::is_base_of<>去限定也可以!
unreal的反射


  • 获取反射类型
  • 成员变量(int float string)
  • Object子类属性
  • 修改和取值
  • 成员函数
  • 接口
获取反射类型

Unreal对满足其规则的类型定义,都会生成配套的反射;如果想使用unreal的反射,只需要使用USTRUCT、UCLASS就可以了(GENERATED_BODY之类的肯定少不了,这里不细说!)。
这里用两个例子说明一下:
USTRUCT()
struct FTestStruct
{
    GENERATED_BODY()
    UPROPERTY()
    FString Field1;
    UPROPERTY()
    FString Field2;
};

UCLASS()
class UTestClass : public UObject
{
    GENERATED_BODY()
public:
    UPROPERTY()
    FString Field1;
    UPROPERTY()
    FString Field2;
};
unreal提供了一下几个获取反射类型的方法
ReflectedTypeAccessors.h
/*-----------------------------------------------------------------------------
C++ templated Static(Class/Struct/Enum) retrieval function prototypes.
-----------------------------------------------------------------------------*/

template<typename ClassType>    UClass*         StaticClass();
template<typename StructType>   UScriptStruct*  StaticStruct();
template<typename EnumType>     UEnum*          StaticEnum();
更加具体的在文件对应的XXXX.generated.h里有!
成员变量(int float string)


  • 通过ChildProperties获取所有的Property
  • 使用TFieldIterator<UProperty>构建迭代器
// 通过`ChildProperties`获取所有的`Property`
auto file = value->StaticStruct()->ChildProperties;
while (file != nullptr)
{
    UE_LOG(LogDemo, Display, TEXT("%s"), *(file->GetName())); // 变量名称
    file = file->Next;
}

// 使用`TFieldIterator<UProperty>`构建迭代器
for(TFieldIterator<FProperty> it(value->StaticStruct()); it;++it)
{
    UE_LOG(LogDemo, Display, TEXT("%s"), *(it->GetName()));
}
注意:如果是UCLASS,请使用StaticClass,UEnum类似
Object子类属性

在讲这个之前要提一下
/** Searches property link chain for a property with the specified name */
FProperty* FindPropertyByName(FName InName) const;
该方法可以直接从Ustruct或者UClass中找到指定的UProperty;

  • 通过变量名寻找UClass的对应属性
  • 将找到的属性转换为对应的Object*指针
UCLASS()
class UTestObject : public UObject
{
    GENERATED_BODY()
public:
    UPROPERTY()
    AActor* TestActor;
};

template<typename T, typename = typename TEnableIf<TIsClass<T>::Value>::Type>
// template<typename T, typename = std::enable_if_t<std::is_class_v<T>, void> >
class NetUtility
{
public:
    static void Test(T* value, TArray<uint8>&  content)
    {
        auto cls = value->StaticClass();
        for(TFieldIterator<FProperty> it(cls); it;++it)
        {
            auto _property = CastField<FObjectProperty>(*it);
            UE_LOG(LogDemo, Display, TEXT("%s"), *_property->GetName());
        }
    }
};
除了FObjectProperty还有很多。
DefineUPropertyMacros.h中比较全(虽然都是对DEPRECATED_MACRO的重定义,但是全呀!)!
到这里估计的时候我就遇到一个问题:
能不能直接识别判定是什么类型后再指定CastField?
在Field.h文件中有这样的代码
template<typename FieldType>
FORCEINLINE FieldType* CastField(FField* Src)
{
    return Src && Src->HasAnyCastFlags(FieldType::StaticClassCastFlagsPrivate()) ? static_cast<FieldType*>(Src) : nullptr;
}

template<typename FieldType>
FORCEINLINE const FieldType* CastField(const FField* Src)
{
    return Src && Src->HasAnyCastFlags(FieldType::StaticClassCastFlagsPrivate()) ? static_cast<const FieldType*>(Src) : nullptr;
}
已经有类型判定了~
修改和取值

UCLASS()
class UTestObject : public UObject
{
    GENERATED_BODY()
public:
    UPROPERTY()
    AActor* ActorValue;
    UPROPERTY()
    FString StringValue;
    UPROPERTY()
    float FloatValue;
};

template<typename T, typename = typename TEnableIf<TIsClass<T>::Value>::Type>
// template<typename T, typename = std::enable_if_t<std::is_class_v<T>, void> >
class NetUtility1
{
public:
    static void Test(T* value, TArray<uint8>&  content)
    {
        auto cls = value->StaticClass();
        for(TFieldIterator<FProperty> it(cls); it;++it)
        {
            auto _objectProperty = CastField<FObjectProperty>(*it);
            if (_objectProperty)
            {
                void* param = _objectProperty->ContainerPtrToValuePtr<void*>(value);
                AActor* pValue = Cast<AActor>(_objectProperty->GetPropertyValue(param));
                UE_LOG(LogDemo, Display, TEXT("before modify %s : %s"), *(it->GetName()), *pValue->GetName());
                _objectProperty->SetPropertyValue(param, NewObject<AActor>());
                pValue = Cast<AActor>(_objectProperty->GetPropertyValue(param));
                UE_LOG(LogDemo, Display, TEXT("after modify %s : %s"), *(it->GetName()), *pValue->GetName());
                continue;
            }
            auto _stringProperty = CastField<FStrProperty>(*it);
            if (_stringProperty)
            {
                void* param = _stringProperty->ContainerPtrToValuePtr<void*>(value);
                FString pValue = _stringProperty->GetPropertyValue(param);
                UE_LOG(LogDemo, Display, TEXT("before modify %s : %s"), *(it->GetName()), *pValue);
                _stringProperty->SetPropertyValue(param, TEXT("CCCCC"));
                pValue = _stringProperty->GetPropertyValue(param);
                UE_LOG(LogDemo, Display, TEXT("after modify %s : %s"), *(it->GetName()), *pValue);
                continue;
            }
            auto _floatProperty = CastField<FFloatProperty>(*it);
            if (_floatProperty)
            {
                void* param = _floatProperty->ContainerPtrToValuePtr<void*>(value);
                auto pValue = _floatProperty->GetPropertyValue_InContainer(param, 0);

                UE_LOG(LogDemo, Display, TEXT("before modify %s : %f"), *(it->GetName()), pValue);
                _floatProperty->SetPropertyValue(param, 444.0f);
                pValue = _floatProperty->GetPropertyValue(param);
                UE_LOG(LogDemo, Display, TEXT("after modify %s : %f"), *(it->GetName()), pValue);
                continue;
            }
        }
    }
};
成员函数

成员函数的获取和属性有点类似

  • 使用TFieldIterator<UProperty>构建迭代器
for(TFieldIterator<UFunction> func(value->StaticClass()); func;++func)
{}
如果是还需要获取函数的参数列表:
for(TFieldIterator<UFunction> func(value->StaticClass()); func;++func)
{
    for(TFieldIterator<FProperty> args(*func); args;++args)
    {}
}
接口

成员函数的获取和属性有点类似

  • 通过ChildProperties获取所有的Property
  • 使用TFieldIterator<UProperty>构建迭代器
// 通过`Interfaces`获取所有的`Interface`
auto interfaceArray = value->StaticStruct()->Interfaces;
for (auto interface : interfaceArray)
{
}

// 使用`TFieldIterator<FInterfaceProperty>`构建迭代器
for(TFieldIterator<FInterfaceProperty> it(value->StaticStruct()); it;++it)
{
}
函数调用

UFunction的调用基本就是UFunction的使用了,这里就不细说了
UFunction->Invoke();
到此,基本就可以直接反射出模板函数中的消息体的每个UProperty的值和定义的变量名称了;从而就能生成json了
注:当然还有其他方式,这里就是提供一种我学习中的测试!
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-22 17:26 , Processed in 0.090608 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表