[UEC++ Learning] Basics of Reflective Programming

If a class wants to have reflection, it needs to include the following content
(1) UCLASS()prefix macro
(2) class name should be added with a reasonable name, such as Aor U
(3) reasonable inheritance of the corresponding class name, such as inheritance AActoror UClass
(4) add the corresponding reflection header file, XXX.generated.h
(5) add the macro corresponding to the reflection,GENERATED_BODY()

The reflection call should be included GENERATED_UCLASS_BODY()under the macro declaration, and the reflection constructor under the declaration is as follows:

AReflectionGameMode::AReflectionGameMode(const FObjectInitializer& ObjectInitializer) : AGameModeBase(ObjectInitializer)
{
    
    
	// 你的反射代码
}

1. Get UClass

/** 通过创建对象获取Class */
UReflectionTestClass* TestObject = NewObject<UReflectionTestClass>();
UClass* TestClass = TestObject->GetClass();
/** 直接获取静态类 */
UClass* TestClass = UReflectionTestClass::StaticClass();

2. Get the class name

/** 获取类名 */
FName ClassName = TestClass->GetFName();

3. Get the member variables of the class

/** 获取类的成员变量 */
for(FProperty* Property = TestClass->PropertyLink; Property; Property = Property->PropertyLinkNext)
{
    
    
	// 获取成员变量的名子
	FString PropertyName = Property->GetName();
	// 获取成员变量的类型
	FString PropertyType = Property->GetCPPType();
	if(PropertyType == "FString")
	{
    
    
		// 强制转化为String
		FStrProperty* StringProperty = CastField<FStrProperty>(Property);
		// 传入某一个对象,表示获取哪一个对象的成员变量值,得到地址
		void* Addr = StringProperty->ContainerPtrToValuePtr<void>(TestObject);
		// 通过地址获取成员变量值
		FString PropertyValue = StringProperty->GetPropertyValue(Addr);
		// 获取成员变量的元数据,传入key值,这里获取的是元数据中的Category项值
		FString PropertyMetaCategory = StringProperty->GetMetaData(TEXT("Category"));
	}
}

Note: Get the metadata of the class attribute, that is, get the metadata information in UPROPERTYand , and there is also metadata information in the tag.UFUNCTION

UCLASS(Min)
class LEARNINGPROJECTV5_API UReflectionTestClass : public UObject
{
    
    
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, Category = "ReflectionTestCategory")
	FString TestParam = "Nello";

	UFUNCTION(BlueprintCallable)
	void TestFuction(const FString& Input);
	
public:
	UReflectionTestClass();
	~UReflectionTestClass();
};

4. Get the method of the class

/** 获取类的方法
 * 注意:类就算没有方法也会自动生成一个可反射的方法
 */
for(TFieldIterator<UFunction> IteratorOfFunction(TestClass); IteratorOfFunction; ++IteratorOfFunction)
{
    
    
	UFunction* Function = *IteratorOfFunction;
	// 获取方法的名称
	FString FunctionName = Function->GetName();
	// 获取方法的元数据标记
	EFunctionFlags FunctionFlags = Function->FunctionFlags;
	// 获取方法的参数,会将返回值也作为参数来输出
	for(TFieldIterator<FProperty> IteratorOfParam(Function); IteratorOfParam; ++IteratorOfParam)
	{
    
    
		FProperty* Param = *IteratorOfParam;
		// 参数的名称
		FString ParamName = Param->GetName();
		// 参数的类型
		FString ParamType = Param->GetCPPType();
		// 参数的标记
		EPropertyFlags ParamFlag = Param->GetPropertyFlags(); 
	}
}

Note:
(1) The class will automatically generate a method that can be reflected.
(2) The mark of the function is that EFunctionFlagsthe result will be added by bit operation, indicating that various attributes of the function include metadata and other information.
(3) The mark of the parameter is that EPropertyFlagsthe result will indicate various attributes of the parameter in the form of bit operation, such as whether the parameter is a return value, metadata and other information.

