better-enums/enum.h
Anton Bachin 4314ad3fd3 Experimental generalization of underlying types.
With this change, the underlying type can be a non-integral type that
provides conversions to and from an integral type. See the test at
test/cxxtest/underlying.h for some examples - though they are more
verbose than strictly necessary, for testing needs.

Move constructors in underlying types are not supported. It has been
difficult so far to get constexpr code not to select the move
constructor, which is generally not constexpr, for various operations.
2015-06-11 20:39:46 -05:00

1042 lines
54 KiB
C++

// This file is part of Better Enums, released under the BSD 2-clause license.
// See LICENSE for details, or visit http://github.com/aantron/better-enums.
#pragma once
#ifndef BETTER_ENUMS__ENUM_H
#define BETTER_ENUMS__ENUM_H
#include <cstddef>
#include <cstring>
#include <stdexcept>
#ifdef __GNUC__
# ifdef __clang__
# if __has_feature(cxx_constexpr)
# define BETTER_ENUMS__HAVE_CONSTEXPR
# endif
# if (__clang_major__ > 2) || \
(__clang_major__ == 2) && (__clang_minor__ >= 9)
# define BETTER_ENUMS__HAVE_LONG_LONG
# define BETTER_ENUMS__HAVE_NEW_CHAR_TYPES
# endif
# else
# if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L
# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6))
# define BETTER_ENUMS__HAVE_CONSTEXPR
# endif
# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3))
# define BETTER_ENUMS__HAVE_LONG_LONG
# endif
# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4))
# define BETTER_ENUMS__HAVE_NEW_CHAR_TYPES
# endif
# endif
# endif
#endif
#ifdef _MSC_VER
# if _MSC_VER >= 1600
# define BETTER_ENUMS__HAVE_LONG_LONG
# endif
#endif
#ifdef BETTER_ENUMS_CONSTEXPR
# define BETTER_ENUMS__HAVE_CONSTEXPR
#endif
#ifdef BETTER_ENUMS_NO_CONSTEXPR
# ifdef BETTER_ENUMS__HAVE_CONSTEXPR
# undef BETTER_ENUMS__HAVE_CONSTEXPR
# endif
#endif
// GCC (and maybe clang) can be made to warn about using 0 or NULL when nullptr
// is available, so Better Enums tries to use nullptr. This passage uses
// availability of constexpr as a proxy for availability of nullptr, i.e. it
// assumes that nullptr is available when compiling on the right versions of gcc
// and clang with the right -std flag. This is actually slightly wrong, because
// nullptr is also available in Visual C++, but constexpr isn't. This
// imprecision doesn't matter, however, because VC++ doesn't have the warnings
// that make using nullptr necessary.
#ifdef BETTER_ENUMS__HAVE_CONSTEXPR
# define BETTER_ENUMS__CONSTEXPR constexpr
# define BETTER_ENUMS__NULLPTR nullptr
#else
# define BETTER_ENUMS__CONSTEXPR
# define BETTER_ENUMS__NULLPTR NULL
#endif
#ifdef BETTER_ENUMS_MACRO_FILE
# include BETTER_ENUMS_MACRO_FILE
#else
#define BETTER_ENUMS__PP_MAP(macro, data, ...) \
BETTER_ENUMS__ID( \
BETTER_ENUMS__APPLY( \
BETTER_ENUMS__PP_MAP_VAR_COUNT, \
BETTER_ENUMS__PP_COUNT(__VA_ARGS__)) \
(macro, data, __VA_ARGS__))
#define BETTER_ENUMS__PP_MAP_VAR_COUNT(count) BETTER_ENUMS__M ## count
#define BETTER_ENUMS__APPLY(macro, ...) BETTER_ENUMS__ID(macro(__VA_ARGS__))
#define BETTER_ENUMS__ID(x) x
#define BETTER_ENUMS__M1(m, d, x) m(d,0,x)
#define BETTER_ENUMS__M2(m,d,x,...) m(d,1,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M1(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M3(m,d,x,...) m(d,2,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M2(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M4(m,d,x,...) m(d,3,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M3(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M5(m,d,x,...) m(d,4,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M4(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M6(m,d,x,...) m(d,5,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M5(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M7(m,d,x,...) m(d,6,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M6(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M8(m,d,x,...) m(d,7,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M7(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M9(m,d,x,...) m(d,8,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M8(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M10(m,d,x,...) m(d,9,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M9(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M11(m,d,x,...) m(d,10,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M10(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M12(m,d,x,...) m(d,11,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M11(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M13(m,d,x,...) m(d,12,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M12(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M14(m,d,x,...) m(d,13,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M13(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M15(m,d,x,...) m(d,14,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M14(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M16(m,d,x,...) m(d,15,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M15(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M17(m,d,x,...) m(d,16,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M16(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M18(m,d,x,...) m(d,17,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M17(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M19(m,d,x,...) m(d,18,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M18(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M20(m,d,x,...) m(d,19,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M19(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M21(m,d,x,...) m(d,20,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M20(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M22(m,d,x,...) m(d,21,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M21(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M23(m,d,x,...) m(d,22,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M22(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M24(m,d,x,...) m(d,23,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M23(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M25(m,d,x,...) m(d,24,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M24(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M26(m,d,x,...) m(d,25,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M25(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M27(m,d,x,...) m(d,26,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M26(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M28(m,d,x,...) m(d,27,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M27(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M29(m,d,x,...) m(d,28,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M28(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M30(m,d,x,...) m(d,29,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M29(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M31(m,d,x,...) m(d,30,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M30(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M32(m,d,x,...) m(d,31,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M31(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M33(m,d,x,...) m(d,32,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M32(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M34(m,d,x,...) m(d,33,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M33(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M35(m,d,x,...) m(d,34,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M34(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M36(m,d,x,...) m(d,35,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M35(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M37(m,d,x,...) m(d,36,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M36(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M38(m,d,x,...) m(d,37,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M37(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M39(m,d,x,...) m(d,38,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M38(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M40(m,d,x,...) m(d,39,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M39(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M41(m,d,x,...) m(d,40,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M40(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M42(m,d,x,...) m(d,41,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M41(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M43(m,d,x,...) m(d,42,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M42(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M44(m,d,x,...) m(d,43,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M43(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M45(m,d,x,...) m(d,44,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M44(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M46(m,d,x,...) m(d,45,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M45(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M47(m,d,x,...) m(d,46,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M46(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M48(m,d,x,...) m(d,47,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M47(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M49(m,d,x,...) m(d,48,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M48(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M50(m,d,x,...) m(d,49,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M49(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M51(m,d,x,...) m(d,50,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M50(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M52(m,d,x,...) m(d,51,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M51(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M53(m,d,x,...) m(d,52,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M52(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M54(m,d,x,...) m(d,53,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M53(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M55(m,d,x,...) m(d,54,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M54(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M56(m,d,x,...) m(d,55,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M55(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M57(m,d,x,...) m(d,56,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M56(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M58(m,d,x,...) m(d,57,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M57(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M59(m,d,x,...) m(d,58,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M58(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M60(m,d,x,...) m(d,59,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M59(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M61(m,d,x,...) m(d,60,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M60(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M62(m,d,x,...) m(d,61,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M61(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M63(m,d,x,...) m(d,62,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M62(m,d,__VA_ARGS__))
#define BETTER_ENUMS__M64(m,d,x,...) m(d,63,x) \
BETTER_ENUMS__ID(BETTER_ENUMS__M63(m,d,__VA_ARGS__))
#define BETTER_ENUMS__PP_COUNT_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
_11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, \
_26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \
_41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, \
_56, _57, _58, _59, _60, _61, _62, _63, _64, count, ...) count
#define BETTER_ENUMS__PP_COUNT(...) \
BETTER_ENUMS__ID(BETTER_ENUMS__PP_COUNT_IMPL(__VA_ARGS__, 64, 63, 62, 61, \
60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43,\
42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25,\
24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,\
5, 4, 3, 2, 1))
#define BETTER_ENUMS__ITERATE(X, f, l) X(f, l, 0) X(f, l, 1) X(f, l, 2) \
X(f, l, 3) X(f, l, 4) X(f, l, 5) X(f, l, 6) X(f, l, 7) X(f, l, 8) \
X(f, l, 9) X(f, l, 10) X(f, l, 11) X(f, l, 12) X(f, l, 13) X(f, l, 14) \
X(f, l, 15) X(f, l, 16) X(f, l, 17) X(f, l, 18) X(f, l, 19) X(f, l, 20) \
X(f, l, 21) X(f, l, 22) X(f, l, 23)
#endif // #ifdef BETTER_ENUMS_MACRO_FILE else case
#define BETTER_ENUMS__EAT_ASSIGN_SINGLE(EnumType, index, expression) \
((::better_enums::_eat_assign<EnumType>)EnumType::expression),
#define BETTER_ENUMS__EAT_ASSIGN(EnumType, ...) \
BETTER_ENUMS__ID( \
BETTER_ENUMS__PP_MAP( \
BETTER_ENUMS__EAT_ASSIGN_SINGLE, EnumType, __VA_ARGS__))
#define BETTER_ENUMS__NS(EnumType) better_enums::_data_ ## EnumType
#define BETTER_ENUMS__NAME_ENDERS "= \t\n"
namespace better_enums {
template <typename T>
BETTER_ENUMS__CONSTEXPR inline T _default()
{
return (typename T::_enumerated)0;
}
template <>
BETTER_ENUMS__CONSTEXPR inline const char* _default<const char*>()
{
return BETTER_ENUMS__NULLPTR;
}
template <>
BETTER_ENUMS__CONSTEXPR inline std::size_t _default<std::size_t>()
{
return 0;
}
template <typename T>
struct optional {
BETTER_ENUMS__CONSTEXPR optional() :
_valid(false), _value(_default<T>()) { }
BETTER_ENUMS__CONSTEXPR optional(T v) : _valid(true), _value(v) { }
BETTER_ENUMS__CONSTEXPR const T& operator *() const { return _value; }
BETTER_ENUMS__CONSTEXPR const T* operator ->() const { return &_value; }
BETTER_ENUMS__CONSTEXPR operator bool() const { return _valid; }
BETTER_ENUMS__CONSTEXPR const T& value() const { return _value; }
private:
bool _valid;
T _value;
};
template <typename CastTo, typename Element>
BETTER_ENUMS__CONSTEXPR static optional<CastTo>
_map_index(const Element *array, optional<std::size_t> index)
{
return index ? (CastTo)array[*index] : optional<CastTo>();
}
template <typename T>
BETTER_ENUMS__CONSTEXPR static T _or_throw(optional<T> maybe,
const char *message)
{
return maybe ? *maybe : throw std::runtime_error(message);
}
template <typename T, typename U>
BETTER_ENUMS__CONSTEXPR U continue_with(T ignored, U value) { return value; }
template <typename EnumType>
struct _eat_assign {
explicit BETTER_ENUMS__CONSTEXPR _eat_assign(EnumType value) : _value(value)
{ }
template <typename Any>
BETTER_ENUMS__CONSTEXPR const _eat_assign& operator =(Any dummy) const
{ return *this; }
BETTER_ENUMS__CONSTEXPR operator EnumType () const { return _value; }
private:
EnumType _value;
};
template <typename Element>
struct _Iterable {
typedef const Element* iterator;
BETTER_ENUMS__CONSTEXPR iterator begin() const { return iterator(_array); }
BETTER_ENUMS__CONSTEXPR iterator end() const
{ return iterator(_array + _size); }
BETTER_ENUMS__CONSTEXPR std::size_t size() const { return _size; }
BETTER_ENUMS__CONSTEXPR const Element& operator [](std::size_t index) const
{ return _array[index]; }
BETTER_ENUMS__CONSTEXPR _Iterable(const Element *array, std::size_t s) :
_array(array), _size(s) { }
private:
const Element * const _array;
const std::size_t _size;
};
BETTER_ENUMS__CONSTEXPR inline bool _endsName(char c, std::size_t index = 0)
{
return
c == BETTER_ENUMS__NAME_ENDERS[index] ? true :
BETTER_ENUMS__NAME_ENDERS[index] == '\0' ? false :
_endsName(c, index + 1);
}
BETTER_ENUMS__CONSTEXPR inline bool _hasExplicitValue(const char *s,
std::size_t index = 0)
{
return
s[index] == '\0' ? false :
s[index] == '=' ? true :
_hasExplicitValue(s, index + 1);
}
BETTER_ENUMS__CONSTEXPR inline std::size_t
_constantLength(const char *s, std::size_t index = 0)
{
return _endsName(s[index]) ? index : _constantLength(s, index + 1);
}
BETTER_ENUMS__CONSTEXPR inline char
_select(const char *from, std::size_t from_length, std::size_t index)
{
return index >= from_length ? '\0' : from[index];
}
BETTER_ENUMS__CONSTEXPR inline char _toLowercaseAscii(char c)
{
return c >= 0x41 && c <= 0x5A ? (char)(c + 0x20) : c;
}
BETTER_ENUMS__CONSTEXPR inline bool _namesMatch(const char *stringizedName,
const char *referenceName,
std::size_t index = 0)
{
return
_endsName(stringizedName[index]) ? referenceName[index] == '\0' :
referenceName[index] == '\0' ? false :
stringizedName[index] != referenceName[index] ? false :
_namesMatch(stringizedName, referenceName, index + 1);
}
BETTER_ENUMS__CONSTEXPR inline bool
_namesMatchNocase(const char *stringizedName, const char *referenceName,
std::size_t index = 0)
{
return
_endsName(stringizedName[index]) ? referenceName[index] == '\0' :
referenceName[index] == '\0' ? false :
_toLowercaseAscii(stringizedName[index]) !=
_toLowercaseAscii(referenceName[index]) ? false :
_namesMatchNocase(stringizedName, referenceName, index + 1);
}
inline void _trim_names(const char * const *raw_names,
const char **trimmed_names, std::size_t count)
{
for (std::size_t index = 0; index < count; ++index) {
std::size_t length =
std::strcspn(raw_names[index], BETTER_ENUMS__NAME_ENDERS);
char *trimmed = new char[length + 1];
std::strncpy(trimmed, raw_names[index], length);
trimmed[length] = '\0';
trimmed_names[index] = trimmed;
}
}
// This template is unfortunately necessary (?) due to the lack of <type_traits>
// in C++98. <With type_traits>, this template could be replaced with a
// combination of std::conditional and std::is_integral.
template <typename T>
struct representation { typedef typename T::integral_representation type; };
template <> struct representation<bool> { typedef bool type; };
template <> struct representation<char> { typedef char type; };
template <> struct representation<wchar_t> { typedef wchar_t type; };
template <> struct representation<signed char> { typedef signed char type; };
template <> struct representation<unsigned char>
{ typedef unsigned char type; };
template <> struct representation<short> { typedef short type; };
template <> struct representation<unsigned short>
{ typedef unsigned short type; };
template <> struct representation<int> { typedef int type; };
template <> struct representation<unsigned int> { typedef unsigned int type; };
template <> struct representation<long> { typedef long type; };
template <> struct representation<unsigned long>
{ typedef unsigned long type; };
#ifdef BETTER_ENUMS__HAVE_LONG_LONG
template <> struct representation<long long> { typedef long long type; };
template <> struct representation<unsigned long long>
{ typedef unsigned long long type; };
#endif
#ifdef BETTER_ENUMS__HAVE_NEW_CHAR_TYPES
template <> struct representation<char16_t> { typedef char16_t type; };
template <> struct representation<char32_t> { typedef char32_t type; };
#endif
template <typename T>
struct underlying_traits {
typedef typename representation<T>::type integral_representation;
BETTER_ENUMS__CONSTEXPR static integral_representation
to_integral(const T &v) { return (integral_representation)v; }
BETTER_ENUMS__CONSTEXPR static T
from_integral(integral_representation n) { return T(n); }
BETTER_ENUMS__CONSTEXPR static bool
are_equal(const T& u, const T& v) { return u == v; }
};
} // namespace better_enums
#ifdef BETTER_ENUMS__HAVE_CONSTEXPR
#define BETTER_ENUMS__SELECT_SINGLE_CHARACTER(from, from_length, index) \
::better_enums::_select(from, from_length, index),
#define BETTER_ENUMS__SELECT_CHARACTERS(from, from_length) \
BETTER_ENUMS__ITERATE( \
BETTER_ENUMS__SELECT_SINGLE_CHARACTER, from, from_length)
#define BETTER_ENUMS__TRIM_SINGLE_STRING(ignored, index, expression) \
constexpr std::size_t _length_ ## index = \
::better_enums::_constantLength(#expression); \
constexpr const char _trimmed_ ## index [] = \
{ BETTER_ENUMS__SELECT_CHARACTERS(#expression, _length_ ## index) }; \
constexpr const char *_final_ ## index = \
::better_enums::_hasExplicitValue(#expression) ? \
_trimmed_ ## index : #expression;
#define BETTER_ENUMS__TRIM_STRINGS(...) \
BETTER_ENUMS__ID( \
BETTER_ENUMS__PP_MAP( \
BETTER_ENUMS__TRIM_SINGLE_STRING, ignored, __VA_ARGS__))
#define BETTER_ENUMS__REFER_TO_SINGLE_STRING(ignored, index, expression) \
_final_ ## index,
#define BETTER_ENUMS__REFER_TO_STRINGS(...) \
BETTER_ENUMS__ID( \
BETTER_ENUMS__PP_MAP( \
BETTER_ENUMS__REFER_TO_SINGLE_STRING, ignored, __VA_ARGS__))
#endif // #ifdef BETTER_ENUMS__HAVE_CONSTEXPR
#define BETTER_ENUMS__STRINGIZE_SINGLE(ignored, index, expression) #expression,
#define BETTER_ENUMS__STRINGIZE(...) \
BETTER_ENUMS__ID( \
BETTER_ENUMS__PP_MAP( \
BETTER_ENUMS__STRINGIZE_SINGLE, ignored, __VA_ARGS__))
// TODO Convert integral to underlying only at the last possible moment, if the
// integral value is valid. This should prevent unexpected behavior. For this to
// work, the underlying values array must store the integral equivalents. That
// would also eliminate the need for "are_equal", but would increase overhead
// during iteration.
// TODO Choose the right return type semantics once the _values array
// representation is chosen: values if integral, const references if underlying.
#define BETTER_ENUMS__TYPE(SetUnderlyingType, SwitchType, GenerateSwitchType, \
GenerateStrings, ToStringConstexpr, \
DeclareInitialize, DefineInitialize, CallInitialize,\
Enum, Underlying, ...) \
\
namespace better_enums { \
namespace _data_ ## Enum { \
\
BETTER_ENUMS__ID(GenerateSwitchType(Underlying, __VA_ARGS__)) \
\
} \
} \
\
class Enum { \
private: \
typedef ::better_enums::optional<Enum> _optional; \
typedef ::better_enums::optional<std::size_t> _optional_index; \
typedef ::better_enums::underlying_traits<Underlying> _traits; \
\
public: \
typedef Underlying _underlying; \
typedef _traits::integral_representation _integral; \
\
enum _enumerated SetUnderlyingType(Underlying) { __VA_ARGS__ }; \
\
BETTER_ENUMS__CONSTEXPR Enum(_enumerated value) : \
_value(_traits::from_integral(value)) { } \
\
BETTER_ENUMS__CONSTEXPR operator SwitchType(Enum)() const \
{ \
return (SwitchType(Enum))_traits::to_integral(_value); \
} \
\
_underlying& operator *() { return _value; } \
BETTER_ENUMS__CONSTEXPR const _underlying& operator *() const \
{ return _value; } \
\
_underlying* operator ->() { return &_value; } \
BETTER_ENUMS__CONSTEXPR const _underlying* operator ->() const \
{ return &_value; } \
\
_underlying _to_underlying() { return _value; } \
BETTER_ENUMS__CONSTEXPR const _underlying& _to_underlying() const \
{ return _value; } \
\
BETTER_ENUMS__CONSTEXPR static Enum \
_from_underlying(const _underlying &value); \
BETTER_ENUMS__CONSTEXPR static Enum \
_from_underlying_unchecked(const _underlying &value); \
BETTER_ENUMS__CONSTEXPR static _optional \
_from_underlying_nothrow(const _underlying &value); \
\
BETTER_ENUMS__CONSTEXPR _integral _to_integral() const; \
BETTER_ENUMS__CONSTEXPR static Enum _from_integral(_integral value); \
BETTER_ENUMS__CONSTEXPR static Enum \
_from_integral_unchecked(_integral value); \
BETTER_ENUMS__CONSTEXPR static _optional \
_from_integral_nothrow(_integral value); \
\
ToStringConstexpr const char* _to_string() const; \
BETTER_ENUMS__CONSTEXPR static Enum _from_string(const char *name); \
BETTER_ENUMS__CONSTEXPR static _optional \
_from_string_nothrow(const char *name); \
\
BETTER_ENUMS__CONSTEXPR static Enum _from_string_nocase(const char *name); \
BETTER_ENUMS__CONSTEXPR static _optional \
_from_string_nocase_nothrow(const char *name); \
\
BETTER_ENUMS__CONSTEXPR static bool _is_valid(const _underlying &value); \
BETTER_ENUMS__CONSTEXPR static bool _is_valid(const char *name); \
BETTER_ENUMS__CONSTEXPR static bool _is_valid_nocase(const char *name); \
\
typedef ::better_enums::_Iterable<Enum> _value_iterable; \
typedef ::better_enums::_Iterable<const char*> _name_iterable; \
\
typedef _value_iterable::iterator _value_iterator; \
typedef _name_iterable::iterator _name_iterator; \
\
BETTER_ENUMS__CONSTEXPR static const std::size_t _size = \
BETTER_ENUMS__ID(BETTER_ENUMS__PP_COUNT(__VA_ARGS__)); \
\
BETTER_ENUMS__CONSTEXPR static const char* _name(); \
BETTER_ENUMS__CONSTEXPR static _value_iterable _values(); \
ToStringConstexpr static _name_iterable _names(); \
\
_underlying _value; \
\
private: \
Enum() : _value(_traits::from_integral(0)) { } \
\
explicit BETTER_ENUMS__CONSTEXPR Enum(const _underlying &value) : \
_value(value) { } \
\
DeclareInitialize \
\
BETTER_ENUMS__CONSTEXPR static _optional_index \
_from_value_loop(const _underlying &value, std::size_t index = 0); \
BETTER_ENUMS__CONSTEXPR static _optional_index \
_from_string_loop(const char *name, std::size_t index = 0); \
BETTER_ENUMS__CONSTEXPR static _optional_index \
_from_string_nocase_loop(const char *name, std::size_t index = 0); \
}; \
\
namespace better_enums { \
namespace _data_ ## Enum { \
\
enum PutNamesInThisScopeAlso { __VA_ARGS__ }; \
\
BETTER_ENUMS__CONSTEXPR const Enum value_array[] = \
{ BETTER_ENUMS__ID(BETTER_ENUMS__EAT_ASSIGN(Enum, __VA_ARGS__)) }; \
\
BETTER_ENUMS__ID(GenerateStrings(Enum, __VA_ARGS__)) \
\
} \
} \
\
BETTER_ENUMS__CONSTEXPR inline const Enum \
operator +(Enum::_enumerated enumerated) \
{ \
return (Enum)enumerated; \
} \
\
BETTER_ENUMS__CONSTEXPR inline Enum \
Enum::_from_underlying(const _underlying &value) \
{ \
return \
::better_enums::_or_throw(_from_underlying_nothrow(value), \
"Enum::_from_underlying: invalid argument"); \
} \
\
BETTER_ENUMS__CONSTEXPR inline Enum \
Enum::_from_underlying_unchecked(const _underlying &value) \
{ \
return Enum(value); \
} \
\
BETTER_ENUMS__CONSTEXPR inline Enum::_optional \
Enum::_from_underlying_nothrow(const _underlying &value) \
{ \
return \
::better_enums::_map_index<Enum>(BETTER_ENUMS__NS(Enum)::value_array, \
_from_value_loop(value)); \
} \
\
BETTER_ENUMS__CONSTEXPR inline Enum::_integral Enum::_to_integral() const \
{ \
return _traits::to_integral(_value); \
} \
\
BETTER_ENUMS__CONSTEXPR inline Enum \
Enum::_from_integral_unchecked(_integral value) \
{ \
return (_enumerated)value; \
} \
\
BETTER_ENUMS__CONSTEXPR inline Enum Enum::_from_integral(_integral value) \
{ \
return \
::better_enums::_or_throw(_from_integral_nothrow(value), \
"Enum::_from_integral: invalid argument"); \
} \
\
BETTER_ENUMS__CONSTEXPR inline Enum::_optional \
Enum::_from_integral_nothrow(_integral value) \
{ \
return _from_underlying_nothrow(_traits::from_integral(value)); \
} \
\
ToStringConstexpr inline const char* Enum::_to_string() const \
{ \
return \
::better_enums::_or_throw( \
::better_enums::_map_index<const char*>( \
BETTER_ENUMS__NS(Enum)::name_array(), \
_from_value_loop(CallInitialize(_value))), \
"Enum::to_string: invalid enum value"); \
} \
\
BETTER_ENUMS__CONSTEXPR inline Enum Enum::_from_string(const char *name) \
{ \
return \
::better_enums::_or_throw(_from_string_nothrow(name), \
"Enum::_from_string: invalid argument"); \
} \
\
BETTER_ENUMS__CONSTEXPR inline Enum::_optional \
Enum::_from_string_nothrow(const char *name) \
{ \
return \
::better_enums::_map_index<Enum>( \
BETTER_ENUMS__NS(Enum)::value_array, _from_string_loop(name)); \
} \
\
BETTER_ENUMS__CONSTEXPR inline Enum Enum::_from_string_nocase(const char *name)\
{ \
return \
::better_enums::_or_throw( \
_from_string_nocase_nothrow(name), \
"Enum::_from_string_nocase: invalid argument"); \
} \
\
BETTER_ENUMS__CONSTEXPR inline Enum::_optional \
Enum::_from_string_nocase_nothrow(const char *name) \
{ \
return \
::better_enums::_map_index<Enum>(BETTER_ENUMS__NS(Enum)::value_array, \
_from_string_nocase_loop(name)); \
} \
\
BETTER_ENUMS__CONSTEXPR inline bool Enum::_is_valid(const _underlying &value) \
{ \
return _from_value_loop(value); \
} \
\
BETTER_ENUMS__CONSTEXPR inline bool Enum::_is_valid(const char *name) \
{ \
return _from_string_loop(name); \
} \
\
BETTER_ENUMS__CONSTEXPR inline bool Enum::_is_valid_nocase(const char *name) \
{ \
return _from_string_nocase_loop(name); \
} \
\
BETTER_ENUMS__CONSTEXPR inline const char* Enum::_name() \
{ \
return #Enum; \
} \
\
BETTER_ENUMS__CONSTEXPR inline Enum::_value_iterable Enum::_values() \
{ \
return _value_iterable(BETTER_ENUMS__NS(Enum)::value_array, _size); \
} \
\
ToStringConstexpr inline Enum::_name_iterable Enum::_names() \
{ \
return \
_name_iterable(BETTER_ENUMS__NS(Enum)::name_array(), \
CallInitialize(_size)); \
} \
\
DefineInitialize(Enum) \
\
BETTER_ENUMS__CONSTEXPR inline Enum::_optional_index \
Enum::_from_value_loop(const Enum::_underlying &value, std::size_t index) \
{ \
return \
index == _size ? _optional_index() : \
_traits::are_equal( \
BETTER_ENUMS__NS(Enum)::value_array[index]._value, value) ? \
_optional_index(index) : \
_from_value_loop(value, index + 1); \
} \
\
BETTER_ENUMS__CONSTEXPR inline Enum::_optional_index \
Enum::_from_string_loop(const char *name, std::size_t index) \
{ \
return \
index == _size ? _optional_index() : \
::better_enums::_namesMatch(BETTER_ENUMS__NS(Enum)::raw_names()[index],\
name) ? \
_optional_index(index) : \
_from_string_loop(name, index + 1); \
} \
\
BETTER_ENUMS__CONSTEXPR inline Enum::_optional_index \
Enum::_from_string_nocase_loop(const char *name, std::size_t index) \
{ \
return \
index == _size ? _optional_index() : \
::better_enums::_namesMatchNocase( \
BETTER_ENUMS__NS(Enum)::raw_names()[index], name) ? \
_optional_index(index) : \
_from_string_nocase_loop(name, index + 1); \
} \
\
BETTER_ENUMS__CONSTEXPR inline bool operator ==(const Enum &a, const Enum &b) \
{ \
return \
::better_enums::underlying_traits<Enum::_underlying> \
::are_equal(a._value, b._value); \
} \
\
BETTER_ENUMS__CONSTEXPR inline bool operator !=(const Enum &a, const Enum &b) \
{ return !(a == b); }
// C++98, C++11
#define BETTER_ENUMS__CXX98_UNDERLYING_TYPE(Underlying)
// C++11
#define BETTER_ENUMS__CXX11_UNDERLYING_TYPE(Underlying) \
: ::better_enums::underlying_traits<Underlying>::integral_representation
// C++98, C++11
#define BETTER_ENUMS__REGULAR_ENUM_SWITCH_TYPE(Type) \
_enumerated
// C++11
#define BETTER_ENUMS__ENUM_CLASS_SWITCH_TYPE(Type) \
BETTER_ENUMS__NS(Type)::EnumClassForSwitchStatements
// C++98, C++11
#define BETTER_ENUMS__REGULAR_ENUM_SWITCH_TYPE_GENERATE(Underlying, ...)
// C++11
#define BETTER_ENUMS__ENUM_CLASS_SWITCH_TYPE_GENERATE(Underlying, ...) \
enum class EnumClassForSwitchStatements : \
::better_enums::underlying_traits<Underlying>::integral_representation \
{ __VA_ARGS__ };
// C++98
#define BETTER_ENUMS__CXX98_TRIM_STRINGS_ARRAYS(Enum, ...) \
inline const char** raw_names() \
{ \
static const char *value[] = \
{ BETTER_ENUMS__ID(BETTER_ENUMS__STRINGIZE(__VA_ARGS__)) }; \
return value; \
} \
\
inline const char** name_array() \
{ \
static const char *value[Enum::_size]; \
return value; \
} \
\
inline bool& initialized() \
{ \
static bool value = false; \
return value; \
}
// C++11 fast version
#define BETTER_ENUMS__CXX11_PARTIAL_CONSTEXPR_TRIM_STRINGS_ARRAYS(Enum, ...) \
constexpr const char *the_raw_names[] = \
{ BETTER_ENUMS__ID(BETTER_ENUMS__STRINGIZE(__VA_ARGS__)) }; \
\
constexpr const char * const * raw_names() \
{ \
return the_raw_names; \
} \
\
inline const char** name_array() \
{ \
static const char *value[Enum::_size]; \
return value; \
} \
\
inline bool& initialized() \
{ \
static bool value = false; \
return value; \
}
// C++11 slow all-constexpr version
#define BETTER_ENUMS__CXX11_FULL_CONSTEXPR_TRIM_STRINGS_ARRAYS(Enum, ...) \
BETTER_ENUMS__ID(BETTER_ENUMS__TRIM_STRINGS(__VA_ARGS__)) \
\
constexpr const char * const the_name_array[] = \
{ BETTER_ENUMS__ID(BETTER_ENUMS__REFER_TO_STRINGS(__VA_ARGS__)) }; \
\
constexpr const char * const * name_array() \
{ \
return the_name_array; \
} \
\
constexpr const char * const * raw_names() \
{ \
return the_name_array; \
}
// C++98, C++11 fast version
#define BETTER_ENUMS__NO_CONSTEXPR_TO_STRING_KEYWORD
// C++11 slow all-constexpr version
#define BETTER_ENUMS__CONSTEXPR_TO_STRING_KEYWORD \
constexpr
// C++98, C++11 fast version
#define BETTER_ENUMS__DO_DECLARE_INITIALIZE \
static int initialize();
// C++11 slow all-constexpr version
#define BETTER_ENUMS__DO_NOT_DECLARE_INITIALIZE
// C++98, C++11 fast version
#define BETTER_ENUMS__DO_DEFINE_INITIALIZE(Enum) \
inline int Enum::initialize() \
{ \
if (BETTER_ENUMS__NS(Enum)::initialized()) \
return 0; \
\
::better_enums::_trim_names(BETTER_ENUMS__NS(Enum)::raw_names(), \
BETTER_ENUMS__NS(Enum)::name_array(), \
_size); \
\
BETTER_ENUMS__NS(Enum)::initialized() = true; \
\
return 0; \
}
// C++11 slow all-constexpr version
#define BETTER_ENUMS__DO_NOT_DEFINE_INITIALIZE(Enum)
// C++98, C++11 fast version
#define BETTER_ENUMS__DO_CALL_INITIALIZE(value) \
::better_enums::continue_with(initialize(), value)
// C++11 slow all-constexpr version
#define BETTER_ENUMS__DO_NOT_CALL_INITIALIZE(value) \
value
#ifdef BETTER_ENUMS_STRICT_CONVERSION
# define BETTER_ENUMS__DEFAULT_SWITCH_TYPE \
BETTER_ENUMS__ENUM_CLASS_SWITCH_TYPE
# define BETTER_ENUMS__DEFAULT_SWITCH_TYPE_GENERATE \
BETTER_ENUMS__ENUM_CLASS_SWITCH_TYPE_GENERATE
#else
# define BETTER_ENUMS__DEFAULT_SWITCH_TYPE \
BETTER_ENUMS__REGULAR_ENUM_SWITCH_TYPE
# define BETTER_ENUMS__DEFAULT_SWITCH_TYPE_GENERATE \
BETTER_ENUMS__REGULAR_ENUM_SWITCH_TYPE_GENERATE
#endif
#ifdef BETTER_ENUMS__HAVE_CONSTEXPR
#ifdef BETTER_ENUMS_CONSTEXPR_TO_STRING
# define BETTER_ENUMS__DEFAULT_TRIM_STRINGS_ARRAYS \
BETTER_ENUMS__CXX11_FULL_CONSTEXPR_TRIM_STRINGS_ARRAYS
# define BETTER_ENUMS__DEFAULT_TO_STRING_KEYWORD \
BETTER_ENUMS__CONSTEXPR_TO_STRING_KEYWORD
# define BETTER_ENUMS__DEFAULT_DECLARE_INITIALIZE \
BETTER_ENUMS__DO_NOT_DECLARE_INITIALIZE
# define BETTER_ENUMS__DEFAULT_DEFINE_INITIALIZE \
BETTER_ENUMS__DO_NOT_DEFINE_INITIALIZE
# define BETTER_ENUMS__DEFAULT_CALL_INITIALIZE \
BETTER_ENUMS__DO_NOT_CALL_INITIALIZE
#else
# define BETTER_ENUMS__DEFAULT_TRIM_STRINGS_ARRAYS \
BETTER_ENUMS__CXX11_PARTIAL_CONSTEXPR_TRIM_STRINGS_ARRAYS
# define BETTER_ENUMS__DEFAULT_TO_STRING_KEYWORD \
BETTER_ENUMS__NO_CONSTEXPR_TO_STRING_KEYWORD
# define BETTER_ENUMS__DEFAULT_DECLARE_INITIALIZE \
BETTER_ENUMS__DO_DECLARE_INITIALIZE
# define BETTER_ENUMS__DEFAULT_DEFINE_INITIALIZE \
BETTER_ENUMS__DO_DEFINE_INITIALIZE
# define BETTER_ENUMS__DEFAULT_CALL_INITIALIZE \
BETTER_ENUMS__DO_CALL_INITIALIZE
#endif
#define ENUM(Enum, Underlying, ...) \
BETTER_ENUMS__ID(BETTER_ENUMS__TYPE( \
BETTER_ENUMS__CXX11_UNDERLYING_TYPE, \
BETTER_ENUMS__DEFAULT_SWITCH_TYPE, \
BETTER_ENUMS__DEFAULT_SWITCH_TYPE_GENERATE, \
BETTER_ENUMS__DEFAULT_TRIM_STRINGS_ARRAYS, \
BETTER_ENUMS__DEFAULT_TO_STRING_KEYWORD, \
BETTER_ENUMS__DEFAULT_DECLARE_INITIALIZE, \
BETTER_ENUMS__DEFAULT_DEFINE_INITIALIZE, \
BETTER_ENUMS__DEFAULT_CALL_INITIALIZE, \
Enum, Underlying, __VA_ARGS__))
#define SLOW_ENUM(Enum, Underlying, ...) \
BETTER_ENUMS__ID(BETTER_ENUMS__TYPE( \
BETTER_ENUMS__CXX11_UNDERLYING_TYPE, \
BETTER_ENUMS__DEFAULT_SWITCH_TYPE, \
BETTER_ENUMS__DEFAULT_SWITCH_TYPE_GENERATE, \
BETTER_ENUMS__CXX11_FULL_CONSTEXPR_TRIM_STRINGS_ARRAYS, \
BETTER_ENUMS__CONSTEXPR_TO_STRING_KEYWORD, \
BETTER_ENUMS__DO_NOT_DECLARE_INITIALIZE, \
BETTER_ENUMS__DO_NOT_DEFINE_INITIALIZE, \
BETTER_ENUMS__DO_NOT_CALL_INITIALIZE, \
Enum, Underlying, __VA_ARGS__))
#else
#define ENUM(Enum, Underlying, ...) \
BETTER_ENUMS__ID(BETTER_ENUMS__TYPE( \
BETTER_ENUMS__CXX98_UNDERLYING_TYPE, \
BETTER_ENUMS__DEFAULT_SWITCH_TYPE, \
BETTER_ENUMS__DEFAULT_SWITCH_TYPE_GENERATE, \
BETTER_ENUMS__CXX98_TRIM_STRINGS_ARRAYS, \
BETTER_ENUMS__NO_CONSTEXPR_TO_STRING_KEYWORD, \
BETTER_ENUMS__DO_DECLARE_INITIALIZE, \
BETTER_ENUMS__DO_DEFINE_INITIALIZE, \
BETTER_ENUMS__DO_CALL_INITIALIZE, \
Enum, Underlying, __VA_ARGS__))
#endif
#endif // #ifndef BETTER_ENUMS__ENUM_H