我曾经是一名C++程序员,对我来说宏是一个预处理器定义使用#define
.
现在我回到使用C++的Unreal引擎进行编程,但是所有这些宏 UCLASS()
UFUNCTION()
FORCELINE
都是UNreal教程调用的宏.我以前从未见过这样的东西,想要了解它.
我不是在询问宏在虚幻中做了什么,但是有人帮助我用C++填补我的知识空白,所以我(作为开发人员)可以理解如何设计以及何时实现这种类型的宏.即使给我链接指南或其他东西也没关系.我尝试使用术语宏,C++,UCLass,Unreal进行搜索,但这些术语并没有真正提出这个宏的C++定义.
#include "GameFramework/Actor.h" #include "Pickup.generated.h" UCLASS() class BATTERYCOLLECTOR_API APickup : public AActor { GENERATED_BODY() public: // Sets default values for this actor's properties APickup(); // Called when the game starts or when spawned virtual void BeginPlay() override;
glampert.. 11
虚幻引擎C++代码库使用一个名为虚幻头文件工具(UHT)的自定义预处理器,从您的C++代码生成自定义运行时类型信息(RTTI).它通过在代码中查找这些特殊的类似宏的注释来实现这一目的.迂腐地说,它们不是简单的C++宏,它们不仅仅是那些.
我不是虚幻引擎的用户,因此我不了解实现细节.我不知道UHT预处理器是否在运行后将它们剥离出来,或者它们可能只是为C++编译器定义为什么,例如:
#if !RUNNING_UHT #define UCLASS() #endif
两种方法似乎都有效.
这是正常的C++吗?
这取决于.它当然不是标准 C++.宏在C++中使用较少,因为在大多数情况下语言提供了更好的选项(例如:内联函数,枚举const
,模板),因此在大多数代码库中你可能不会看到这种宏用法.
虚幻引擎C++代码库使用一个名为虚幻头文件工具(UHT)的自定义预处理器,从您的C++代码生成自定义运行时类型信息(RTTI).它通过在代码中查找这些特殊的类似宏的注释来实现这一目的.迂腐地说,它们不是简单的C++宏,它们不仅仅是那些.
我不是虚幻引擎的用户,因此我不了解实现细节.我不知道UHT预处理器是否在运行后将它们剥离出来,或者它们可能只是为C++编译器定义为什么,例如:
#if !RUNNING_UHT #define UCLASS() #endif
两种方法似乎都有效.
这是正常的C++吗?
这取决于.它当然不是标准 C++.宏在C++中使用较少,因为在大多数情况下语言提供了更好的选项(例如:内联函数,枚举const
,模板),因此在大多数代码库中你可能不会看到这种宏用法.
正如虚幻引擎文档所说:
类声明定义了类的名称,它继承的类,以及它继承的任何函数和变量,以及通过类说明符和元数据可能需要的其他引擎和编辑器特定行为.声明类的语法如下:
UCLASS([specifier, specifier, ...], [meta(key=value, key=value, ...)]) class ClassName : ParentName { GENERATED_UCLASS_BODY() }
声明包含该类的标准C++类声明.在标准声明之上,诸如类说明符和元数据之类的描述符将传递给UCLASS宏.这些用于为声明的类创建UClass,可以将其视为引擎的类的专用表示.此外,GENERATED_UCLASS_BODY()
宏必须放在类主体的最开头.
创建此宏是为了向引擎类添加一些元数据功能,如RTTI,Reflection等.这个宏不是C++ STD库提供的标准宏,而是由Epic Games编写的宏,以满足虚幻引擎的需要.
有关虚幻引擎游戏性类和它们使用的宏的更多信息.
以下是虚幻引擎为简单游戏类所取代的一些宏代码示例:
// Copyright 1998-2015 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" #ifdef MYPROJECTCODE_MyProjectCodeGameMode_generated_h #error "MyProjectCodeGameMode.generated.h already included, missing '#pragma once' in MyProjectCodeGameMode.h" #endif #define MYPROJECTCODE_MyProjectCodeGameMode_generated_h #define AMyProjectCodeGameMode_EVENTPARMS #define AMyProjectCodeGameMode_RPC_WRAPPERS #define AMyProjectCodeGameMode_RPC_WRAPPERS_NO_PURE_DECLS \ static inline void StaticChecks_Implementation_Validate() \ { \ } #define AMyProjectCodeGameMode_CALLBACK_WRAPPERS #define AMyProjectCodeGameMode_INCLASS_NO_PURE_DECLS \ private: \ static void StaticRegisterNativesAMyProjectCodeGameMode(); \ friend MYPROJECTCODE_API class UClass* Z_Construct_UClass_AMyProjectCodeGameMode(); \ public: \ DECLARE_CLASS(AMyProjectCodeGameMode, AGameMode, COMPILED_IN_FLAGS(0 | CLASS_Transient | CLASS_Config), 0, MyProjectCode, MYPROJECTCODE_API) \ DECLARE_SERIALIZER(AMyProjectCodeGameMode) \ /** Indicates whether the class is compiled into the engine */ enum {IsIntrinsic=COMPILED_IN_INTRINSIC}; \ UObject* _getUObject() const { return const_cast(this); } #define AMyProjectCodeGameMode_INCLASS \ private: \ static void StaticRegisterNativesAMyProjectCodeGameMode(); \ friend MYPROJECTCODE_API class UClass* Z_Construct_UClass_AMyProjectCodeGameMode(); \ public: \ DECLARE_CLASS(AMyProjectCodeGameMode, AGameMode, COMPILED_IN_FLAGS(0 | CLASS_Transient | CLASS_Config), 0, MyProjectCode, MYPROJECTCODE_API) \ DECLARE_SERIALIZER(AMyProjectCodeGameMode) \ /** Indicates whether the class is compiled into the engine */ enum {IsIntrinsic=COMPILED_IN_INTRINSIC}; \ UObject* _getUObject() const { return const_cast (this); } #define AMyProjectCodeGameMode_STANDARD_CONSTRUCTORS \ /** Standard constructor, called after all reflected properties have been initialized */ \ MYPROJECTCODE_API AMyProjectCodeGameMode(const FObjectInitializer& ObjectInitializer); \ DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(AMyProjectCodeGameMode) \ private: \ /** Private copy-constructor, should never be used */ \ MYPROJECTCODE_API AMyProjectCodeGameMode(const AMyProjectCodeGameMode& InCopy); \ public: #define AMyProjectCodeGameMode_ENHANCED_CONSTRUCTORS \ private: \ /** Private copy-constructor, should never be used */ \ MYPROJECTCODE_API AMyProjectCodeGameMode(const AMyProjectCodeGameMode& InCopy); \ public: \ DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(AMyProjectCodeGameMode) #undef UCLASS_CURRENT_FILE_NAME #define UCLASS_CURRENT_FILE_NAME AMyProjectCodeGameMode #undef UCLASS #undef UINTERFACE #if UE_BUILD_DOCS #define UCLASS(...) #else #define UCLASS(...) \ AMyProjectCodeGameMode_EVENTPARMS #endif #undef GENERATED_UCLASS_BODY #undef GENERATED_BODY #undef GENERATED_IINTERFACE_BODY #define GENERATED_UCLASS_BODY() \ PRAGMA_DISABLE_DEPRECATION_WARNINGS \ public: \ AMyProjectCodeGameMode_RPC_WRAPPERS \ AMyProjectCodeGameMode_CALLBACK_WRAPPERS \ AMyProjectCodeGameMode_INCLASS \ AMyProjectCodeGameMode_STANDARD_CONSTRUCTORS \ public: \ PRAGMA_POP #define GENERATED_BODY() \ PRAGMA_DISABLE_DEPRECATION_WARNINGS \ public: \ AMyProjectCodeGameMode_RPC_WRAPPERS_NO_PURE_DECLS \ AMyProjectCodeGameMode_CALLBACK_WRAPPERS \ AMyProjectCodeGameMode_INCLASS_NO_PURE_DECLS \ AMyProjectCodeGameMode_ENHANCED_CONSTRUCTORS \ private: \ PRAGMA_POP
和它的课程.
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. #pragma once #include "GameFramework/GameMode.h" #include "MyProjectCodeGameMode.generated.h" UCLASS(minimalapi) class AMyProjectCodeGameMode : public AGameMode { GENERATED_BODY() public: AMyProjectCodeGameMode(const FObjectInitializer& ObjectInitializer); };
正如您所看到的,此宏为类添加了额外的字段变量和方法,以便为虚幻引擎元数据信息提供接口.因此引擎可以使用此信息来构建关卡,生成玩家等.
引擎定义的宏不能被重写,如果您想设计自己的引擎框架来添加一些新功能(使用新的宏保护),或者修改当前的虚幻引擎以满足您的特定需求(这是一项非常艰苦的工作).