enum EFunctionFlags : uint32
{
    
    
	// Function flags.
	FUNC_None				= 0x00000000,

	FUNC_Final				= 0x00000001,	// Function is final (prebindable, non-overridable function).
	FUNC_RequiredAPI		= 0x00000002,	// Indicates this function is DLL exported/imported.
	FUNC_BlueprintAuthorityOnly= 0x00000004,   // Function will only run if the object has network authority
	FUNC_BlueprintCosmetic	= 0x00000008,   // Function is cosmetic in nature and should not be invoked on dedicated servers
	// FUNC_				= 0x00000010,   // unused.
	// FUNC_				= 0x00000020,   // unused.
	FUNC_Net				= 0x00000040,   // Function is network-replicated.
	FUNC_NetReliable		= 0x00000080,   // Function should be sent reliably on the network.
	FUNC_NetRequest			= 0x00000100,	// Function is sent to a net service
	FUNC_Exec				= 0x00000200,	// Executable from command line.
	FUNC_Native				= 0x00000400,	// Native function.
	FUNC_Event				= 0x00000800,   // Event function.
	FUNC_NetResponse		= 0x00001000,   // Function response from a net service
	FUNC_Static				= 0x00002000,   // Static function.
	FUNC_NetMulticast		= 0x00004000,	// Function is networked multicast Server -> All Clients
	FUNC_UbergraphFunction	= 0x00008000,   // Function is used as the merge 'ubergraph' for a blueprint, only assigned when using the persistent 'ubergraph' frame
	FUNC_MulticastDelegate	= 0x00010000,	// Function is a multi-cast delegate signature (also requires FUNC_Delegate to be set!)
	FUNC_Public				= 0x00020000,	// Function is accessible in all classes (if overridden, parameters must remain unchanged).
	FUNC_Private			= 0x00040000,	// Function is accessible only in the class it is defined in (cannot be overridden, but function name may be reused in subclasses.  IOW: if overridden, parameters don't need to match, and Super.Func() cannot be accessed since it's private.)
	FUNC_Protected			= 0x00080000,	// Function is accessible only in the class it is defined in and subclasses (if overridden, parameters much remain unchanged).
	FUNC_Delegate			= 0x00100000,	// Function is delegate signature (either single-cast or multi-cast, depending on whether FUNC_MulticastDelegate is set.)
	FUNC_NetServer			= 0x00200000,	// Function is executed on servers (set by replication code if passes check)
	FUNC_HasOutParms		= 0x00400000,	// function has out (pass by reference) parameters
	FUNC_HasDefaults		= 0x00800000,	// function has structs that contain defaults
	FUNC_NetClient			= 0x01000000,	// function is executed on clients
	FUNC_DLLImport			= 0x02000000,	// function is imported from a DLL
	FUNC_BlueprintCallable	= 0x04000000,	// function can be called from blueprint code
	FUNC_BlueprintEvent		= 0x08000000,	// function can be overridden/implemented from a blueprint
	FUNC_BlueprintPure		= 0x10000000,	// function can be called from blueprint code, and is also pure (produces no side effects). If you set this, you should set FUNC_BlueprintCallable as well.
	FUNC_EditorOnly			= 0x20000000,	// function can only be called from an editor scrippt.
	FUNC_Const				= 0x40000000,	// function can be called from blueprint code, and only reads state (never writes state)
	FUNC_NetValidate		= 0x80000000,	// function must supply a _Validate implementation

	FUNC_AllFlags		= 0xFFFFFFFF,
};
enum EPropertyFlags : uint64
{
    
    
	CPF_None = 0,

