前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >UE4的枚举

UE4的枚举

作者头像
quabqi
发布2021-10-22 15:46:23
1.6K0
发布2021-10-22 15:46:23
举报
文章被收录于专栏:Dissecting UnrealDissecting Unreal

UE4在C++中和标准的枚举是一样的,支持两种写法,enum和enum class

/** Parameter enum for CastChecked() function, defines when it will check/assert */
namespace ECastCheckedType
{
	enum Type
	{
		/** Null is okay, only assert on incorrect type */
		NullAllowed,
		/** Null is not allowed, assert on incorrect type or null */
		NullChecked
	};
};

enum是在全局作用域的枚举,和宏,常量很相似,使用时不能加枚举名,可以认为就是一组常量把定义写在一起。如果需要指定作用域就必须和namespace配合使用,如上示例。无法定义类型的内存大小,也没有严格的类型检查,但在做位运算时很方便

/** Objects flags for internal use (GC, low level UObject code) */
enum class EInternalObjectFlags : int32
{
	None = 0,
	//~ All the other bits are reserved, DO NOT ADD NEW FLAGS HERE!

	ReachableInCluster = 1 << 23, ///< External reference to object in cluster exists
	ClusterRoot = 1 << 24, ///< Root of a cluster
	Native = 1 << 25, ///< Native (UClass only). 
	Async = 1 << 26, ///< Object exists only on a different thread than the game thread.
	AsyncLoading = 1 << 27, ///< Object is being asynchronously loaded.
	Unreachable = 1 << 28, ///< Object is not reachable on the object graph.
	PendingKill = 1 << 29, ///< Objects that are pending destruction (invalid for gameplay but valid objects)
	RootSet = 1 << 30, ///< Object will not be garbage collected, even if unreferenced.
	//~ UnusedFlag = 1 << 31,

	GarbageCollectionKeepFlags = Native | Async | AsyncLoading,

	//~ Make sure this is up to date!
	AllFlags = ReachableInCluster | ClusterRoot | Native | Async | AsyncLoading | Unreachable | PendingKill | RootSet
};
ENUM_CLASS_FLAGS(EInternalObjectFlags);

enum class是C++11新增的枚举,只在枚举作用域内有效,使用时需要在之前加枚举类型名和两个冒号。可以通过继承的方式指定内存占用长度,不指定时默认是int,使用规则和C#或java/C#的枚举很像,有严格的类型检查,做位运算需要先转换为底层类型(可通过std::underlying_type转换)再进行位运算

UE4为了解决enum class在做位运算时不方便的问题,提供了一个宏和一些模板函数

// Defines all bitwise operators for enum classes so it can be (mostly) used as a regular flags enum
#define ENUM_CLASS_FLAGS(Enum) \
	inline           Enum& operator|=(Enum& Lhs, Enum Rhs) { return Lhs = (Enum)((__underlying_type(Enum))Lhs | (__underlying_type(Enum))Rhs); } \
	inline           Enum& operator&=(Enum& Lhs, Enum Rhs) { return Lhs = (Enum)((__underlying_type(Enum))Lhs & (__underlying_type(Enum))Rhs); } \
	inline           Enum& operator^=(Enum& Lhs, Enum Rhs) { return Lhs = (Enum)((__underlying_type(Enum))Lhs ^ (__underlying_type(Enum))Rhs); } \
	inline constexpr Enum  operator| (Enum  Lhs, Enum Rhs) { return (Enum)((__underlying_type(Enum))Lhs | (__underlying_type(Enum))Rhs); } \
	inline constexpr Enum  operator& (Enum  Lhs, Enum Rhs) { return (Enum)((__underlying_type(Enum))Lhs & (__underlying_type(Enum))Rhs); } \
	inline constexpr Enum  operator^ (Enum  Lhs, Enum Rhs) { return (Enum)((__underlying_type(Enum))Lhs ^ (__underlying_type(Enum))Rhs); } \
	inline constexpr bool  operator! (Enum  E)             { return !(__underlying_type(Enum))E; } \
	inline constexpr Enum  operator~ (Enum  E)             { return (Enum)~(__underlying_type(Enum))E; }

