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查找枚举类型的对象,只能在游戏线程运行,在其他线程执行会因为虚拟机状态无法保证会有崩溃,写坏内存等不确定的情况出现