	CPF_Edit							= 0x0000000000000001,	///< Property is user-settable in the editor.
	CPF_ConstParm						= 0x0000000000000002,	///< This is a constant function parameter
	CPF_BlueprintVisible				= 0x0000000000000004,	///< This property can be read by blueprint code
	CPF_ExportObject					= 0x0000000000000008,	///< Object can be exported with actor.
	CPF_BlueprintReadOnly				= 0x0000000000000010,	///< This property cannot be modified by blueprint code
	CPF_Net								= 0x0000000000000020,	///< Property is relevant to network replication.
	CPF_EditFixedSize					= 0x0000000000000040,	///< Indicates that elements of an array can be modified, but its size cannot be changed.
	CPF_Parm							= 0x0000000000000080,	///< Function/When call parameter.
	CPF_OutParm							= 0x0000000000000100,	///< Value is copied out after function call.
	CPF_ZeroConstructor					= 0x0000000000000200,	///< memset is fine for construction
	CPF_ReturnParm						= 0x0000000000000400,	///< Return value.
	CPF_DisableEditOnTemplate			= 0x0000000000000800,	///< Disable editing of this property on an archetype/sub-blueprint
	CPF_NonNullable						= 0x0000000000001000,	///< Object property can never be null
	CPF_Transient   					= 0x0000000000002000,	///< Property is transient: shouldn't be saved or loaded, except for Blueprint CDOs.
	CPF_Config      					= 0x0000000000004000,	///< Property should be loaded/saved as permanent profile.
	//CPF_								= 0x0000000000008000,	///< 
	CPF_DisableEditOnInstance			= 0x0000000000010000,	///< Disable editing on an instance of this class
	CPF_EditConst   					= 0x0000000000020000,	///< Property is uneditable in the editor.
	CPF_GlobalConfig					= 0x0000000000040000,	///< Load config from base class, not subclass.
	CPF_InstancedReference				= 0x0000000000080000,	///< Property is a component references.
	//CPF_								= 0x0000000000100000,	///<
	CPF_DuplicateTransient				= 0x0000000000200000,	///< Property should always be reset to the default value during any type of duplication (copy/paste, binary duplication, etc.)
	//CPF_								= 0x0000000000400000,	///< 
	//CPF_    							= 0x0000000000800000,	///< 
	CPF_SaveGame						= 0x0000000001000000,	///< Property should be serialized for save games, this is only checked for game-specific archives with ArIsSaveGame
	CPF_NoClear							= 0x0000000002000000,	///< Hide clear (and browse) button.
	//CPF_  							= 0x0000000004000000,	///<
	CPF_ReferenceParm					= 0x0000000008000000,	///< Value is passed by reference; CPF_OutParam and CPF_Param should also be set.
	CPF_BlueprintAssignable				= 0x0000000010000000,	///< MC Delegates only.  Property should be exposed for assigning in blueprint code
	CPF_Deprecated  					= 0x0000000020000000,	///< Property is deprecated.  Read it from an archive, but don't save it.
	CPF_IsPlainOldData					= 0x0000000040000000,	///< If this is set, then the property can be memcopied instead of CopyCompleteValue / CopySingleValue
	CPF_RepSkip							= 0x0000000080000000,	///< Not replicated. For non replicated properties in replicated structs 
	CPF_RepNotify						= 0x0000000100000000,	///< Notify actors when a property is replicated
	CPF_Interp							= 0x0000000200000000,	///< interpolatable property for use with cinematics
	CPF_NonTransactional				= 0x0000000400000000,	///< Property isn't transacted
	CPF_EditorOnly						= 0x0000000800000000,	///< Property should only be loaded in the editor
	CPF_NoDestructor					= 0x0000001000000000,	///< No destructor
	//CPF_								= 0x0000002000000000,	///<
	CPF_AutoWeak						= 0x0000004000000000,	///< Only used for weak pointers, means the export type is autoweak
	CPF_ContainsInstancedReference		= 0x0000008000000000,	///< Property contains component references.
	CPF_AssetRegistrySearchable			= 0x0000010000000000,	///< asset instances will add properties with this flag to the asset registry automatically
	CPF_SimpleDisplay					= 0x0000020000000000,	///< The property is visible by default in the editor details view
	CPF_AdvancedDisplay					= 0x0000040000000000,	///< The property is advanced and not visible by default in the editor details view
	CPF_Protected						= 0x0000080000000000,	///< property is protected from the perspective of script
	CPF_BlueprintCallable				= 0x0000100000000000,	///< MC Delegates only.  Property should be exposed for calling in blueprint code
	CPF_BlueprintAuthorityOnly			= 0x0000200000000000,	///< MC Delegates only.  This delegate accepts (only in blueprint) only events with BlueprintAuthorityOnly.
	CPF_TextExportTransient				= 0x0000400000000000,	///< Property shouldn't be exported to text format (e.g. copy/paste)
	CPF_NonPIEDuplicateTransient		= 0x0000800000000000,	///< Property should only be copied in PIE
	CPF_ExposeOnSpawn					= 0x0001000000000000,	///< Property is exposed on spawn
	CPF_PersistentInstance				= 0x0002000000000000,	///< A object referenced by the property is duplicated like a component. (Each actor should have an own instance.)
	CPF_UObjectWrapper					= 0x0004000000000000,	///< Property was parsed as a wrapper class like TSubclassOf<T>, FScriptInterface etc., rather than a USomething*
	CPF_HasGetValueTypeHash				= 0x0008000000000000,	///< This property can generate a meaningful hash value.
	CPF_NativeAccessSpecifierPublic		= 0x0010000000000000,	///< Public native access specifier
	CPF_NativeAccessSpecifierProtected	= 0x0020000000000000,	///< Protected native access specifier
	CPF_NativeAccessSpecifierPrivate	= 0x0040000000000000,	///< Private native access specifier
	CPF_SkipSerialization				= 0x0080000000000000,	///< Property shouldn't be serialized, can still be exported to text
};