// Friends all bitwise operators for enum classes so the definition can be kept private / protected.
#define FRIEND_ENUM_CLASS_FLAGS(Enum) \
	friend           Enum& operator|=(Enum& Lhs, Enum Rhs); \
	friend           Enum& operator&=(Enum& Lhs, Enum Rhs); \
	friend           Enum& operator^=(Enum& Lhs, Enum Rhs); \
	friend constexpr Enum  operator| (Enum  Lhs, Enum Rhs); \
	friend constexpr Enum  operator& (Enum  Lhs, Enum Rhs); \
	friend constexpr Enum  operator^ (Enum  Lhs, Enum Rhs); \
	friend constexpr bool  operator! (Enum  E); \
	friend constexpr Enum  operator~ (Enum  E);

template<typename Enum>
constexpr bool EnumHasAllFlags(Enum Flags, Enum Contains)
{
	return ( ( ( __underlying_type(Enum) )Flags ) & ( __underlying_type(Enum) )Contains ) == ( ( __underlying_type(Enum) )Contains );
}

template<typename Enum>
constexpr bool EnumHasAnyFlags(Enum Flags, Enum Contains)
{
	return ( ( ( __underlying_type(Enum) )Flags ) & ( __underlying_type(Enum) )Contains ) != 0;
}

template<typename Enum>
void EnumAddFlags(Enum& Flags, Enum FlagsToAdd)
{
	Flags |= FlagsToAdd;
}

template<typename Enum>
void EnumRemoveFlags(Enum& Flags, Enum FlagsToRemove)
{
	Flags &= ~FlagsToRemove;
}

可以看到,宏中声明了各种位运算的运算符重载函数,只要定义enum class时候再额外使用这个宏来声明枚举类型,枚举就自动支持了位运算功能

除此之外,还提供了枚举范围查询,迭代器等相关的辅助宏和模板

/**
 * Defines a contiguous enum range containing Count values, starting from zero:
 *
 * Example:
 *
 * enum class ECountedThing
 * {
 *     First,
 *     Second,
 *     Third,
 *
 *     Count
 * };
 *
 * // Defines iteration over ECountedThing to be: First, Second, Third
 * ENUM_RANGE_BY_COUNT(ECountedThing, ECountedThing::Count)
 */
#define ENUM_RANGE_BY_COUNT(EnumType, Count) ENUM_RANGE_BY_FIRST_AND_LAST(EnumType, 0, (__underlying_type(EnumType))(Count) - 1)


/**
 * Defines a contiguous enum range with specific start and end values:
 *
 * Example:
 *
 * enum class EDoubleEndedThing
 * {
 *     Invalid,
 *
 *     First,
 *     Second,
 *     Third,
 *
 *     Count
 * };
 *
 * // Defines iteration over EDoubleEndedThing to be: First, Second, Third
 * ENUM_RANGE_BY_FIRST_AND_LAST(EDoubleEndedThing, EDoubleEndedThing::First, EDoubleEndedThing::Third)
 */
#define ENUM_RANGE_BY_FIRST_AND_LAST(EnumType, First, Last) \
	template <> \
	struct NEnumRangePrivate::TEnumRangeTraits<EnumType> \
	{ \
		enum { RangeType = 0 }; \
		static const __underlying_type(EnumType) Begin = (__underlying_type(EnumType))(First); \
		static const __underlying_type(EnumType) End   = (__underlying_type(EnumType))(Last) + 1; \
	};


/**
 * Defines a non-contiguous enum range with specific individual values:
 *
 * Example:
 *
 * enum class ERandomValuesThing
 * {
 *     First  = 2,
 *     Second = 3,
 *     Third  = 5,
 *     Fourth = 7,
 *     Fifth  = 11
 * };
 *
 * // Defines iteration over ERandomValuesThing to be: First, Second, Third, Fourth, Fifth
 * ENUM_RANGE_BY_VALUES(ERandomValuesThing, ERandomValuesThing::First, ERandomValuesThing::Second, ERandomValuesThing::Third, ERandomValuesThing::Fourth, ERandomValuesThing::Fifth)
 */
#define ENUM_RANGE_BY_VALUES(EnumType, ...) \
	template <> \
	struct NEnumRangePrivate::TEnumRangeTraits<EnumType> \
	{ \
		enum { RangeType = 1 }; \
		template <typename Dummy> \
		static const EnumType* GetPointer(bool bLast) \
		{ \
			static const EnumType Values[] = { __VA_ARGS__ }; \
			return bLast ? Values + sizeof(Values) / sizeof(EnumType) : Values; \
		} \
	};


