Eliminated non-integral underlying types.

This was an experimental feature. Removing it to simplify maintenance.
This commit is contained in:
Anton Bachin 2015-10-04 18:27:56 -05:00
parent c1d4a1c006
commit 9a2389cd15
10 changed files with 27 additions and 936 deletions

View File

@ -53,11 +53,9 @@ the rich enums that are missing from standard C++.
- Stream operators.
- Does not use the heap and can be compiled with exceptions disabled, for use in
minimal freestanding environments.
- The underlying type [does not have to be an integral type][underlying].
[testing]: http://aantron.github.io/better-enums/CompilerSupport.html
[performance]: http://aantron.github.io/better-enums/Performance.html
[underlying]: http://aantron.github.io/better-enums/demo/NonIntegralUnderlyingTypes.html
## Limitations

View File

@ -51,15 +51,6 @@ passed by value.
All names declared in the scope of a Better Enum are prefixed with an underscore
in order to avoid conflicts with potential constant names.
If you are using [non-integral underlying types][non-integral], you need to be
aware of section of this reference on underlying types. However, if you are
using a regular, integral underlying type, the type `Enum::_underlying` is the
same `Enum::_integral`, and each of the `*_underlying` functions is the same as
the corresponding `*_integral` function, so you can safely ignore that whole
section.
[non-integral]: ${prefix}demo/NonIntegralUnderlyingTypes.html
### Running example
@ -394,136 +385,6 @@ as [`_from_string`](#_from_string). In case of failure, sets the stream's
### Non-integral underlying type
This section is relevant only if you are using an underlying type that is not
an integral type — otherwise, `Enum::_underlying` is the same as
`Enum::_integral`, and all the functions described here are redundant with their
corresponding functions in the [section on integer conversions][integral].
That section is written for the simple, but common case where the underlying
type is an integral type, in order to avoid overloading users not using the
feature described here with unnecessary generality. The information in that
section is fully accurate for integral underlying types, but for non-integral
underlying types this section corrects it.
[integral]: #IntegerConversion
The rest of this section will assume that your non-integral underlying type is
called `Underlying`.
In this case, the memory representation of your Better Enum type is the same as
for `Underlying`. In fact, this is always true — the memory representation
is always the same as for the underlying type. It is only a matter of whether
that type is integral or not.
When `Underlying` is not integral, Better Enums still needs an integral
representation of `Underlying` for use in `switch`. That is the true meaning of
the member type `_integral`. It's just that when `Underlying` *is* integral to
begin with, it is its own integral representation, and the two types collapse.
To support non-integral underlying types, Better Enums requires a two-way
mapping between `Underlying` and some type `_integral`. In case `Underlying`
*is* integral, however, that mapping is simply the identity function. Otherwise,
you have to supply a mapping as shown [here][non-integral].
In short, the underlying type is "first-class," whether it is integral or not,
and the type `_integral` is a helper type. When `Underlying` *is* integral, the
various `*_integral` functions just happen to work with the underlying type, as
a special case. The material in this section is for the general case where
`Underlying` is not integral.
#### <em>typename _underlying</em>
`Enum::_underlying` is the same type as `Underlying`. It has to satisfy the
requirements given [here][non-integral].
#### non-member specialization <em>struct better_enums::integral_mapping</em>&lt;Underlying&gt;
You should specialize this template for `Underlying`, as shown in the
[example][non-integral]. The specialization needs the following members:
- A type `integral_representation`, which gives an integral type that Better
Enums will use to make `Underlying` compatible with `switch` statements, and
to define an ordering on the generated Better Enums type. This type is *not*
the internal representation of the Better Enum &mdash; the Better Enum's
memory representation is the same as `Underlying`.
- A function
`constexpr static integral_representation to_integral(const Underlying&)`.
- A function
`constexpr static Underlying from_integral(integral_representation)`.
In $cxx98, the above functions don't have to be `constexpr`.
You can avoid specializing this template, but its default implementation puts
additional requirements on `Underlying` in order to be able to define default
versions of `to_integral` and `from_integral`:
- `Underlying` must have a member type `integral_representation`, with the same
meaning as above.
- `Underlying` must have a conversion
`constexpr operator integral_representation() const`.
- `Underlying` must have a constructor
`constexpr Underlying(integral_representation)`. This constructor can be
explicit.
Again, in $cxx98, these members don't have to be `constexpr`.
#### member constexpr _underlying <em>_to_underlying</em>() const
No-op conversion of a Better Enum to its underlying type. Behaves as
[`_to_integral`](#_to_integral), except that the text concerning implicit
conversions is irrelevant when `_underlying` is not the same as `_integral`.
Implicit conversions, if not disabled, are always to `_integral`.
#### static constexpr Enum <em>_from_underlying</em>(_underlying)
Same as [`_from_integral`](#_from_integral), but for the underlying type. In
fact, `from_integral` is a wrapper that first converts the integer to a value of
the underlying type (a no-op when the types are equal), and then calls
`_from_underlying`.
#### static constexpr optional<Enum> <em>_from_underlying_nothrow</em>(_underlying)
Same as [`_from_integral_nothrow`](#_from_integral_nothrow), but for the
underlying type. `_from_integral_nothrow` is a wrapper as described
[above](#_from_underlying).
#### static constexpr Enum <em>_from_underlying_unchecked</em>(_underlying)
Same as [`_from_integral_unchecked`](#_from_integral), but for the underlying
type. `_from_integral_unchecked` is a wrapper as described
[above](#_from_underlying).
#### static constexpr bool <em>_is_valid(_underlying)</em>
Replaces [`_is_valid(_integral)`](#_is_valid_integral). In fact, *this* function
is the only one defined, but in the case where `_integral` is `_underlying`,
this function's signature is equivalent to
[`_is_valid(_integral)`](#_is_valid_integral).
#### static constexpr _value_iterable <em>_values &mdash; _underlying[]</em>()
Collection of declared enum values, stored in memory as instances of the
underlying type.
Replaces [`_values`](#_values), the collection of integral values of declared
constants. In fact, this is the only member defined &mdash; in the case where
`_integral` is the same as `_underlying`, the definition in the section on
integer conversions is equivalent to this one.
#### member constexpr const _underlying& <em>operator *</em>() const
Returns a reference to the wrapped underlying value. There is also a non-`const`
version.
#### member constexpr const _underlying* <em>operator -&gt;</em>() const
Returns a pointer to the wrapped underlying value that is suitable for member
access, if `_underlying` has members.
%% class = api
%% description = Detailed description of the Better Enums API.

View File

@ -186,13 +186,6 @@ However, it also introduces some difficulties.
type, similar to how it is done [here][infer], but that will not be suitable
for all contexts, and the user may be surprised by ambiguous resolution error
messages when it is not.
- The experimental feature presented [here][underlying] would be questionable in
the traits interpretation. It is still possible to have a non-integral
underlying type with traits, but it would be strange if the traits version of
the macro accepted a non-integral underlying type, and then declared the
actual language enum with an integral underlying type. Even though that is
exactly what the non-traits version does, there the language enum is hidden
inside the generated type, instead of being exposed alongside a traits type.
- Scoped constants are lost for $cxx98 unless Better Enums again wraps them in a
generated type, though it will be more lightweight than a full Better Enum of
the non-traits approach.
@ -272,7 +265,6 @@ Choosing this approach has serious drawbacks.
[traits]: #Traits
[implicit]: ${prefix}OptInFeatures.html#StrictConversions
[infer]: ${prefix}demo/SpecialValues.html
[underlying]: ${prefix}demo/NonIntegralUnderlyingTypes.html
[traits-branch]: $repo/tree/traits
[traits-samples]: $repo/tree/traits/samples

View File

@ -1,117 +0,0 @@
## General underlying types
The underlying type of a Better Enum doesn't have to be an integral type. It can
be any literal type `T`, as long as you provide a `constexpr` two-way mapping
between `T` and an integral type of your choosing. It also works in $cxx98,
though, of course, `T` doesn't have to be literal and the mapping doesn't have
to be `constexpr` &mdash; everything will be done by Better Enums at run time.
Doing this enables the following usage:
// The type. A color triplet.
<em>struct html_color {
uint8_t r, g, b;
constexpr html_color(uint8_t _r, uint8_t g, uint8_t b) :
r(_r), g(_g), b(_b) { }
};</em>
// The enum.
<em>BETTER_ENUM(Color, html_color,
darksalmon = 0xc47451, purplemimosa = 0x9e7bff,
slimegreen = 0xbce954)</em>
// The usage.
<em>Color c = Color::darksalmon;
std::cout << "Red component: " << c->r << std::endl;
switch (c) {
case Color::darksalmon: // ...
case Color::purplemimosa: // ...
case Color::slimegreen: // ...
}</em>
As you can see, you can have an enumerated set of any literal type, and safely
use the values in `switch`, with the compiler checking exhaustiveness. You can
also access the type's members using the `enum->underlying_member` syntax.
You do have to supply the mapping to an integral type, however. One option is:
// The mapping. It just stuffs bits.
<em>template <>
struct ::better_enums::underlying_traits<html_color> {
using integral_representation = unsigned int;
constexpr static html_color from_integral(unsigned int i)
{ return html_color(i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff); }
constexpr static unsigned int to_integral(html_color c)
{ return (unsigned int)c.r << 16 | (unsigned int)c.g << 8 | c.b; }
};</em>
### Using constructors in initializers
The declaration above used only numeric initializers. It is possible to use the
type's own constructors, provided the type has a `constexpr` conversion to your
chosen integral type:
// The type.
<em>struct html_color {
uint8_t r, g, b;
constexpr html_color(uint8_t _r, uint8_t g, uint8_t b) :
r(_r), g(_g), b(_b) { }
</em>// This is new:<em>
constexpr operator unsigned int() const
{ return (unsigned int)r << 16 | (unsigned int)g << 8 | b; }
};</em>
// The enum.
<em>BETTER_ENUM(Color, html_color,
darksalmon = 0xc47451, purplemimosa = 0x9e7bff,
slimegreen = 0xbce954,
celeste = html_color(0x50, 0xeb, 0xec))</em>
This is not possible at all in $cxx98, however.
### Letting the compiler enumerate your literal type
You don't have to use initializers. For example, as long as your example type
`file_descriptor` knows how to deal with the values, you can have the compiler
generate them in sequence:
<em>BETTER_ENUM(FD, file_descriptor,
STDIN, STDOUT, STDERR, SomePipeYourDaemonHas, ...)</em>
SAMPLE
You can see the code "in action" in the [test case][test]. Be aware that it's
not very "nice," because it uses conditional compilation to run under multiple
compilers. I haven't written a clean sample or documentation yet simply because
this feature is in a very early stage of development.
[test]: $repo/blob/master/test/cxxtest/underlying.h
### Discussion
This feature is still semi-experimental, though I expect it to remain stable,
except perhaps that I will make it possible to infer the type
`integral_representation`.
Any opinions are welcome.
- The main reason Better Enums needs you to supply and explicit mapping is
because it can't just get the "bits" of objects of underlying type in
`constexpr` code. Both `reinterpret_cast` and union abuse seem to be forbidden
in `constexpr` functions.
- There is currently no way to have two different integral representaitons for
the same underlying type in different enums. I don't think that's a major use
case at this point, however.
%% description = "Using Better Enums with non-integral underlying types."

View File

@ -1,172 +0,0 @@
## Non-integral underlying types
The underlying type of a Better Enum doesn't have to be an integral type. It can
be any literal type `T`, as long as you provide a `constexpr` two-way mapping
between `T` and an integral type of your choosing. This also works in $cxx98
&mdash; though then, of course, `T` doesn't have to be literal and the mapping
doesn't have to be `constexpr`. In $cxx98, everything involving `T` will simply
be done by Better Enums at run time.
This feature is semi-experimental. I am considering relaxing the requirements on
`T` so that it doesn't have to be literal. I can use a `reinterpret_cast` to
make a mapping automatically. This will make non-integral underlying types
easier to use, but will also prevent usage at compile time, which unfortunately
has structural consequences for the implementation of Better Enums, and
additional semantic consequences for usage, even at run time.
In the meantime, here's how to have a non-integral underlying type in the
current version.
#include <iostream>
#include <enum.h>
typedef unsigned char uint8_t; // <cstdint> not in C++98.
// The underlying type. A color triplet.
<em>struct html_color {
uint8_t r, g, b;
constexpr html_color(uint8_t _r, uint8_t _g, uint8_t _b) :
r(_r), g(_g), b(_b) { }
};</em>
// The mapping. It just stuffs bits to get the same effect as
// reinterpret_cast, except reinterpret_cast is not available in constexpr
// functions, so we have to write the bit manipulations out. On modern
// C++11 compilers, you don't have to enter the better_enums namespace like
// this - you can just do
// struct ::better_enums::integral_mapping<html_color> { ...
namespace better_enums {
<em>template <>
struct integral_mapping<html_color> {
using integral_representation = unsigned int;
constexpr static html_color from_integral(unsigned int i)
{ return html_color(i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff); }
constexpr static unsigned int to_integral(html_color c)
{ return (unsigned int)c.r << 16 | (unsigned int)c.g << 8 | c.b; }
};</em>
}
// The enum itself.
<em>BETTER_ENUM(Color, html_color,
darksalmon = 0xc47451, purplemimosa = 0x9e7bff,
slimegreen = 0xbce954)</em>
Now, we can do:
int main()
{
<em>Color color = Color::darksalmon</em>;
std::cout << std::hex;
std::cout << "Red component: " << <em>(int)color->r</em> << std::endl;
std::cout << "Green component: " << <em>(int)color->g</em> << std::endl;
std::cout << "Blue component: " << <em>(int)color->b</em> << std::endl;
std::cout << <em>color._to_string()</em> << std::endl;
<em>switch (color)</em> {
<em>case Color::darksalmon</em>: return 0;
<em>case Color::purplemimosa</em>: return 1;
<em>case Color::slimegreen</em>: return 2;
}
return 0;
}
This prints each component, the name of the color (`"darksalmon"`), and then
exits from the `switch` with status 0.
### Constructors in initializers
The above declaration used only numbers in initializers, but it is actually
possible to use constructors of `html_color`. We have to add a `constexpr`
converting operator directly to `html_color`, however:
~~~comment
struct better_html_color {
uint8_t r, g, b;
constexpr better_html_color(uint8_t _r, uint8_t _g, uint8_t _b) :
r(_r), g(_g), b(_b) { }
<em>// This is new:
constexpr operator unsigned int() const
{ return (unsigned int)r << 16 | (unsigned int)g << 8 | b; }</em>
};
namespace better_enums {
template <>
struct integral_mapping<better_html_color> {
using integral_representation = unsigned int;
constexpr static better_html_color from_integral(unsigned int i)
{
return better_html_color(i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff);
}
constexpr static unsigned int to_integral(better_html_color c)
{ return (unsigned int)c.r << 16 | (unsigned int)c.g << 8 | c.b; }
};
}
~~~
This allows:
~~~comment
BETTER_ENUM(BetterColor, better_html_color,
darksalmon = 0xc47451, purplemimosa = 0x9e7bff,
slimegreen = 0xbce954,
<em>celeste = better_html_color(0x50, 0xeb, 0xec)</em>)
~~~
If you can't edit your literal type to add this converting operator, or don't
want to for type safety reasons, you can achieve a similar effect by declaring
an intermediate type `U` that `html_color` can convert to, that can convert to
the integral type. Then, cast your constructor call to `U`. The type `U` is for
declarations only.
Constructors in initializers require $cxx11. Also, g++ doesn't support this
before 5.1.
### Letting the compiler enumerate your type
Of course, as long as the values are valid, you can let the compiler enumerate
your type as in a regular enum, by omitting initializers:
~~~comment
<em>BETTER_ENUM(FD, file_descriptor,
STDIN, STDOUT, STDERR, SomePipeYourDaemonHas, ...)</em>
~~~
Here, `FD::STDIN` maps to the integral representation 0, `STDOUT` to 1, and so
on.
### Discussion
This feature is still semi-experimental, though I expect it to remain stable,
except perhaps that I will make it possible to infer the type
`integral_representation`.
Any opinions are welcome.
- The main reason Better Enums needs you to supply and explicit mapping is
because it can't just get the "bits" of objects of underlying type in
`constexpr` code. Both `reinterpret_cast` and union abuse seem to be forbidden
in `constexpr` functions.
- There is currently no way to have two different integral representaitons for
the same underlying type in different enums. I don't think that's a major use
case at this point, however.
%% description = "Using Better Enums with non-integral underlying types."

View File

@ -163,14 +163,6 @@ Try it live online in
</li>
<li class="zero-mod-two one-mod-three">
<strong>Non-integral underlying types</strong>
<em>
Have sets of named, <code>switch</code>-friendly constants of any literal
type.
</em>
</li>
<li class="one-mod-two two-mod-three">
<strong>Free and open source</strong>
<em>
Released under the BSD license for use in any project, free or commercial.

159
enum.h
View File

@ -25,21 +25,11 @@
# if !defined(__EXCEPTIONS) || !__has_feature(cxx_exceptions)
# define BETTER_ENUMS__NO_EXCEPTIONS
# endif
# if __cplusplus >= 201103L
# 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
# ifndef __EXCEPTIONS
# define BETTER_ENUMS__NO_EXCEPTIONS
@ -48,9 +38,6 @@
#endif
#ifdef _MSC_VER
# if _MSC_VER >= 1600
# define BETTER_ENUMS__HAVE_LONG_LONG
# endif
# ifndef _CPPUNWIND
# define BETTER_ENUMS__NO_EXCEPTIONS
# endif
@ -484,56 +471,6 @@ inline void _trim_names(const char * const *raw_names,
// General underlying types.
// 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 integral_mapping {
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); }
};
// Eager initialization.
template <typename Enum>
struct _initialize_at_program_start {
@ -618,13 +555,6 @@ constexpr const char *_final_ ## index = \
// The enums proper.
// TODO Convert integral to underlying only at the last possible moment, if the
// integral value is valid. This should prevent unexpected behavior (conversions
// during scans).
// TODO Choose the right return type semantics once the _values array
// representation is chosen: values if integral, const references if underlying.
#define BETTER_ENUMS__NS(EnumType) better_enums::_data_ ## EnumType
#ifdef BETTER_ENUMS__VC2008_WORKAROUNDS
@ -656,45 +586,21 @@ class Enum { \
private: \
typedef ::better_enums::optional<Enum> _optional; \
typedef ::better_enums::optional<std::size_t> _optional_index; \
typedef ::better_enums::integral_mapping<Underlying> _mapping; \
\
public: \
typedef Underlying _underlying; \
typedef _mapping::integral_representation _integral; \
typedef Underlying _integral; \
\
enum _enumerated SetUnderlyingType(Underlying) { __VA_ARGS__ }; \
\
BETTER_ENUMS__CONSTEXPR Enum(_enumerated value) : \
_value(_mapping::from_integral(value)) { } \
BETTER_ENUMS__CONSTEXPR Enum(_enumerated value) : _value(value) { } \
\
BETTER_ENUMS__COPY_CONSTRUCTOR(Enum) \
\
BETTER_ENUMS__CONSTEXPR operator SwitchType(Enum)() const \
{ \
return (SwitchType(Enum))_mapping::to_integral(_value); \
return SwitchType(Enum)(_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__IF_EXCEPTIONS( \
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__IF_EXCEPTIONS( \
BETTER_ENUMS__CONSTEXPR static Enum _from_integral(_integral value); \
@ -717,7 +623,7 @@ class Enum { \
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(_integral value); \
BETTER_ENUMS__CONSTEXPR static bool _is_valid(const char *name); \
BETTER_ENUMS__CONSTEXPR static bool _is_valid_nocase(const char *name); \
\
@ -736,18 +642,18 @@ class Enum { \
BETTER_ENUMS__CONSTEXPR static _value_iterable _values(); \
ToStringConstexpr static _name_iterable _names(); \
\
_underlying _value; \
_integral _value; \
\
private: \
Enum() : _value(_mapping::from_integral(0)) { } \
Enum() : _value(0) { } \
\
explicit BETTER_ENUMS__CONSTEXPR Enum(const _underlying &value) : \
explicit BETTER_ENUMS__CONSTEXPR Enum(const _integral &value) : \
_value(value) { } \
\
DeclareInitialize \
\
BETTER_ENUMS__CONSTEXPR static _optional_index \
_from_value_loop(const _underlying &value, std::size_t index = 0); \
_from_value_loop(_integral 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 \
@ -778,34 +684,9 @@ operator +(Enum::_enumerated enumerated) \
return (Enum)enumerated; \
} \
\
BETTER_ENUMS__IF_EXCEPTIONS( \
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 _mapping::to_integral(_value); \
return _integral(_value); \
} \
\
BETTER_ENUMS__CONSTEXPR inline Enum \
@ -826,7 +707,9 @@ BETTER_ENUMS__CONSTEXPR inline Enum Enum::_from_integral(_integral value) \
BETTER_ENUMS__CONSTEXPR inline Enum::_optional \
Enum::_from_integral_nothrow(_integral value) \
{ \
return _from_underlying_nothrow(_mapping::from_integral(value)); \
return \
::better_enums::_map_index<Enum>(BETTER_ENUMS__NS(Enum)::_value_array, \
_from_value_loop(value)); \
} \
\
ToStringConstexpr inline const char* Enum::_to_string() const \
@ -873,7 +756,7 @@ Enum::_from_string_nocase_nothrow(const char *name) \
_from_string_nocase_loop(name)); \
} \
\
BETTER_ENUMS__CONSTEXPR inline bool Enum::_is_valid(const _underlying &value) \
BETTER_ENUMS__CONSTEXPR inline bool Enum::_is_valid(_integral value) \
{ \
return _from_value_loop(value); \
} \
@ -908,13 +791,13 @@ ToStringConstexpr inline Enum::_name_iterable Enum::_names() \
DefineInitialize(Enum) \
\
BETTER_ENUMS__CONSTEXPR inline Enum::_optional_index \
Enum::_from_value_loop(const Enum::_underlying &value, std::size_t index) \
Enum::_from_value_loop(Enum::_integral value, std::size_t index) \
{ \
return \
index == _size() ? _optional_index() : \
_mapping::to_integral( \
BETTER_ENUMS__NS(Enum)::_value_array[index]._value) \
== _mapping::to_integral(value) ? _optional_index(index) : \
index == _size() ? \
_optional_index() : \
BETTER_ENUMS__NS(Enum)::_value_array[index]._value == value ? \
_optional_index(index) : \
_from_value_loop(value, index + 1); \
} \
\
@ -962,7 +845,7 @@ BETTER_ENUMS__CONSTEXPR inline bool operator >=(const Enum &a, const Enum &b) \
// C++11
#define BETTER_ENUMS__CXX11_UNDERLYING_TYPE(Underlying) \
: ::better_enums::integral_mapping<Underlying>::integral_representation
: Underlying
// C++98, C++11
#define BETTER_ENUMS__REGULAR_ENUM_SWITCH_TYPE(Type) \
@ -977,9 +860,7 @@ BETTER_ENUMS__CONSTEXPR inline bool operator >=(const Enum &a, const Enum &b) \
// C++11
#define BETTER_ENUMS__ENUM_CLASS_SWITCH_TYPE_GENERATE(Underlying, ...) \
enum class _EnumClassForSwitchStatements : \
::better_enums::integral_mapping<Underlying>::integral_representation \
{ __VA_ARGS__ };
enum class _EnumClassForSwitchStatements : Underlying { __VA_ARGS__ };
// C++98
#define BETTER_ENUMS__CXX98_TRIM_STRINGS_ARRAYS(Enum, ...) \

View File

@ -1,164 +0,0 @@
// This file was generated automatically.
// Non-integral underlying types
//
// The underlying type of a Better Enum doesn't have to be an integral type. It
// can be any literal type T, as long as you provide a constexpr two-way mapping
// between T and an integral type of your choosing. This also works in C++98 -
// though then, of course, T doesn't have to be literal and the mapping doesn't
// have to be constexpr. In C++98, everything involving T will simply be done by
// Better Enums at run time.
//
// This feature is semi-experimental. I am considering relaxing the requirements
// on T so that it doesn't have to be literal. I can use a reinterpret_cast to
// make a mapping automatically. This will make non-integral underlying types
// easier to use, but will also prevent usage at compile time, which
// unfortunately has structural consequences for the implementation of Better
// Enums, and additional semantic consequences for usage, even at run time.
//
// In the meantime, here's how to have a non-integral underlying type in the
// current version.
#include <iostream>
#include <enum.h>
typedef unsigned char uint8_t; // <cstdint> not in C++98.
// The underlying type. A color triplet.
struct html_color {
uint8_t r, g, b;
constexpr html_color(uint8_t _r, uint8_t _g, uint8_t _b) :
r(_r), g(_g), b(_b) { }
};
// The mapping. It just stuffs bits to get the same effect as
// reinterpret_cast, except reinterpret_cast is not available in constexpr
// functions, so we have to write the bit manipulations out. On modern
// C++11 compilers, you don't have to enter the better_enums namespace like
// this - you can just do
// struct ::better_enums::integral_mapping<html_color> { ...
namespace better_enums {
template <>
struct integral_mapping<html_color> {
using integral_representation = unsigned int;
constexpr static html_color from_integral(unsigned int i)
{ return html_color(i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff); }
constexpr static unsigned int to_integral(html_color c)
{ return (unsigned int)c.r << 16 | (unsigned int)c.g << 8 | c.b; }
};
}
// The enum itself.
BETTER_ENUM(Color, html_color,
darksalmon = 0xc47451, purplemimosa = 0x9e7bff,
slimegreen = 0xbce954)
// Now, we can do:
int main()
{
Color color = Color::darksalmon;
std::cout << std::hex;
std::cout << "Red component: " << (int)color->r << std::endl;
std::cout << "Green component: " << (int)color->g << std::endl;
std::cout << "Blue component: " << (int)color->b << std::endl;
std::cout << color._to_string() << std::endl;
switch (color) {
case Color::darksalmon: return 0;
case Color::purplemimosa: return 1;
case Color::slimegreen: return 2;
}
return 0;
}
// This prints each component, the name of the color ("darksalmon"), and then
// exits from the switch with status 0.
//
// Constructors in initializers
//
// The above declaration used only numbers in initializers, but it is actually
// possible to use constructors of html_color. We have to add a constexpr
// converting operator directly to html_color, however:
//
// struct better_html_color {
// uint8_t r, g, b;
//
// constexpr better_html_color(uint8_t _r, uint8_t _g, uint8_t _b) :
// r(_r), g(_g), b(_b) { }
//
// // This is new:
// constexpr operator unsigned int() const
// { return (unsigned int)r << 16 | (unsigned int)g << 8 | b; }
// };
//
// namespace better_enums {
//
// template <>
// struct integral_mapping<better_html_color> {
// using integral_representation = unsigned int;
//
// constexpr static better_html_color from_integral(unsigned int i)
// {
// return better_html_color(i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff);
// }
//
// constexpr static unsigned int to_integral(better_html_color c)
// { return (unsigned int)c.r << 16 | (unsigned int)c.g << 8 | c.b; }
// };
//
// }
//
// This allows:
//
// BETTER_ENUM(BetterColor, better_html_color,
// darksalmon = 0xc47451, purplemimosa = 0x9e7bff,
// slimegreen = 0xbce954,
// celeste = better_html_color(0x50, 0xeb, 0xec))
//
// If you can't edit your literal type to add this converting operator, or don't
// want to for type safety reasons, you can achieve a similar effect by
// declaring an intermediate type U that html_color can convert to, that can
// convert to the integral type. Then, cast your constructor call to U. The type
// U is for declarations only.
//
// Constructors in initializers require C++11. Also, g++ doesn't support this
// before 5.1.
//
// Letting the compiler enumerate your type
//
// Of course, as long as the values are valid, you can let the compiler
// enumerate your type as in a regular enum, by omitting initializers:
//
// BETTER_ENUM(FD, file_descriptor,
// STDIN, STDOUT, STDERR, SomePipeYourDaemonHas, ...)
//
// Here, FD::STDIN maps to the integral representation 0, STDOUT to 1, and so
// on.
//
// Discussion
//
// This feature is still semi-experimental, though I expect it to remain stable,
// except perhaps that I will make it possible to infer the type
// integral_representation.
//
// Any opinions are welcome.
//
// 1. The main reason Better Enums needs you to supply and explicit mapping is
// because it can't just get the "bits" of objects of underlying type in
// constexpr code. Both reinterpret_cast and union abuse seem to be
// forbidden in constexpr functions.
// 2. There is currently no way to have two different integral representaitons
// for the same underlying type in different enums. I don't think that's a
// major use case at this point, however.

View File

@ -112,14 +112,14 @@ endforeach(TEST)
set(EXAMPLES
1-hello-world 2-conversions 3-iterate 4-switch 6-iostreams 7-safety
8-representation 9-constexpr 101-special-values 102-any-underlying
103-bitset 104-quine 105-c++17-reflection)
set(SKIPPED_FOR_CXX98
5-map 9-constexpr 101-special-values 102-any-underlying 103-bitset 104-quine
8-representation 9-constexpr 101-special-values 103-bitset 104-quine
105-c++17-reflection)
set(SKIPPED_FOR_STRICT_CONVERSION 4-switch 102-any-underlying)
set(SKIPPED_FOR_CXX98
5-map 9-constexpr 101-special-values 103-bitset 104-quine
105-c++17-reflection)
set(SKIPPED_FOR_STRICT_CONVERSION 4-switch)
if(CONFIGURATION STREQUAL CXX98 OR NOT SUPPORTS_CONSTEXPR)
list(REMOVE_ITEM EXAMPLES ${SKIPPED_FOR_CXX98})

View File

@ -1,180 +0,0 @@
#include <cxxtest/TestSuite.h>
#include <enum.h>
// This test uses internal constants declared by feature testing macros in
// enum.h. They are not part of the public interface, but are used here to avoid
// duplication of definitions for C++11 and C++98.
//
// BETTER_ENUMS__CONSTEXPR is a synonym for constexpr when available, and
// othewise expands to nothing.
// Using a type trait.
struct html_color_1 {
unsigned char r, g, b;
BETTER_ENUMS__CONSTEXPR
html_color_1(unsigned char _r, unsigned char _g, unsigned char _b) :
r(_r), g(_g), b(_b) { }
};
// In C++11, can simply use struct ::better_enums::integral_mapping below,
// instead of having to enclose the specialization in the namespace.
namespace better_enums {
template <>
struct integral_mapping<html_color_1> {
typedef unsigned int integral_representation;
BETTER_ENUMS__CONSTEXPR static html_color_1 from_integral(unsigned int i)
{ return html_color_1(i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff); }
BETTER_ENUMS__CONSTEXPR static unsigned int to_integral(html_color_1 v)
{ return (unsigned int)v.r << 16 | (unsigned int)v.g << 8 | v.b; }
};
}
BETTER_ENUM(HtmlColor1, html_color_1,
darksalmon = 0xc47451, purplemimosa = 0x9e7bff,
slimegreen = 0xbce954)
// Using member definitions.
struct html_color_2 {
unsigned char r, g, b;
BETTER_ENUMS__CONSTEXPR
html_color_2(unsigned char _r, unsigned char _g, unsigned char _b) :
r(_r), g(_g), b(_b) { }
typedef unsigned int integral_representation;
explicit BETTER_ENUMS__CONSTEXPR html_color_2(unsigned int i) :
r(i >> 16 & 0xff), g(i >> 8 & 0xff), b(i & 0xff) { }
BETTER_ENUMS__CONSTEXPR operator unsigned int() const
{ return (unsigned int)r << 16 | (unsigned int)g << 8 | b; }
};
// Initializers are not supported before GCC 5.1, even with a literal type and
// constexpr conversion to integer.
#ifdef BETTER_ENUMS__HAVE_CONSTEXPR
# ifdef __GNUC__
# ifdef __clang__
# define SUPPORT_INITIALIZER
# else
# if __GNUC__ >= 5
# define SUPPORT_INITIALIZER
# endif
# endif
# endif
#endif
#ifdef SUPPORT_INITIALIZER
BETTER_ENUM(HtmlColor2, html_color_2,
darksalmon = 0xc47451, purplemimosa = 0x9e7bff,
slimegreen = 0xbce954, celeste = html_color_2(0x50, 0xeb, 0xec))
#else
BETTER_ENUM(HtmlColor2, html_color_2,
darksalmon = 0xc47451, purplemimosa = 0x9e7bff,
slimegreen = 0xbce954)
#endif
class UnderlyingTypeTests : public CxxTest::TestSuite {
public:
void test_basics_and_operators()
{
HtmlColor1 color_1 = HtmlColor1::darksalmon;
TS_ASSERT_EQUALS(color_1->r, 0xc4);
TS_ASSERT_EQUALS(color_1->g, 0x74);
TS_ASSERT_EQUALS(color_1->b, 0x51);
TS_ASSERT_EQUALS(sizeof(HtmlColor1), sizeof(html_color_1));
HtmlColor2 color_2 = HtmlColor2::purplemimosa;
TS_ASSERT_EQUALS(color_2->r, 0x9e);
TS_ASSERT_EQUALS(color_2->g, 0x7b);
TS_ASSERT_EQUALS(color_2->b, 0xff);
TS_ASSERT_EQUALS(sizeof(HtmlColor2), sizeof(html_color_2));
#ifdef SUPPORT_INITIALIZER
HtmlColor2 color_3 = HtmlColor2::celeste;
TS_ASSERT_EQUALS(color_3->r, 0x50);
TS_ASSERT_EQUALS(color_3->g, 0xeb);
TS_ASSERT_EQUALS(color_3->b, 0xec);
#endif
color_1->r = 0;
const HtmlColor1 const_color = HtmlColor1::slimegreen;
TS_ASSERT_EQUALS(const_color->r, 0xbc);
html_color_1 &color_1_reference = *color_1;
const html_color_1 &const_color_reference = *const_color;
TS_ASSERT_EQUALS(color_1_reference.r, 0);
TS_ASSERT_EQUALS(const_color_reference.r, 0xbc);
}
void test_conversion_functions()
{
HtmlColor2 color_1 = HtmlColor2::darksalmon;
html_color_2 color_1_underlying = color_1._to_underlying();
TS_ASSERT_EQUALS(color_1_underlying.r, color_1->r);
TS_ASSERT_EQUALS(color_1_underlying.g, color_1->g);
TS_ASSERT_EQUALS(color_1_underlying.b, color_1->b);
color_1_underlying = html_color_2(HtmlColor2::slimegreen);
color_1 = HtmlColor2::_from_underlying(color_1_underlying);
TS_ASSERT_EQUALS(color_1->r, color_1_underlying.r);
TS_ASSERT_EQUALS(color_1->g, color_1_underlying.g);
TS_ASSERT_EQUALS(color_1->b, color_1_underlying.b);
color_1_underlying = html_color_2(1, 2, 3);
color_1 = HtmlColor2::_from_underlying_unchecked(color_1_underlying);
TS_ASSERT_EQUALS(color_1->r, color_1_underlying.r);
TS_ASSERT_EQUALS(color_1->g, color_1_underlying.g);
TS_ASSERT_EQUALS(color_1->b, color_1_underlying.b);
}
void test_switch()
{
#ifndef BETTER_ENUMS_STRICT_CONVERSION
HtmlColor1 html_color_1 = HtmlColor1::purplemimosa;
switch (html_color_1) {
case HtmlColor1::darksalmon: TS_ASSERT(false); break;
case HtmlColor1::purplemimosa: TS_ASSERT(true); break;
case HtmlColor1::slimegreen: TS_ASSERT(false); break;
}
#else
# ifdef BETTER_ENUMS__HAVE_CONSTEXPR
HtmlColor1 html_color_1 = HtmlColor1::purplemimosa;
switch (html_color_1) {
case +HtmlColor1::darksalmon: TS_ASSERT(false); break;
case +HtmlColor1::purplemimosa: TS_ASSERT(true); break;
case +HtmlColor1::slimegreen: TS_ASSERT(false); break;
}
# endif
#endif
}
};