5. Get the markup of the class

/** 获取类的标记 */
EClassFlags ClassFlags = TestClass->ClassFlags;
// 判断是否存在某一标记
bool ClassHasConfigFlag = TestClass->HasAnyClassFlags(EClassFlags::CLASS_Config);
enum EClassFlags
{
    
    
	/** No Flags */
	CLASS_None				  = 0x00000000u,
	/** Class is abstract and can't be instantiated directly. */
	CLASS_Abstract            = 0x00000001u,
	/** Save object configuration only to Default INIs, never to local INIs. Must be combined with CLASS_Config */
	CLASS_DefaultConfig		  = 0x00000002u,
	/** Load object configuration at construction time. */
	CLASS_Config			  = 0x00000004u,
	/** This object type can't be saved; null it out at save time. */
	CLASS_Transient			  = 0x00000008u,
	/** This object type may not be available in certain context. (i.e. game runtime or in certain configuration). Optional class data is saved separately to other object types. (i.e. might use sidecar files) */
	CLASS_Optional            = 0x00000010u,
	/** */
	CLASS_MatchedSerializers  = 0x00000020u,
	/** Indicates that the config settings for this class will be saved to Project/User*.ini (similar to CLASS_GlobalUserConfig) */
	CLASS_ProjectUserConfig	  = 0x00000040u,
	/** Class is a native class - native interfaces will have CLASS_Native set, but not RF_MarkAsNative */
	CLASS_Native			  = 0x00000080u,
	/** Don't export to C++ header. */
	CLASS_NoExport UE_DEPRECATED(5.1, "CLASS_NoExport should no longer be used. It is no longer being set by engine code.") = 0x00000100u,
	/** Do not allow users to create in the editor. */
	CLASS_NotPlaceable        = 0x00000200u,
	/** Handle object configuration on a per-object basis, rather than per-class. */
	CLASS_PerObjectConfig     = 0x00000400u,
	
	/** Whether SetUpRuntimeReplicationData still needs to be called for this class */
	CLASS_ReplicationDataIsSetUp = 0x00000800u,
	
	/** Class can be constructed from editinline New button. */
	CLASS_EditInlineNew		  = 0x00001000u,
	/** Display properties in the editor without using categories. */
	CLASS_CollapseCategories  = 0x00002000u,
	/** Class is an interface **/
	CLASS_Interface           = 0x00004000u,
	/**  Do not export a constructor for this class, assuming it is in the cpptext **/
	CLASS_CustomConstructor UE_DEPRECATED(5.1, "CLASS_CustomConstructor should no longer be used. It is no longer being set by engine code.") = 0x00008000u,
	/** all properties and functions in this class are const and should be exported as const */
	CLASS_Const			      = 0x00010000u,