namespace NEnumRangePrivate
{
	template <typename EnumType>
	struct TEnumRangeTraits
	{
		enum { RangeType = -1 };
	};

	template <typename EnumType, int32 RangeType>
	struct TEnumRange_Impl
	{
		static_assert(sizeof(EnumType) == 0, "Unknown enum type - use one of the ENUM_RANGE macros to define iteration semantics for your enum type.");
	};

	template <typename EnumType>
	struct TEnumContiguousIterator
	{
		typedef __underlying_type(EnumType) IntType;

		FORCEINLINE explicit TEnumContiguousIterator(IntType InValue)
			: Value(InValue)
		{
		}

		FORCEINLINE TEnumContiguousIterator& operator++()
		{
			++Value;
			return *this;
		}

		FORCEINLINE EnumType operator*() const
		{
			return (EnumType)Value;
		}

	private:
		IntType Value;

		FORCEINLINE friend bool operator!=(const TEnumContiguousIterator& Lhs, const TEnumContiguousIterator& Rhs)
		{
			return Lhs.Value != Rhs.Value;
		}
	};

	template <typename EnumType>
	struct TEnumValueArrayIterator
	{
		FORCEINLINE explicit TEnumValueArrayIterator(const EnumType* InPtr)
			: Ptr(InPtr)
		{
		}

		FORCEINLINE TEnumValueArrayIterator& operator++()
		{
			++Ptr;
			return *this;
		}

		FORCEINLINE EnumType operator*() const
		{
			return *Ptr;
		}

	private:
		const EnumType* Ptr;

		FORCEINLINE friend bool operator!=(const TEnumValueArrayIterator& Lhs, const TEnumValueArrayIterator& Rhs)
		{
			return Lhs.Ptr != Rhs.Ptr;
		}
	};

	template <typename EnumType>
	struct TEnumRange_Impl<EnumType, 0>
	{
		TEnumContiguousIterator<EnumType> begin() const { return TEnumContiguousIterator<EnumType>(TEnumRangeTraits<EnumType>::Begin); }
		TEnumContiguousIterator<EnumType> end()   const { return TEnumContiguousIterator<EnumType>(TEnumRangeTraits<EnumType>::End  ); }
	};

	template <typename EnumType>
	struct TEnumRange_Impl<EnumType, 1>
	{
		TEnumValueArrayIterator<EnumType> begin() const { return TEnumValueArrayIterator<EnumType>(TEnumRangeTraits<EnumType>::template GetPointer<void>(false)); }
		TEnumValueArrayIterator<EnumType> end  () const { return TEnumValueArrayIterator<EnumType>(TEnumRangeTraits<EnumType>::template GetPointer<void>(true )); }
	};
}

/**
 * Range type for iterating over enum values.  Enums should define themselves as iterable by specifying
 * one of the ENUM_RANGE_* macros.
 *
 * Example:
 *
 * for (ECountedThing Val : TEnumRange<ECountedThing>())
 * {
 *     ...
 * }
 **/
template <typename EnumType>
struct TEnumRange : NEnumRangePrivate::TEnumRange_Impl<EnumType, NEnumRangePrivate::TEnumRangeTraits<EnumType>::RangeType>
{
};

如上所示,通过这种写法声明后可以直接支持for-each语法遍历。

如果枚举不想只在C++中使用,还想暴露给蓝图使用,那么可以通过在枚举定义之前使用UENUM宏,在枚举定义内部使用UMETA来声明,这样UHT会在编译时自动生成辅助代码,运行时会创建一个UEnum的UObject对象,将枚举自动注册到了虚拟机中。如下图所示

上面这种方式定义的枚举或在蓝图中定义的枚举,还可以通过反射的方式在运行时按名查找,遍历,取值,转换为字符串等操作。比如下面的示例代码,会将传入的枚举名中的所有字符串以key-value的方式存入Map中

有一点额外要注意的是,FindObject查找枚举类型的对象,只能在游戏线程运行,在其他线程执行会因为虚拟机状态无法保证会有崩溃,写坏内存等不确定的情况出现

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档