APSchmidt 发表于 2022-3-12 11:20

unreal的几个数据类型的反射

背景交代

因为是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了
注:当然还有其他方式,这里就是提供一种我学习中的测试!
页: [1]
查看完整版本: unreal的几个数据类型的反射