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++中支持反射类型的几种方式也介绍一下。

  • 相关阅读:
    Microsoft Enterprise Library 5.0 系列(二) Cryptography Application Block (初级)
    Microsoft Enterprise Library 5.0 系列(五) Data Access Application Block
    Microsoft Enterprise Library 5.0 系列(八) Unity Dependency Injection and Interception
    Microsoft Enterprise Library 5.0 系列(九) Policy Injection Application Block
    Microsoft Enterprise Library 5.0 系列(三) Validation Application Block (高级)
    软件研发打油诗祝大家节日快乐
    从挖井的故事中想到开发管理中最容易忽视的几个简单道理
    ITIL管理思想的执行工具发布
    管理类软件设计“渔”之演化
    20070926日下午工作流与ITILQQ群 事件管理 讨论聊天记录
  • 原文地址:https://www.cnblogs.com/ghl_carmack/p/5716512.html
Copyright © 2011-2022 走看看