	/** Class flag indicating objects of this class need deferred dependency loading */
	CLASS_NeedsDeferredDependencyLoading = 0x00020000u,
	
	/** Indicates that the class was created from blueprint source material */
	CLASS_CompiledFromBlueprint  = 0x00040000u,

	/** Indicates that only the bare minimum bits of this class should be DLL exported/imported */
	CLASS_MinimalAPI	      = 0x00080000u,
	
	/** Indicates this class must be DLL exported/imported (along with all of it's members) */
	CLASS_RequiredAPI	      = 0x00100000u,

	/** Indicates that references to this class default to instanced. Used to be subclasses of UComponent, but now can be any UObject */
	CLASS_DefaultToInstanced  = 0x00200000u,

	/** Indicates that the parent token stream has been merged with ours. */
	CLASS_TokenStreamAssembled  = 0x00400000u,
	/** Class has component properties. */
	CLASS_HasInstancedReference= 0x00800000u,
	/** Don't show this class in the editor class browser or edit inline new menus. */
	CLASS_Hidden			  = 0x01000000u,
	/** Don't save objects of this class when serializing */
	CLASS_Deprecated		  = 0x02000000u,
	/** Class not shown in editor drop down for class selection */
	CLASS_HideDropDown		  = 0x04000000u,
	/** Class settings are saved to <AppData>/..../Blah.ini (as opposed to CLASS_DefaultConfig) */
	CLASS_GlobalUserConfig	  = 0x08000000u,
	/** Class was declared directly in C++ and has no boilerplate generated by UnrealHeaderTool */
	CLASS_Intrinsic			  = 0x10000000u,
	/** Class has already been constructed (maybe in a previous DLL version before hot-reload). */
	CLASS_Constructed		  = 0x20000000u,
	/** Indicates that object configuration will not check against ini base/defaults when serialized */
	CLASS_ConfigDoNotCheckDefaults = 0x40000000u,
	/** Class has been consigned to oblivion as part of a blueprint recompile, and a newer version currently exists. */
	CLASS_NewerVersionExists  = 0x80000000u,
};

Summary:
(1) Obtaining the tag is the sum of bit operations. The tag contains not only the value of metadata, but also the value of various marked attributes.
(2) Regardless of whether it is an attribute mark, a function mark, or a class mark, it can be HasAnyClassFlagsquickly judged whether a certain mark is contained.

6. Get the parent and child classes of the class

/** 获取类的父类的名称 */
UClass* ParentClass = TestObject->GetClass()->GetSuperClass();
FString ParentClassName = ParentClass->GetName();

/** 判断一个类是否是另一个类的子类 */
// 获取静态类
UClass* Class1 = UReflectionTestClass::StaticClass();
UClass* Class2 = UObject::StaticClass();
// 判断 Class1 是否是 Class2 的孩子
if(Class1->IsChildOf(Class2))
{
    
    
	
}

/** 获取特定类的所有子类 */
TArray<UClass*> ChildOfClassResults;
// 第一个参数:传入要查找类的子类的该类;第二个参数:接受返回的数组;第三个参数:是否循环迭代,即是否查找子类的子类
GetDerivedClasses(UReflectionTestClass::StaticClass(), ChildOfClassResults, false);

7. Get all objects generated by the class

/** 获取特定类生成的所有对象 */
TArray<UObject*> ObjectOfClassResults;
// 第一个参数:传入要查找类的对象的该类;第二个参数:接受返回的数组
GetObjectsOfClass(UReflectionTestClass::StaticClass(), ObjectOfClassResults, false);

8. Find the corresponding class/method by name

