zoukankan      html  css  js  c++  java
  • 深入研究虚幻4反射系统实现原理(二)

    上一篇文章中讲解了UE4中对类(UCLASS)的反射支持,这篇文章我们还是以实例的形式来讲解虚幻4对结构体(USTRUCT)以及枚举(UENUM)的支持。

    结构体

    首先让我们看一下测试结构体反射支持的代码,我们用USTRUCT声明了一个结构体,告诉虚幻4 要对这个类型支持反射类型,我们向其中添加了一个float类型的值来测试程序。

    #pragma once
    
    #include "ReflectionStructTest.generated.h"
    
    USTRUCT(Blueprintable)
    struct FReflectionTest
    {
    	GENERATED_USTRUCT_BODY()
    
    	UPROPERTY(BlueprintReadWrite)
    	float ReflectionValue;
    };
    

      

    生成的.generated.h文件

    点击编译后,我们得到了.generated.h文件,这段代码就是GENERATED_USTRUCT_BODY()宏展开后对应的内容,代码比较简单,如下所示:

    // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
    
    /*===========================================================================
    
        C++ class header boilerplate exported from UnrealHeaderTool.
    
        This is automatically generated by the tools.
    
        DO NOT modify this manually! Edit the corresponding .h files instead!
    
    ===========================================================================*/
    
    #include "ObjectBase.h"
    
    PRAGMA_DISABLE_DEPRECATION_WARNINGS
    #ifdef REFLECTIONSTUDY_ReflectionStructTest_generated_h
    #error "ReflectionStructTest.generated.h already included, missing '#pragma once' in ReflectionStructTest.h"
    #endif
    
    #define REFLECTIONSTUDY_ReflectionStructTest_generated_h
    
    #define ReflectionStudy_Source_ReflectionStudy_ReflectionStructTest_h_9_GENERATED_BODY 
        friend REFLECTIONSTUDY_API class UScriptStruct* Z_Construct_UScriptStruct_FReflectionTest(); 
        REFLECTIONSTUDY_API static class UScriptStruct* StaticStruct();
    
    #undef CURRENT_FILE_ID
    #define CURRENT_FILE_ID ReflectionStudy_Source_ReflectionStudy_ReflectionStructTest_h
    PRAGMA_ENABLE_DEPRECATION_WARNINGS
    

      

    它主要做了以下三件事情:

    • friend REFLECTIONSTUDY_API class UScriptStruct* Z_Construct_UScriptStruct_FReflectionTest() 定义了一个友元函数,用于创建这个结构体的反射对象UScriptStruct
    • REFLECTIONSTUDY_API static class UScriptStruct* StaticStruct(); 定义了一个成员函数StaticStruct(),这样我们可以通过类来获取它的反射结构体。
    • #define CURRENT_FILE_ID ReflectionStudy_Source_ReflectionStudy_ReflectionStructTest_h 重新定义了CURRENT_FILE_ID 详细信息请参照开头提到的内容有对GENERATED_USTRUCT_BODY()的讲解

    .generated.cpp中相关的内容

      

        UScriptStruct* Z_Construct_UScriptStruct_FReflectionTest()
        {
            UPackage* Outer = Z_Construct_UPackage__Script_ReflectionStudy();
            extern uint32 Get_Z_Construct_UScriptStruct_FReflectionTest_CRC();
            static UScriptStruct* ReturnStruct = FindExistingStructIfHotReloadOrDynamic(Outer, TEXT("ReflectionTest"), sizeof(FReflectionTest), Get_Z_Construct_UScriptStruct_FReflectionTest_CRC(), false);
            if (!ReturnStruct)
            {
                ReturnStruct = new(EC_InternalUseOnlyConstructor, Outer, TEXT("ReflectionTest"), RF_Public|RF_Transient|RF_MarkAsNative) UScriptStruct(FObjectInitializer(), NULL, new UScriptStruct::TCppStructOps<FReflectionTest>, EStructFlags(0x00000001));
                UProperty* NewProp_ReflectionValue = new(EC_InternalUseOnlyConstructor, ReturnStruct, TEXT("ReflectionValue"), RF_Public|RF_Transient|RF_MarkAsNative) UFloatProperty(CPP_PROPERTY_BASE(ReflectionValue, FReflectionTest), 0x0010000000000004);
                ReturnStruct->StaticLink();
    
    #if WITH_METADATA
                UMetaData* MetaData = ReturnStruct->GetOutermost()->GetMetaData();
                MetaData->SetValue(ReturnStruct, TEXT("BlueprintType"), TEXT("true"));
                MetaData->SetValue(ReturnStruct, TEXT("IsBlueprintBase"), TEXT("true"));
                MetaData->SetValue(ReturnStruct, TEXT("ModuleRelativePath"), TEXT("ReflectionStructTest.h"));
                MetaData->SetValue(NewProp_ReflectionValue, TEXT("Category"), TEXT("ReflectionTest"));
                MetaData->SetValue(NewProp_ReflectionValue, TEXT("ModuleRelativePath"), TEXT("ReflectionStructTest.h"));
    #endif
            }
            return ReturnStruct;
        }
    
        uint32 Get_Z_Construct_UScriptStruct_FReflectionTest_CRC() { return 486791486U; }
    

      

    从上面的代码中我们可以得知三点:
    • 创建UScriptStruct并添加到当前工程特定的package中
    • 创建我们上文中添加的ReflectionValue属性
    • 添加元数据,供编辑器使用,比如我们上面在USTRUCT中指定的BlueprintType
     
    class UScriptStruct* FReflectionTest::StaticStruct()
    {
    
        extern REFLECTIONSTUDY_API class UPackage* Z_Construct_UPackage__Script_ReflectionStudy();
        static class UScriptStruct* Singleton = NULL;
        if (!Singleton)
        {
            extern REFLECTIONSTUDY_API class UScriptStruct* Z_Construct_UScriptStruct_FReflectionTest();
            extern REFLECTIONSTUDY_API uint32 Get_Z_Construct_UScriptStruct_FReflectionTest_CRC();
            Singleton = GetStaticStruct(Z_Construct_UScriptStruct_FReflectionTest, Z_Construct_UPackage__Script_ReflectionStudy(), TEXT("ReflectionTest"), sizeof(FReflectionTest), Get_Z_Construct_UScriptStruct_FReflectionTest_CRC());
        }
        return Singleton;
    }
    
    static FCompiledInDeferStruct Z_CompiledInDeferStruct_UScriptStruct_FReflectionTest(FReflectionTest::StaticStruct, TEXT("/Script/ReflectionStudy"), TEXT("ReflectionTest"), false, nullptr, nullptr);
    

      

    上面的代码主要做了两件事情:

    • StaticStruct()判断Singleton是否为空,如果为空,那么就调用GetStaticStruct(),面GetStaticStruct()就是调用了Z_Construct_UScriptStruct_FReflectionTest()函数。
    class UScriptStruct *GetStaticStruct(class UScriptStruct *(*InRegister)(), UObject* StructOuter, const TCHAR* StructName, SIZE_T Size, uint32 Crc)
    {
        return (*InRegister)();
    }
    

      

    • 定义了一个静态全局变量,用于注册到一个列表中,在引擎初始化的时候调用StaticStruct()方法。
    static struct FScriptStruct_ReflectionStudy_StaticRegisterNativesFReflectionTest
    {
        FScriptStruct_ReflectionStudy_StaticRegisterNativesFReflectionTest()
        {
            UScriptStruct::DeferCppStructOps(FName(TEXT("ReflectionTest")),new UScriptStruct::TCppStructOps<FReflectionTest>);
        }
    } ScriptStruct_ReflectionStudy_StaticRegisterNativesFReflectionTest;
    

      

    • 定义一个静态变量用于存储一个CppStructOps(主要用来 动态获取结构体的构造和析造函数) ,用于在程序中使用。

      枚举

      接下来我们来看下枚举的实现方式,测试代码如下所示:

    UENUM(BlueprintType)
    enum class EReflectionTest : uint8
    {
        E0,
        E1
    };
    

      

    生成的.generated.h文件

    编译代码,我们在.generated.h文件中会得到如下代码,它仅仅定义了一个FOREACH_ENUM_EREFLECTIONTEST的宏。

    #define FOREACH_ENUM_EREFLECTIONTEST(op) 
        op(EReflectionTest::E0) 
        op(EReflectionTest::E1)
    

      

    .generated.cpp中相关代码

       UEnum* Z_Construct_UEnum_ReflectionStudy_EReflectionTest()
        {
            UPackage* Outer=Z_Construct_UPackage__Script_ReflectionStudy();
            extern uint32 Get_Z_Construct_UEnum_ReflectionStudy_EReflectionTest_CRC();
            static UEnum* ReturnEnum = FindExistingEnumIfHotReloadOrDynamic(Outer, TEXT("EReflectionTest"), 0, Get_Z_Construct_UEnum_ReflectionStudy_EReflectionTest_CRC(), false);
            if (!ReturnEnum)
            {
                ReturnEnum = new(EC_InternalUseOnlyConstructor, Outer, TEXT("EReflectionTest"), RF_Public|RF_Transient|RF_MarkAsNative) UEnum(FObjectInitializer());
                TArray<TPair<FName, uint8>> EnumNames;
                EnumNames.Add(TPairInitializer<FName, uint8>(FName(TEXT("EReflectionTest::E0")), 0));
                EnumNames.Add(TPairInitializer<FName, uint8>(FName(TEXT("EReflectionTest::E1")), 1));
                EnumNames.Add(TPairInitializer<FName, uint8>(FName(TEXT("EReflectionTest::EReflectionTest_MAX")), 2));
                ReturnEnum->SetEnums(EnumNames, UEnum::ECppForm::EnumClass);
                ReturnEnum->CppType = TEXT("EReflectionTest");
    #if WITH_METADATA
                UMetaData* MetaData = ReturnEnum->GetOutermost()->GetMetaData();
                MetaData->SetValue(ReturnEnum, TEXT("BlueprintType"), TEXT("true"));
                MetaData->SetValue(ReturnEnum, TEXT("ModuleRelativePath"), TEXT("ReflectionStructTest.h"));
    #endif
            }
            return ReturnEnum;
        }
    
        uint32 Get_Z_Construct_UEnum_ReflectionStudy_EReflectionTest_CRC() { return 1111016117U; }
    

      

    上面代码的主要作用就是:

    • 查看反射的UEnum有没有生成,如果没有生成,那么会new一个UEnum并且将我们定义的E0,E1,两个枚举添加进来,而且默认都会添加一个 '枚举名+_Max'的一个枚举值
    • 注册编辑器需要的元数据,比如我们在UENUM()中添加的BlueprintType
    static class UEnum* EReflectionTest_StaticEnum()
    {
        extern REFLECTIONSTUDY_API class UPackage* Z_Construct_UPackage__Script_ReflectionStudy();
        static class UEnum* Singleton = NULL;
        if (!Singleton)
        {
            extern REFLECTIONSTUDY_API class UEnum* Z_Construct_UEnum_ReflectionStudy_EReflectionTest();
            Singleton = GetStaticEnum(Z_Construct_UEnum_ReflectionStudy_EReflectionTest, Z_Construct_UPackage__Script_ReflectionStudy(), TEXT("EReflectionTest"));
        }
        return Singleton;
    }
    
    static FCompiledInDeferEnum Z_CompiledInDeferEnum_UEnum_EReflectionTest(EReflectionTest_StaticEnum, TEXT("/Script/ReflectionStudy"), TEXT("EReflectionTest"), false, nullptr, nullptr);
    

      

    这个代码跟上面结构的比较相似,也是主要做了两件事情:

    • EReflectionTest_StaticEnum()判断Singleton是不是为空,如果为空那么通过GetStaticEnum来创建或返回UEnum对象,具体可参考GetStaticEnum实现。
    • 向一个列表中注册EReflectionTest_StaticEnum()函数,用于在引擎启动的时候调用。

    到这里关于虚幻4中怎样对类、结构体、函数、属性、变量支持反射有了一个简单的了解,相信读者你也有了一定的认识。但是具体怎么注册到引擎的初始化列表,以及是如何调用的,我们这里并没有展开来讲,限于篇幅问题,我们下一篇文章再来讲解着方面的内容,当然如果可以我也会把在C++中支持反射类型的几种方式也介绍一下。

  • 相关阅读:
    CSS中各种长度单位总结
    Android中实现双击(多击)事件
    android 文件保存到应用和sd卡中
    在Eclipse中搭建Android开发环境
    算法空间复杂度
    我的Android案例签到日历
    Android使用SDKManager下载SDK速度慢 容易丢包和异常的解决办法
    Android应用系列:仿MIUI的Toast动画效果实现
    Android学习笔记之数据的Sdcard存储方法及操作sdcard的工具类
    Android学习笔记之Menu的ShowAsAction属性的设置
  • 原文地址:https://www.cnblogs.com/ghl_carmack/p/5716512.html
Copyright © 2011-2022 走看看