Skip to content

Reflection System Details: Part 2#

Source

Previous article explained the reflection support for UCLASS in UE4. In this article, we will explain the support of Unreal 4 structure (USTRUCT) and enumeration (UENUM) in the form of examples .

First let’s look at the code that tests structure reflection support. We declared a structure with USTRUCT and told Unreal 4 that to support reflection type for this type, we added a float value to it to test the program.

#pragma once

#include "ReflectionStructTest.generated.h"

USTRUCT (Blueprintable)
struct FReflectionTest
{
GENERATED_USTRUCT_BODY ()

UPROPERTY (BlueprintReadWrite)
float ReflectionValue;
};

Generated .generated.h file#

After clicking compile, we got the .generated.h file. This code is the corresponding content after the GENERATED_USTRUCT_BODY () macro is expanded. The code is relatively simple, as shown below:

  // 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

It mainly does the following three things:

  • friend REFLECTIONSTUDY_API class UScriptStruct * Z_Construct_UScriptStruct_FReflectionTest() defines a friend function for creating the reflection object UScriptStruct of this structure
  • REFLECTIONSTUDY_API static class UScriptStruct * StaticStruct (); defines a member function StaticStruct (), so that we can get its reflection structure through the class.
  • #define CURRENT_FILE_ID ReflectionStudy_Source_ReflectionStudy_ReflectionStructTest_h redefines CURRENT_FILE_ID. Please refer to the content mentioned at the beginning for a description of GENERATED_USTRUCT_BODY()
  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, 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;}

We can learn three things from the above code:

  • Create UScriptStruct and add it to the current project specific package
  • Create the ReflectionValue property we added above
  • Add metadata for editor use, such as the BlueprintType we specified above in USTRUCT
  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);

The above code does two things:

  • StaticStruct () determines whether the Singleton is empty. If it is empty, then GetStaticStruct () is called, and GetStaticStruct () just calls the Z_Construct_UScriptStruct_FReflectionTest () function.
  class UScriptStruct * GetStaticStruct (class UScriptStruct * (* InRegister) (), UObject * StructOuter, const TCHAR * StructName, SIZE_T Size, uint32 Crc)
  {
    return (* InRegister) ();
  }
  • Defines a static global variable that is used to register to a list and calls the StaticStruct () method when the engine is initialized.
  static struct FScriptStruct_ReflectionStudy_StaticRegisterNativesFReflectionTest
  {
    FScriptStruct_ReflectionStudy_StaticRegisterNativesFReflectionTest ()
    {
      UScriptStruct :: DeferCppStructOps (FName (TEXT ("ReflectionTest")), new UScriptStruct :: TCppStructOps);
    }
  } ScriptStruct_ReflectionStudy_StaticRegisterNativesFReflectionTest;
  • Define a static variable for storing a CppStructOps (mainly used to dynamically obtain the structure and destructor of a structure) for use in a program.

Next, let’s look at the implementation of enumeration. The test code is as follows:

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

Generated .generated.h file#

Compiling the code, we will get the following code in the .generated.h file, which only defines a FOREACH_ENUM_EREFLECTIONTEST macro.

  #define FOREACH_ENUM_EREFLECTIONTEST (op)
    op (EReflectionTest :: E0)
    op (EReflectionTest :: E1)
    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> EnumNames;
        EnumNames.Add (TPairInitializer (FName (TEXT ("EReflectionTest :: E0")), 0));
        EnumNames.Add (TPairInitializer (FName (TEXT ("EReflectionTest :: E1")), 1));
        EnumNames.Add (TPairInitializer (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;}

The main function of the above code is:

  • Check whether the reflected UEnum is generated. If it is not generated, then a UEnum will be new and E0, E1, and two enums we defined will be added, and an enum of ‘enum name + _Max’ will be added by default value
  • Metadata needed to register the editor, such as the BlueprintType we added in UENUM ()
  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);

This code is similar to the above structure, and it mainly does two things:

  • EReflectionTest_StaticEnum () determines whether the Singleton is empty. If it is empty, use GetStaticEnum to create or return a UEnum object. For details, refer to GetStaticEnum.
  • Register the EReflectionTest_StaticEnum () function to a list for calling when the engine starts.

So far, you have a simple understanding of how classes, structures, functions, properties, and variables support reflection in Unreal 4. I believe you have a certain understanding. But specifically how to register to the engine’s initialization list and how to call it, we have not expanded here, limited to space issues, we will explain the content in the next article, of course, if I can also put in C ++ Several ways to support reflection types are also introduced.


Last update: July 3, 2020