/** 根据给定字符串查找相应的类 */
// 通过名字查找类的时候不要加名称前缀,A/U等
UClass* ClassByStringName = FindObject<UClass>(ANY_PACKAGE, *FString("ReflectionTestClass"), true);
/** 根据给定字符串查找相应的枚举 */
UEnum* EnumByStringName = FindObject<UEnum>(ANY_PACKAGE, *FString("ReflectionTestEnum"), true);
/** 根据给定字符串查找相应的蓝图类, 注意该蓝图类需要继承自UObject */
UBlueprint* BlueprintByStringName = FindObject<UBlueprint>(ANY_PACKAGE, *FString("BP_ReflectionTestBlueprint"), true);
/** 根据给定字符串查找相应的类的成员函数,EIncludeSuperFlag::ExcludeSuper表示不查找父类 */
UFunction* FindFunctionByStringName = 
	UReflectionTestClass::StaticClass()->FindFunctionByName(TEXT("TestFuction"), EIncludeSuperFlag::ExcludeSuper);

Note:
(1) The character string used in the search does not need to be prefixed with U/A.
(2) The searched classes and functions must have reflection marks

9. Get the name of the item in the enumeration

/** 查找枚举下的所有项
 * 注意:枚举会自动增加一个项:枚举名_MAX
 */
if(EnumByStringName)
{
    
    
	for(int32 i = 0; i < EnumByStringName->NumEnums(); i++)
	{
    
    
		FString FindEnumItemName = EnumByStringName->GetNameStringByIndex(i);
	}
}

10. Invoking the method of the class

ProcessEvent()Use the method of the calling class through reflection

/** 使用ProcessEvent调用类的成员方法 */
UReflectionTestClass* Object = NewObject<UReflectionTestClass>();
UFunction* Function = 
	UReflectionTestClass::StaticClass()->FindFunctionByName(TEXT("TestFuction"), EIncludeSuperFlag::ExcludeSuper);
if(Function)
{
    
    
	// 1. 给所有方法参数分配空间并初始化为0
	uint8* FunctionParamMemory = static_cast<uint8*>(FMemory_Alloca(Function->ParmsSize));
	FMemory::Memzero(FunctionParamMemory, Function->ParmsSize);
	// 2. 给所有方法参数赋值
	for(TFieldIterator<FProperty> IteratorOfParam(Function); IteratorOfParam; ++IteratorOfParam)
	{
    
    
		FProperty* Param = *IteratorOfParam;
		FString FunctionParamName = Param->GetName();
		// 通过参数名找到该传入参数
		if(FunctionParamName == FString("Input"))
		{
    
    
			// 设置传入参数的值
			*Param->ContainerPtrToValuePtr<FString>(FunctionParamMemory) = "One";
		}
	}
	// 3. 通过ProcessEvent调用方法
	Object->ProcessEvent(Function, FunctionParamMemory);
}

Invoke()Use the method of the calling class through reflection

/** 使用Invoke调用类的成员方法 */
UReflectionTestClass* Object = NewObject<UReflectionTestClass>();
UFunction* Function = 
	UReflectionTestClass::StaticClass()->FindFunctionByName(TEXT("TestFuction"), EIncludeSuperFlag::ExcludeSuper);
if(Function)
{
    
    
	// 1. 给所有方法参数分配空间并初始化为0
	uint8* FunctionParamMemory = static_cast<uint8*>(FMemory_Alloca(Function->ParmsSize));
	FMemory::Memzero(FunctionParamMemory, Function->ParmsSize);
	// 2. 创建一个Frame
	FFrame Frame(nullptr, Function, &FunctionParamMemory);
	// 3. 调用Invoke,第一个参数:调用方法的对象;第二个参数:Frame;第三个参数:为返回值,没有返回值就设为nullptr
	Function->Invoke(Object, Frame, nullptr);
}

11. Iterate over all classes

/** 遍历所有的类 */
for(TObjectIterator<UClass> ClassIterator; ClassIterator; ++ClassIterator)
{
    
    
	FString ClassName = ClassIterator->GetName();
}

Guess you like

Origin blog.csdn.net/qq_45617648/article/details/131786513