mirror of
https://github.com/aantron/better-enums.git
synced 2025-12-07 01:06:42 +08:00
Eliminated non-integral underlying types.
This was an experimental feature. Removing it to simplify maintenance.
This commit is contained in:
parent
c1d4a1c006
commit
9a2389cd15
@ -53,11 +53,9 @@ the rich enums that are missing from standard C++.
|
|||||||
- Stream operators.
|
- Stream operators.
|
||||||
- Does not use the heap and can be compiled with exceptions disabled, for use in
|
- Does not use the heap and can be compiled with exceptions disabled, for use in
|
||||||
minimal freestanding environments.
|
minimal freestanding environments.
|
||||||
- The underlying type [does not have to be an integral type][underlying].
|
|
||||||
|
|
||||||
[testing]: http://aantron.github.io/better-enums/CompilerSupport.html
|
[testing]: http://aantron.github.io/better-enums/CompilerSupport.html
|
||||||
[performance]: http://aantron.github.io/better-enums/Performance.html
|
[performance]: http://aantron.github.io/better-enums/Performance.html
|
||||||
[underlying]: http://aantron.github.io/better-enums/demo/NonIntegralUnderlyingTypes.html
|
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
|
|||||||
@ -51,15 +51,6 @@ passed by value.
|
|||||||
All names declared in the scope of a Better Enum are prefixed with an underscore
|
All names declared in the scope of a Better Enum are prefixed with an underscore
|
||||||
in order to avoid conflicts with potential constant names.
|
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
|
### 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><Underlying>
|
|
||||||
|
|
||||||
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 — 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 — _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 — 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 -></em>() const
|
|
||||||
|
|
||||||
Returns a pointer to the wrapped underlying value that is suitable for member
|
|
||||||
access, if `_underlying` has members.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
%% class = api
|
%% class = api
|
||||||
|
|
||||||
%% description = Detailed description of the Better Enums API.
|
%% description = Detailed description of the Better Enums API.
|
||||||
|
|||||||
@ -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
|
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
|
for all contexts, and the user may be surprised by ambiguous resolution error
|
||||||
messages when it is not.
|
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
|
- 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
|
generated type, though it will be more lightweight than a full Better Enum of
|
||||||
the non-traits approach.
|
the non-traits approach.
|
||||||
@ -272,7 +265,6 @@ Choosing this approach has serious drawbacks.
|
|||||||
[traits]: #Traits
|
[traits]: #Traits
|
||||||
[implicit]: ${prefix}OptInFeatures.html#StrictConversions
|
[implicit]: ${prefix}OptInFeatures.html#StrictConversions
|
||||||
[infer]: ${prefix}demo/SpecialValues.html
|
[infer]: ${prefix}demo/SpecialValues.html
|
||||||
[underlying]: ${prefix}demo/NonIntegralUnderlyingTypes.html
|
|
||||||
[traits-branch]: $repo/tree/traits
|
[traits-branch]: $repo/tree/traits
|
||||||
[traits-samples]: $repo/tree/traits/samples
|
[traits-samples]: $repo/tree/traits/samples
|
||||||
|
|
||||||
|
|||||||
@ -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` — 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."
|
|
||||||
@ -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
|
|
||||||
— 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."
|
|
||||||
@ -163,14 +163,6 @@ Try it live online in
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="zero-mod-two one-mod-three">
|
<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>
|
<strong>Free and open source</strong>
|
||||||
<em>
|
<em>
|
||||||
Released under the BSD license for use in any project, free or commercial.
|
Released under the BSD license for use in any project, free or commercial.
|
||||||
|
|||||||
159
enum.h
159
enum.h
@ -25,21 +25,11 @@
|
|||||||
# if !defined(__EXCEPTIONS) || !__has_feature(cxx_exceptions)
|
# if !defined(__EXCEPTIONS) || !__has_feature(cxx_exceptions)
|
||||||
# define BETTER_ENUMS__NO_EXCEPTIONS
|
# define BETTER_ENUMS__NO_EXCEPTIONS
|
||||||
# endif
|
# endif
|
||||||
# if __cplusplus >= 201103L
|
|
||||||
# define BETTER_ENUMS__HAVE_LONG_LONG
|
|
||||||
# define BETTER_ENUMS__HAVE_NEW_CHAR_TYPES
|
|
||||||
# endif
|
|
||||||
# else
|
# else
|
||||||
# if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L
|
# if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L
|
||||||
# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6))
|
# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6))
|
||||||
# define BETTER_ENUMS__HAVE_CONSTEXPR
|
# define BETTER_ENUMS__HAVE_CONSTEXPR
|
||||||
# endif
|
# 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
|
||||||
# ifndef __EXCEPTIONS
|
# ifndef __EXCEPTIONS
|
||||||
# define BETTER_ENUMS__NO_EXCEPTIONS
|
# define BETTER_ENUMS__NO_EXCEPTIONS
|
||||||
@ -48,9 +38,6 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# if _MSC_VER >= 1600
|
|
||||||
# define BETTER_ENUMS__HAVE_LONG_LONG
|
|
||||||
# endif
|
|
||||||
# ifndef _CPPUNWIND
|
# ifndef _CPPUNWIND
|
||||||
# define BETTER_ENUMS__NO_EXCEPTIONS
|
# define BETTER_ENUMS__NO_EXCEPTIONS
|
||||||
# endif
|
# 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.
|
// Eager initialization.
|
||||||
template <typename Enum>
|
template <typename Enum>
|
||||||
struct _initialize_at_program_start {
|
struct _initialize_at_program_start {
|
||||||
@ -618,13 +555,6 @@ constexpr const char *_final_ ## index = \
|
|||||||
|
|
||||||
// The enums proper.
|
// 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
|
#define BETTER_ENUMS__NS(EnumType) better_enums::_data_ ## EnumType
|
||||||
|
|
||||||
#ifdef BETTER_ENUMS__VC2008_WORKAROUNDS
|
#ifdef BETTER_ENUMS__VC2008_WORKAROUNDS
|
||||||
@ -656,45 +586,21 @@ class Enum { \
|
|||||||
private: \
|
private: \
|
||||||
typedef ::better_enums::optional<Enum> _optional; \
|
typedef ::better_enums::optional<Enum> _optional; \
|
||||||
typedef ::better_enums::optional<std::size_t> _optional_index; \
|
typedef ::better_enums::optional<std::size_t> _optional_index; \
|
||||||
typedef ::better_enums::integral_mapping<Underlying> _mapping; \
|
|
||||||
\
|
\
|
||||||
public: \
|
public: \
|
||||||
typedef Underlying _underlying; \
|
typedef Underlying _integral; \
|
||||||
typedef _mapping::integral_representation _integral; \
|
|
||||||
\
|
\
|
||||||
enum _enumerated SetUnderlyingType(Underlying) { __VA_ARGS__ }; \
|
enum _enumerated SetUnderlyingType(Underlying) { __VA_ARGS__ }; \
|
||||||
\
|
\
|
||||||
BETTER_ENUMS__CONSTEXPR Enum(_enumerated value) : \
|
BETTER_ENUMS__CONSTEXPR Enum(_enumerated value) : _value(value) { } \
|
||||||
_value(_mapping::from_integral(value)) { } \
|
|
||||||
\
|
\
|
||||||
BETTER_ENUMS__COPY_CONSTRUCTOR(Enum) \
|
BETTER_ENUMS__COPY_CONSTRUCTOR(Enum) \
|
||||||
\
|
\
|
||||||
BETTER_ENUMS__CONSTEXPR operator SwitchType(Enum)() const \
|
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__CONSTEXPR _integral _to_integral() const; \
|
||||||
BETTER_ENUMS__IF_EXCEPTIONS( \
|
BETTER_ENUMS__IF_EXCEPTIONS( \
|
||||||
BETTER_ENUMS__CONSTEXPR static Enum _from_integral(_integral value); \
|
BETTER_ENUMS__CONSTEXPR static Enum _from_integral(_integral value); \
|
||||||
@ -717,7 +623,7 @@ class Enum { \
|
|||||||
BETTER_ENUMS__CONSTEXPR static _optional \
|
BETTER_ENUMS__CONSTEXPR static _optional \
|
||||||
_from_string_nocase_nothrow(const char *name); \
|
_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(const char *name); \
|
||||||
BETTER_ENUMS__CONSTEXPR static bool _is_valid_nocase(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(); \
|
BETTER_ENUMS__CONSTEXPR static _value_iterable _values(); \
|
||||||
ToStringConstexpr static _name_iterable _names(); \
|
ToStringConstexpr static _name_iterable _names(); \
|
||||||
\
|
\
|
||||||
_underlying _value; \
|
_integral _value; \
|
||||||
\
|
\
|
||||||
private: \
|
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) { } \
|
_value(value) { } \
|
||||||
\
|
\
|
||||||
DeclareInitialize \
|
DeclareInitialize \
|
||||||
\
|
\
|
||||||
BETTER_ENUMS__CONSTEXPR static _optional_index \
|
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 \
|
BETTER_ENUMS__CONSTEXPR static _optional_index \
|
||||||
_from_string_loop(const char *name, std::size_t index = 0); \
|
_from_string_loop(const char *name, std::size_t index = 0); \
|
||||||
BETTER_ENUMS__CONSTEXPR static _optional_index \
|
BETTER_ENUMS__CONSTEXPR static _optional_index \
|
||||||
@ -778,34 +684,9 @@ operator +(Enum::_enumerated enumerated) \
|
|||||||
return (Enum)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 \
|
BETTER_ENUMS__CONSTEXPR inline Enum::_integral Enum::_to_integral() const \
|
||||||
{ \
|
{ \
|
||||||
return _mapping::to_integral(_value); \
|
return _integral(_value); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
BETTER_ENUMS__CONSTEXPR inline Enum \
|
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 \
|
BETTER_ENUMS__CONSTEXPR inline Enum::_optional \
|
||||||
Enum::_from_integral_nothrow(_integral value) \
|
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 \
|
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)); \
|
_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); \
|
return _from_value_loop(value); \
|
||||||
} \
|
} \
|
||||||
@ -908,13 +791,13 @@ ToStringConstexpr inline Enum::_name_iterable Enum::_names() \
|
|||||||
DefineInitialize(Enum) \
|
DefineInitialize(Enum) \
|
||||||
\
|
\
|
||||||
BETTER_ENUMS__CONSTEXPR inline Enum::_optional_index \
|
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 \
|
return \
|
||||||
index == _size() ? _optional_index() : \
|
index == _size() ? \
|
||||||
_mapping::to_integral( \
|
_optional_index() : \
|
||||||
BETTER_ENUMS__NS(Enum)::_value_array[index]._value) \
|
BETTER_ENUMS__NS(Enum)::_value_array[index]._value == value ? \
|
||||||
== _mapping::to_integral(value) ? _optional_index(index) : \
|
_optional_index(index) : \
|
||||||
_from_value_loop(value, index + 1); \
|
_from_value_loop(value, index + 1); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
@ -962,7 +845,7 @@ BETTER_ENUMS__CONSTEXPR inline bool operator >=(const Enum &a, const Enum &b) \
|
|||||||
|
|
||||||
// C++11
|
// C++11
|
||||||
#define BETTER_ENUMS__CXX11_UNDERLYING_TYPE(Underlying) \
|
#define BETTER_ENUMS__CXX11_UNDERLYING_TYPE(Underlying) \
|
||||||
: ::better_enums::integral_mapping<Underlying>::integral_representation
|
: Underlying
|
||||||
|
|
||||||
// C++98, C++11
|
// C++98, C++11
|
||||||
#define BETTER_ENUMS__REGULAR_ENUM_SWITCH_TYPE(Type) \
|
#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
|
// C++11
|
||||||
#define BETTER_ENUMS__ENUM_CLASS_SWITCH_TYPE_GENERATE(Underlying, ...) \
|
#define BETTER_ENUMS__ENUM_CLASS_SWITCH_TYPE_GENERATE(Underlying, ...) \
|
||||||
enum class _EnumClassForSwitchStatements : \
|
enum class _EnumClassForSwitchStatements : Underlying { __VA_ARGS__ };
|
||||||
::better_enums::integral_mapping<Underlying>::integral_representation \
|
|
||||||
{ __VA_ARGS__ };
|
|
||||||
|
|
||||||
// C++98
|
// C++98
|
||||||
#define BETTER_ENUMS__CXX98_TRIM_STRINGS_ARRAYS(Enum, ...) \
|
#define BETTER_ENUMS__CXX98_TRIM_STRINGS_ARRAYS(Enum, ...) \
|
||||||
|
|||||||
@ -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.
|
|
||||||
@ -112,14 +112,14 @@ endforeach(TEST)
|
|||||||
|
|
||||||
set(EXAMPLES
|
set(EXAMPLES
|
||||||
1-hello-world 2-conversions 3-iterate 4-switch 6-iostreams 7-safety
|
1-hello-world 2-conversions 3-iterate 4-switch 6-iostreams 7-safety
|
||||||
8-representation 9-constexpr 101-special-values 102-any-underlying
|
8-representation 9-constexpr 101-special-values 103-bitset 104-quine
|
||||||
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
|
|
||||||
105-c++17-reflection)
|
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)
|
if(CONFIGURATION STREQUAL CXX98 OR NOT SUPPORTS_CONSTEXPR)
|
||||||
list(REMOVE_ITEM EXAMPLES ${SKIPPED_FOR_CXX98})
|
list(REMOVE_ITEM EXAMPLES ${SKIPPED_FOR_CXX98})
|
||||||
|
|||||||
@ -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
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Loading…
x
Reference in New Issue
Block a user