Skip to content

Instantly share code, notes, and snippets.

@ishani
Created December 5, 2012 00:01
Show Gist options
  • Save ishani/4210469 to your computer and use it in GitHub Desktop.
Save ishani/4210469 to your computer and use it in GitHub Desktop.
Fancy C++ Enums
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
// ---------------------------------------------------------------------------------------------------------------------
// these are the actions used within the enum creation macro to produce output per-entry
// we break out the _ID ones to avoid triggering errors when using Clang in -Wall mode
#define FANCYENUM_DEF_ENUM(_ty) _ty,
#define FANCYENUM_DEF_ENUM_ID(_ty, _id) _ty = _id,
#define FANCYENUM_DEF_TOSTR(_ty) case _ty: return #_ty;
#define FANCYENUM_DEF_FIDX(_ty) case _ty: return _ty;
#define FANCYENUM_DEF_VALID(_ty) case _ty: return true;
#define FANCYENUM_DEF_FROMSTR(_ty) if (strcmp(str, #_ty) == 0) return _ty; else
#define FANCYENUM_DEF_TOSTR_ID(_ty, ...) FANCYENUM_DEF_TOSTR(_ty)
#define FANCYENUM_DEF_FIDX_ID(_ty, ...) FANCYENUM_DEF_FIDX(_ty)
#define FANCYENUM_DEF_VALID_ID(_ty, ...) FANCYENUM_DEF_VALID(_ty)
#define FANCYENUM_DEF_FROMSTR_ID(_ty, ...) FANCYENUM_DEF_FROMSTR(_ty)
#if defined(_MSC_VER) && !defined(__CLANG_VSX__)
# define FANCYENUM_ADORN abstract sealed
# if _MSC_VER > 1600
# define FANCYENUM_STORAGE_SPEC(_storageType) : _storageType
# else
# define FANCYENUM_STORAGE_SPEC(_storageType)
# endif // _MSC_VER > 1600
#else
# define FANCYENUM_ADORN
# define FANCYENUM_STORAGE_SPEC(_storageType) : _storageType
#endif // _MSC_VER
// ---------------------------------------------------------------------------------------------------------------------
// main code expander
// _enumName is the wrapper name for the enum, eg. VertexFormat or PlayerState
// _storageType is used as the C++11 storage class if available, along with setting the index type
// _enumAdorn pass-through for any declaration adornments, like 'abstract' or 'sealed' on MSVC
// _macroWorker is the macro that takes an action macro to create code. confused yet?
// _????Builder are the functions that create code specific to individual functions
// _topAccessor chooses what to call the accessor for the 'top' or 'count' depending if it has IDs or not
//
#define _TL_CREATE_FANCYENUM(_enumName, _storageType, _enumAdorn, _macroWorker, _entryBuilder, _fidxBuilder, _validBuilder, _toStrBuilder, _fromStrBuilder, _topAccessor) \
struct _enumName _enumAdorn \
{ \
typedef _storageType StorageType; \
enum Enum FANCYENUM_STORAGE_SPEC(_storageType) \
{ \
_macroWorker(_entryBuilder) \
e_Top \
}; \
\
inline static const char* enumName() { return #_enumName; } \
inline static _storageType _topAccessor() { return static_cast<_storageType>(e_Top); } \
\
inline static _enumName::Enum getByValue(_storageType idx) \
{ \
switch (idx) \
{ \
_macroWorker(_fidxBuilder) \
default: \
{ \
assert(0); \
return (_enumName::e_Top); \
} \
} \
} \
\
inline static bool isValidValue(_storageType idx) \
{ \
switch ((_enumName::Enum)idx) \
{ \
_macroWorker(_validBuilder) \
default: \
return false; \
} \
} \
\
inline static const char* toString(_enumName::Enum e) \
{ \
switch (e) \
{ \
_macroWorker(_toStrBuilder) \
default: \
{ \
assert(0); \
return "error"; \
} \
} \
} \
\
inline static _enumName::Enum fromString(const char* str) \
{ \
_macroWorker(_fromStrBuilder) \
{ \
assert(0); \
return (_enumName::e_Top); \
} \
} \
private: \
_enumName(); \
}
#define CREATE_FANCYENUM(_enumName, _storageType, _macroWorker) \
_TL_CREATE_FANCYENUM(_enumName, _storageType, FANCYENUM_ADORN, _macroWorker, FANCYENUM_DEF_ENUM, FANCYENUM_DEF_FIDX, FANCYENUM_DEF_VALID, FANCYENUM_DEF_TOSTR, FANCYENUM_DEF_FROMSTR, enumCount)
#define CREATE_FANCYENUM_IDS(_enumName, _storageType, _macroWorker) \
_TL_CREATE_FANCYENUM(_enumName, _storageType, FANCYENUM_ADORN, _macroWorker, FANCYENUM_DEF_ENUM_ID, FANCYENUM_DEF_FIDX_ID, FANCYENUM_DEF_VALID_ID, FANCYENUM_DEF_TOSTR_ID, FANCYENUM_DEF_FROMSTR_ID, enumLast)
// ---------------------------------------------------------------------------------------------------------------------
// demonstrate creation of default enum
//
#define BF_VERTEX_FORMATS(_entry) \
_entry(Vertex_Pos3) \
_entry(Vertex_Pos4) \
_entry(Vertex_Pos3_RGBA) \
_entry(Vertex_Pos4_RGBA) \
_entry(Vertex_Pos3_RGBA_Normal3) \
_entry(Vertex_Pos3_RGBA_Normal3_UV0) \
_entry(Vertex_Pos3_RGBA_UV0)
CREATE_FANCYENUM(VertexFormat, uint16_t, BF_VERTEX_FORMATS);
// ---------------------------------------------------------------------------------------------------------------------
// create enum with manual ID assignment
//
#define BF_WITH_IDS(_entry) \
_entry(Kepler, 5) \
_entry(Hthran, -10) \
_entry(Eidelon, -11) \
_entry(Montegue, -20) \
_entry(Falagray, 22) \
_entry(Sepfarra, 51) \
_entry(Winteray, 60) \
CREATE_FANCYENUM_IDS(Codenames, int32_t, BF_WITH_IDS);
// ---------------------------------------------------------------------------------------------------------------------
int main(int, char*[])
{
VertexFormat::Enum k = VertexFormat::Vertex_Pos3_RGBA_Normal3_UV0;
const char* str = VertexFormat::toString(k);
k = VertexFormat::fromString(str);
printf("%s has %i entries, of which %s == %i\n\n",
VertexFormat::enumName(),
VertexFormat::enumCount(),
str,
k);
for (VertexFormat::StorageType i=0; i<VertexFormat::enumCount(); i++)
{
printf("> %s == %i \n", VertexFormat::toString( VertexFormat::getByValue(i) ), VertexFormat::getByValue(i) );
}
printf("\n------------\n\n");
Codenames::Enum n = Codenames::Falagray;
str = Codenames::toString(n);
n = Codenames::fromString(str);
printf("%s final entry == %i, of which %s == %i\n\n",
Codenames::enumName(),
Codenames::enumLast(),
str,
n);
for (Codenames::StorageType i=-30; i<(Codenames::StorageType)Codenames::enumLast(); i++)
{
if (Codenames::isValidValue(i))
printf("> %s == %i \n", Codenames::toString( Codenames::getByValue(i) ), Codenames::getByValue(i) );
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment