目录
- [0 引言](#0 引言)
- [1 C++如何实现反射机制](#1 C++如何实现反射机制)
-
- [1.1 使用代码生成工具实现反射机制](#1.1 使用代码生成工具实现反射机制)
- [2 UE4的反射系统](#2 UE4的反射系统)
-
- [2.1 ****.generated.h头文件](#2.1 ****.generated.h头文件)
- [2.2 GENERATED_BODY()](#2.2 GENERATED_BODY())
- [2.3 反射宏 UCLASS 等](#2.3 反射宏 UCLASS 等)
- [2.4 UHT和UBT](#2.4 UHT和UBT)
- [3 基本宏的使用](#3 基本宏的使用)
-
- [3.1 UCLASS](#3.1 UCLASS)
- [3.2 UFUNCTION](#3.2 UFUNCTION)
- [3.3 UPROPERTY](#3.3 UPROPERTY)
0 引言
- 反射系统 是一种允许程序在运行时 获取类型信息 和动态操作类和对象 的机制。在C++语言中,没有内置的反射系统,但可以使用不同的库和技术实现类似的功能。(Java和C#语言都是有内置的反射机制的)
- UE4在C++的基础上搭建了自己的一套反射机制,使用自定义的反射系统来支持蓝图编程和编辑器功能 。UE4的反射系统允许在运行时访问类和对象的属性、函数和元数据,并提供了一些宏和关键字来标识和定义可被反射系统处理的内容 。其中,"UCLASS "、"UFUNCTION "和"UPROPERTY"是UE4中用于反射的一些重要宏。
- UE4的反射系统是选择加入的,只有主动标记的类型、属性、方法会被反射系统追踪,UnrealHeaderTool(UHT)会收集这些信息,生成用于支持反射机制的C++代码,然后再编译工程。
1 C++如何实现反射机制
实现C++的反射机制有多种方式。下面详细介绍其中几种常见的方法:
- 手动定义转换函数 :这是最基础的反射实现方式。在类的定义中,手动定义转换函数,用于将类的数据成员或方法映射到字符串或其他类型。例如,可以为每个类定义一个静态成员函数,接受类的实例作为参数,并返回包含类信息的结构体。这种方法需要手动编写大量的重复代码,不够灵活。
- 使用宏 :宏是C++中实现代码生成的一种方式。通过定义一系列宏来模拟反射机制。可以使用宏定义类的元信息,例如类名、成员变量名、函数名等。通过宏展开,可以在运行时获取类的元信息,并执行对应的操作。这种方法可以减少手动编写的代码量,但仍然需要繁琐的宏定义。
- 使用代码生成工具 :可以编写一个代码生成工具,根据特定的标记或注释解析源代码,并生成反射所需的代码。工具可以根据类定义自动生成访问元信息的代码,避免手动编写大量重复的代码。通过将生成工具作为构建系统的一部分,可以在每次构建时自动更新反射代码。这种方法相对灵活且易于维护 ,但需要编写自定义的代码生成工具。(QT和UE4就是采用的该方法实现反射机制)
- 使用第三方库:也可以使用现有的第三方库来实现C++反射。例如,"Boost.Reflection"库提供了一套用于C++的反射功能。它可以动态地获取和操作类的成员变量、函数和类型信息。这样,开发者可以在运行时获得类的元信息,从而实现动态加载和操作类的功能。
需要注意的是,C++作为静态类型语言,没有内置的反射机制。因此,以上这些方法只是模拟了反射的部分功能,无法像动态类型语言一样灵活。
总结起来,实现C++的反射机制可以通过手动定义转换函数、使用宏、编写代码生成工具或使用第三方库等方式。每种方式都有其优缺点,具体选择取决于项目需求和开发者的偏好。
1.1 使用代码生成工具实现反射机制
- 代码生成工具 通常是在构建过程中的一个独立阶段,通过解析源代码来生成反射所需的代码。
简单的实现步骤:
- 定义注释或标记 :在源代码中定义一些注释或特定的标记,用于标识需要反射的类、成员变量和方法 。
例如,可以使用REFLECT_CLASS、REFLECT_MEMBER和REFLECT_METHOD等宏 来注释类、成员变量和方法。- 编写代码生成工具 :编写一个代码生成工具,它可以读取源代码文件,并解析这些注释或标记 。
例如,可以使用编译器提供的编译器前端库,如Clang的LibTooling,来辅助解析源代码。- 解析源代码 :使用代码生成工具解析源代码文件,并提取类、成员变量和方法的相关信息 。
例如,可以获取类名 、成员变量的名称 、类型 和访问修饰符,以及方法的名称、参数列表和返回类型等- 生成反射代码 :根据解析得到的信息,代码生成工具可以生成反射所需的代码。
例如,可以生成用于获取类的元信息的结构体或类 ,以及用于动态访问成员变量和调用方法的函数。生成的代码可以包括类注册、获取类信息、访问成员变量、调用方法等功能。- 构建生成工具 :将代码生成工具集成到项目的构建过程中 。
例如,使用Makefile、CMake或其他构建系统。确保代码生成工具在每次构建时自动运行,以根据最新的源代码生成反射代码。- 使用反射功能 :在运行时 ,可以使用生成的反射代码动态地获取和操作类的成员变量和方法 。
根据类的元信息,可以在不知道类的实际名称的情况下,动态创建对象、获取和修改成员变量的值,以及调用方法。
2 UE4的反射系统
在了解了C++中如何使用代码生成工具实现反射机制之后,那么再来理解 UE4 反射系统就会容易许多。
先从最简单的一个UObject子类开始分析(因为日常使用的类基本上都继承UObject),我们新建一个C++类继承UObject,UE会自动生成许多反射的代码:
cpp
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "MyObject1.generated.h"
/**
*
*/
UCLASS()
class CPP_CLASS_10_15_API UMyObject1 : public UObject
{
GENERATED_BODY()
public:
UFUNCTION()
void fun1()
{
UE_LOG(LogTemp, Warning, TEXT("name = %s"), *name);
}
UPROPERTY()
FString name = "007";
};
2.1 ****.generated.h头文件
- generate.h,其实就是UE对我们自己写的.h文件产生的反射代码文件。
- #include "MyObject1.generated.h"需要写在头文件包含的最后。不然编译时会报错。错误如下:
error : #include found after .generated.h file - the .generated.h file should always be the last #include in a header
错误:#include在.generated.h文件之后找到-.generated.h文件应该始终是头中的最后一个#include
2.2 GENERATED_BODY()
使用GENERATED_BODY()宏自动展开为通用的成员函数接口和数据成员
2.3 反射宏 UCLASS 等
可以看到上面出现了三个宏,这些都是反射宏,下图中是UE源码中对宏的定义
反射宏名称 | 作用 |
---|---|
UCLASS | 告诉UE这个类是个反射类。此类必须派生自UObject(如果不用这个宏,那么成员函数和成员变量加了宏也反射不了) |
UFUNCTION | 定义一个反射的函数 |
UPROPERTY | 定义一个反射的变量 |
USTRUCT | 可以不用派生自UObject。不支持GC,也不能包含函数 |
UENUM | 告诉UE这是一个反射的枚举类。支持enum, enum class, enum namespace |
UINTERFACE | 定义一个反射接口类,只能包含函数 |
UMETA | 反射的一些元数据定义,可以通过标签定义一些该变量的属性 |
UPARAM | 定义函数的参数属性。主要就是显示名字和Ref属性 |
UDELEGATE | 告诉UE这是一个可反射的委托(很少用到) |
2.4 UHT和UBT
由于作者目前水平有限,理解还不透彻。等学习领悟之后再来填坑。
这里放一个 参考文章 帮助大家理解
3 基本宏的使用
3.1 UCLASS
- 如果想要使用蓝图生成C++类,则需要给UCLASS宏中添加说明符,UCLASS宏的所有说明符如下所示:
- 最常用的就是 BlueprintType(将此类公开为可用于蓝图中变量的类型)
cpp
// These are used for syntax highlighting and to allow autocomplete hints
namespace UC
{
// valid keywords for the UCLASS macro
enum
{
/// This keyword is used to set the actor group that the class is show in, in the editor.
classGroup,
/// Declares that instances of this class should always have an outer of the specified class. This is inherited by subclasses unless overridden.
Within, /* =OuterClassName */
/// Exposes this class as a type that can be used for variables in blueprints
BlueprintType,
/// Prevents this class from being used for variables in blueprints
NotBlueprintType,
/// Exposes this class as an acceptable base class for creating blueprints. The default is NotBlueprintable, unless inherited otherwise. This is inherited by subclasses.
Blueprintable,
/// Specifies that this class is *NOT* an acceptable base class for creating blueprints. The default is NotBlueprintable, unless inherited otherwise. This is inherited by subclasses.
NotBlueprintable,
/// This keyword indicates that the class should be accessible outside of it's module, but does not need all methods exported.
/// It exports only the autogenerated methods required for dynamic_cast<>, etc... to work.
MinimalAPI,
/// Prevents automatic generation of the constructor declaration.
customConstructor,
/// Class was declared directly in C++ and has no boilerplate generated by UnrealHeaderTool.
/// DO NOT USE THIS FLAG ON NEW CLASSES.
Intrinsic,
/// No autogenerated code will be created for this class; the header is only provided to parse metadata from.
/// DO NOT USE THIS FLAG ON NEW CLASSES.
noexport,
/// Allow users to create and place this class in the editor. This flag is inherited by subclasses.
placeable,
/// This class cannot be placed in the editor (it cancels out an inherited placeable flag).
notplaceable,
/// All instances of this class are considered "instanced". Instanced classes (components) are duplicated upon construction. This flag is inherited by subclasses.
DefaultToInstanced,
/// All properties and functions in this class are const and should be exported as const. This flag is inherited by subclasses.
Const,
/// Class is abstract and can't be instantiated directly.
Abstract,
/// This class is deprecated and objects of this class won't be saved when serializing. This flag is inherited by subclasses.
deprecated,
/// This class can't be saved; null it out at save time. This flag is inherited by subclasses.
Transient,
/// This class should be saved normally (it cancels out an inherited transient flag).
nonTransient,
/// This class is optional and might not be available in certain context. reference from non optional data type is not allowed.
Optional,
/// Load object configuration at construction time. These flags are inherited by subclasses.
/// Class containing config properties. Usage config=ConfigName or config=inherit (inherits config name from base class).
config,
/// Handle object configuration on a per-object basis, rather than per-class.
perObjectConfig,
/// Determine whether on serialize to configs a check should be done on the base/defaults ini's
configdonotcheckdefaults,
/// Save object config only to Default INIs, never to local INIs.
defaultconfig,
/// Mark the editor config file to load from if loading into this object.
EditorConfig,
/// These affect the behavior of the property editor.
/// Class can be constructed from editinline New button.
editinlinenew,
/// Class can't be constructed from editinline New button.
noteditinlinenew,
/// Class not shown in editor drop down for class selection.
hidedropdown,
/// Shows the specified categories in a property viewer. Usage: showCategories=CategoryName or showCategories=(category0, category1, ...)
showCategories,
/// Hides the specified categories in a property viewer. Usage: hideCategories=CategoryName or hideCategories=(category0, category1, ...)
hideCategories,
/// Indicates that this class is a wrapper class for a component with little intrinsic functionality (this causes things like hideCategories and showCategories to be ignored if the class is subclassed in a Blueprint)
ComponentWrapperClass,
/// Shows the specified function in a property viewer. Usage: showFunctions=FunctionName or showFunctions=(category0, category1, ...)
showFunctions,
/// Hides the specified function in a property viewer. Usage: hideFunctions=FunctionName or hideFunctions=(category0, category1, ...)
hideFunctions,
/// Specifies which categories should be automatically expanded in a property viewer.
autoExpandCategories,
/// Specifies which categories should be automatically collapsed in a property viewer.
autoCollapseCategories,
/// Clears the list of auto collapse categories.
dontAutoCollapseCategories,
/// Display properties in the editor without using categories.
collapseCategories,
/// Display properties in the editor using categories (default behaviour).
dontCollapseCategories,
/// Specifies category display order, unspecified will follow default display order.
prioritizeCategories,
/// All the properties of the class are hidden in the main display by default, and are only shown in the advanced details section.
AdvancedClassDisplay,
/// A root convert limits a sub-class to only be able to convert to child classes of the first root class going up the hierarchy.
ConversionRoot,
/// Marks this class as 'experimental' (a totally unsupported and undocumented prototype)
Experimental,
/// Marks this class as an 'early access' preview (while not considered production-ready, it's a step beyond 'experimental' and is being provided as a preview of things to come)
EarlyAccessPreview,
/// Some properties are stored once per class in a sidecar structure and not on instances of the class
SparseClassDataType,
/// Specifies the struct that contains the CustomThunk implementations
CustomThunkTemplates
};
}
3.2 UFUNCTION
- 最常用的函数说明符就是 BlueprintCallable(此函数可在蓝图或关卡蓝图图表中执行)
cpp
namespace UF
{
// valid keywords for the UFUNCTION and UDELEGATE macros
enum
{
/// This function is designed to be overridden by a blueprint. Do not provide a body for this function;
/// the autogenerated code will include a thunk that calls ProcessEvent to execute the overridden body.
BlueprintImplementableEvent,
/// This function is designed to be overridden by a blueprint, but also has a native implementation.
/// Provide a body named [FunctionName]_Implementation instead of [FunctionName]; the autogenerated
/// code will include a thunk that calls the implementation method when necessary.
BlueprintNativeEvent,
/// This function is sealed and cannot be overridden in subclasses.
/// It is only a valid keyword for events; declare other methods as static or final to indicate that they are sealed.
SealedEvent,
/// This function is executable from the command line.
Exec,
/// This function is replicated, and executed on servers. Provide a body named [FunctionName]_Implementation instead of [FunctionName];
/// the autogenerated code will include a thunk that calls the implementation method when necessary.
Server,
/// This function is replicated, and executed on clients. Provide a body named [FunctionName]_Implementation instead of [FunctionName];
/// the autogenerated code will include a thunk that calls the implementation method when necessary.
Client,
/// This function is both executed locally on the server and replicated to all clients, regardless of the Actor's NetOwner
NetMulticast,
/// Replication of calls to this function should be done on a reliable channel.
/// Only valid when used in conjunction with Client or Server
Reliable,
/// Replication of calls to this function can be done on an unreliable channel.
/// Only valid when used in conjunction with Client or Server
Unreliable,
/// This function fulfills a contract of producing no side effects, and additionally implies BlueprintCallable.
BlueprintPure,
/// This function can be called from blueprint code and should be exposed to the user of blueprint editing tools.
BlueprintCallable,
/// This function is used as the get accessor for a blueprint exposed property. Implies BlueprintPure and BlueprintCallable.
BlueprintGetter,
/// This function is used as the set accessor for a blueprint exposed property. Implies BlueprintCallable.
BlueprintSetter,
/// This function will not execute from blueprint code if running on something without network authority
BlueprintAuthorityOnly,
/// This function is cosmetic and will not run on dedicated servers
BlueprintCosmetic,
/// Indicates that a Blueprint exposed function should not be exposed to the end user
BlueprintInternalUseOnly,
/// This function can be called in the editor on selected instances via a button in the details panel.
CallInEditor,
/// The UnrealHeaderTool code generator will not produce a execFoo thunk for this function; it is up to the user to provide one.
CustomThunk,
/// Specifies the category of the function when displayed in blueprint editing tools.
/// Usage: Category=CategoryName or Category="MajorCategory,SubCategory"
Category,
/// This function must supply a _Validate implementation
WithValidation,
/// This function is RPC service request
ServiceRequest,
/// This function is RPC service response
ServiceResponse,
/// [FunctionMetadata] Marks a UFUNCTION as accepting variadic arguments. Variadic functions may have extra terms they need to emit after the main set of function arguments
/// These are all considered wildcards so no type checking will be performed on them
Variadic,
/// [FunctionMetadata] Indicates the display name of the return value pin
ReturnDisplayName,
/// [FunctionMetadata] Indicates that a particular function parameter is for internal use only, which means it will be both hidden and not connectible.
InternalUseParam,
/// [FunctionMetadata] Indicates that the function should be ignored when considered for blueprint type promotion
IgnoreTypePromotion,
};
}
3.3 UPROPERTY
- BlueprintReadWrite 可从蓝图读取或写入此属性。此说明符与 BlueprintReadOnly 说明符不兼容
cpp
namespace UP
{
// valid keywords for the UPROPERTY macro
enum
{
/// This property is const and should be exported as const.
Const,
/// Property should be loaded/saved to ini file as permanent profile.
Config,
/// Same as above but load config from base class, not subclass.
GlobalConfig,
/// Property should be loaded as localizable text. Implies ReadOnly.
Localized,
/// Property is transient: shouldn't be saved, zero-filled at load time.
Transient,
/// Property should always be reset to the default value during any type of duplication (copy/paste, binary duplication, etc.)
DuplicateTransient,
/// Property should always be reset to the default value unless it's being duplicated for a PIE session - deprecated, use NonPIEDuplicateTransient instead
NonPIETransient,
/// Property should always be reset to the default value unless it's being duplicated for a PIE session
NonPIEDuplicateTransient,
/// Value is copied out after function call. Only valid on function param declaration.
Ref,
/// Object property can be exported with it's owner.
Export,
/// Hide clear (and browse) button in the editor.
NoClear,
/// Indicates that elements of an array can be modified, but its size cannot be changed.
EditFixedSize,
/// Property is relevant to network replication.
Replicated,
/// Property is relevant to network replication. Notify actors when a property is replicated (usage: ReplicatedUsing=FunctionName).
ReplicatedUsing,
/// Skip replication (only for struct members and parameters in service request functions).
NotReplicated,
/// Interpolatable property for use with cinematics. Always user-settable in the editor.
Interp,
/// Property isn't transacted.
NonTransactional,
/// Property is a component reference. Implies EditInline and Export.
Instanced,
/// MC Delegates only. Property should be exposed for assigning in blueprints.
BlueprintAssignable,
/// Specifies the category of the property. Usage: Category=CategoryName.
Category,
/// Properties appear visible by default in a details panel
SimpleDisplay,
/// Properties are in the advanced dropdown in a details panel
AdvancedDisplay,
/// Indicates that this property can be edited by property windows in the editor
EditAnywhere,
/// Indicates that this property can be edited by property windows, but only on instances, not on archetypes
EditInstanceOnly,
/// Indicates that this property can be edited by property windows, but only on archetypes
EditDefaultsOnly,
/// Indicates that this property is visible in property windows, but cannot be edited at all
VisibleAnywhere,
/// Indicates that this property is only visible in property windows for instances, not for archetypes, and cannot be edited
VisibleInstanceOnly,
/// Indicates that this property is only visible in property windows for archetypes, and cannot be edited
VisibleDefaultsOnly,
/// This property can be read by blueprints, but not modified.
BlueprintReadOnly,
/// This property has an accessor to return the value. Implies BlueprintReadOnly if BlueprintSetter or BlueprintReadWrite is not specified. (usage: BlueprintGetter=FunctionName).
BlueprintGetter,
/// This property can be read or written from a blueprint.
BlueprintReadWrite,
/// This property has an accessor to set the value. Implies BlueprintReadWrite. (usage: BlueprintSetter=FunctionName).
BlueprintSetter,
/// The AssetRegistrySearchable keyword indicates that this property and it's value will be automatically added
/// to the asset registry for any asset class instances containing this as a member variable. It is not legal
/// to use on struct properties or parameters.
AssetRegistrySearchable,
/// Property should be serialized for save games.
/// This is only checked for game-specific archives with ArIsSaveGame set
SaveGame,
/// MC Delegates only. Property should be exposed for calling in blueprint code
BlueprintCallable,
/// MC Delegates only. This delegate accepts (only in blueprint) only events with BlueprintAuthorityOnly.
BlueprintAuthorityOnly,
/// Property shouldn't be exported to text format (e.g. copy/paste)
TextExportTransient,
/// Property shouldn't be serialized, can still be exported to text
SkipSerialization,
/// If true, the self pin should not be shown or connectable regardless of purity, const, etc. similar to InternalUseParam
HideSelfPin,
};
}