|
#pragma once |
|
|
|
//------------------------------------------------------------------------------------------------- |
|
// GC_Enum is a way to declare a typesafe enum that can be serialized to and from a string without |
|
// any dynamic allocation overhead. The one big restriction with this is that it only works with sequential |
|
// values, however they can start at any value. |
|
// |
|
// Reserved words: Count, Start, End, Default |
|
// |
|
// Declare enum using one of the macros: GC_DECLARE_ENUM(Name, Value1, Value2, Value3) |
|
// |
|
// Then use it like you would any strongly types enum: |
|
// Name anEnum = Name::Value1; |
|
// |
|
// Now you get fun features such age |
|
// anEnum.ToString() |
|
// anEnum = Name::FromString("Value1"); |
|
// Name::Start and Name::End |
|
//------------------------------------------------------------------------------------------------- |
|
|
|
typedef GC_StaticString<32> GC_EnumString; |
|
|
|
//------------------------------------------------------------------------------------------------- |
|
// Used to Store information about the enum, mainly for serialization to/from strings |
|
//------------------------------------------------------------------------------------------------- |
|
class GC_EnumMetaData |
|
{ |
|
public: |
|
// Hold the start and end of a part of an enum string |
|
struct StringPart |
|
{ |
|
uint16 myStart; |
|
uint16 myEnd; |
|
uint16 Length() const { return myEnd - myStart; } |
|
}; |
|
|
|
// Construct with a comma separated string and pre-allocated set of string parts to hold the string offsets. |
|
GC_EnumMetaData(char const* aString, StringPart* someParts, uint aCount, int aStart, int aDefault); |
|
|
|
GC_EnumString ToString(int aValue) const; |
|
bool FromString(char const* aString, int& aFoundValue) const; |
|
|
|
private: |
|
char const* myEnumString; |
|
StringPart const* myParts; |
|
uint myCount; |
|
int myStartValue; |
|
int myDefault; |
|
}; |
|
|
|
//------------------------------------------------------------------------------------------------- |
|
// GC_Enum functions as the actual type through a typedef |
|
//------------------------------------------------------------------------------------------------- |
|
template<typename EnumBase> |
|
struct GC_Enum : public EnumBase |
|
{ |
|
typedef typename EnumBase::Type Type; |
|
|
|
//------------------------------------------------------------------------------------------------- |
|
// for loop helper to iterate all values. While I'm not a fan of the generated code, it's not that commonly used. |
|
// Usage: for (Enum i : Enum::Range()) |
|
//------------------------------------------------------------------------------------------------- |
|
struct Iter |
|
{ |
|
GC_FORCEINLINE void operator++() { ++myValue; } |
|
GC_FORCEINLINE bool operator!=(Type anEnd) const { return myValue != anEnd; } |
|
GC_FORCEINLINE Type operator*() const { return (Type)myValue; } |
|
|
|
int myValue; |
|
}; |
|
struct Range |
|
{ |
|
GC_FORCEINLINE Iter begin() { return { EnumBase::Start }; } |
|
GC_FORCEINLINE Type end() { return EnumBase::End; } |
|
}; |
|
|
|
constexpr GC_Enum() : myValue(EnumBase::Default) {} |
|
constexpr GC_Enum(Type aValue) : myValue(aValue) {} |
|
constexpr explicit GC_Enum(int aValue) : myValue((Type)aValue) {} |
|
constexpr GC_Enum(GC_Enum const& aEnum) : myValue(aEnum.myValue) {} |
|
|
|
constexpr operator Type() const { return myValue; } |
|
constexpr Type GetEnd() const { return Type::End; } |
|
|
|
constexpr Type operator++() { myValue = Type(myValue + 1); return myValue; } |
|
constexpr Type operator++(int) { Type tmp = myValue; myValue = Type(myValue + 1); return tmp; } |
|
constexpr Type operator--() { myValue = Type(myValue - 1); return myValue; } |
|
constexpr Type operator--(int) { Type tmp = myValue; myValue = Type(myValue - 1); return tmp; } |
|
|
|
GC_EnumString ToString() const |
|
{ |
|
return ourMetaData.ToString(myValue); |
|
} |
|
|
|
static GC_EnumString ToString(Type aValue) |
|
{ |
|
return ourMetaData.ToString(aValue); |
|
} |
|
|
|
static GC_EnumString ToString(int aValue) |
|
{ |
|
return ourMetaData.ToString(aValue); |
|
} |
|
|
|
bool SetString(char const* aString) |
|
{ |
|
int value = myValue; |
|
bool found = ourMetaData.FromString(aString, value); |
|
myValue = (Type)value; |
|
return found; |
|
} |
|
|
|
static bool FromString(char const* aString, Type& aValueOut) |
|
{ |
|
int value; |
|
if (!ourMetaData.FromString(aString, value)) |
|
return false; |
|
|
|
aValueOut = Type(value); |
|
} |
|
|
|
static bool FromString(char const* aString, int& value) |
|
{ |
|
return ourMetaData.FromString(aString, value); |
|
} |
|
|
|
static Type FromString(char const* aString) |
|
{ |
|
int value = EnumBase::Start; |
|
ourMetaData.FromString(aString, value); |
|
return Type(value); |
|
} |
|
|
|
Type myValue; |
|
static GC_EnumMetaData const ourMetaData; |
|
}; |
|
|
|
template<typename EnumBase> |
|
GC_EnumMetaData const GC_Enum<EnumBase>::ourMetaData = EnumBase::GetMetaData(); |
|
|
|
//------------------------------------------------------------------------------------------------------------ |
|
// Declaration variant to change the first value |
|
//------------------------------------------------------------------------------------------------------------ |
|
#define GC_DECLARE_ENUM_WITH_START_AND_DEFAULT(type, start, defaultValue, first, ...) \ |
|
struct type##Base \ |
|
{ \ |
|
enum Type : int8 { first = start, Start = start, __VA_ARGS__, End, Default = defaultValue }; \ |
|
enum { Count = End - Start }; \ |
|
protected: \ |
|
static inline GC_EnumMetaData GetMetaData() \ |
|
{ \ |
|
static GC_EnumMetaData::StringPart ourParts[Count]; \ |
|
return GC_EnumMetaData(#first", "#__VA_ARGS__, ourParts, Count, Start, Default); \ |
|
} \ |
|
};\ |
|
typedef GC_Enum<type##Base> type; |
|
|
|
//------------------------------------------------------------------------------------------------------------ |
|
//------------------------------------------------------------------------------------------------------------ |
|
#define GC_DECLARE_ENUM_WITH_DEFAULT(type, defaultValue, first, ...) GC_DECLARE_ENUM_WITH_START_AND_DEFAULT(type, 0, defaultValue, first, __VA_ARGS__) |
|
#define GC_DECLARE_ENUM_WITH_START(type, start, first, ...) GC_DECLARE_ENUM_WITH_START_AND_DEFAULT(type, start, first, first, __VA_ARGS__) |
|
#define GC_DECLARE_ENUM(type, first, ...) GC_DECLARE_ENUM_WITH_START_AND_DEFAULT(type, 0, first, first, __VA_ARGS__) |
|
|
|
//------------------------------------------------------------------------------------------------------------ |
|
// allow some use of enum type without including it |
|
//------------------------------------------------------------------------------------------------------------ |
|
#define GC_FORWARD_DECLARE_ENUM(type) struct type##Info; typedef GC_Enum<type##Info> type |
|
|
|
//------------------------------------------------------------------------------------------------------------ |
|
// Add handling for normally hidden value when using switch statements. |
|
// Can optionally be used with default: to catch all unused values, but prevert to use on it's own |
|
// switch (en) { |
|
// GC_ENUM_UNREACHABLE(en); |
|
// } |
|
// OR |
|
// switch (en) { |
|
// default: GC_ENUM_UNREACHABLE(en); |
|
// } |
|
// |
|
// Doing it this way allows us to keep the error about unused elements if you don't add the default label. |
|
//------------------------------------------------------------------------------------------------------------ |
|
#define GC_ENUM_UNREACHABLE(value) case decltype(value)::End: GC_ASSERT(false, "%s not handled", value.ToString()); break; |