mirror of
https://github.com/aantron/better-enums.git
synced 2025-12-06 08:46:42 +08:00
Updated documentation.
This commit is contained in:
parent
d90bfd6f18
commit
f5f669277a
17
README.md
17
README.md
@ -43,13 +43,13 @@ The interface is the same for C++98 — you just have to use most of it at
|
|||||||
run time only. This library does provide scoped and sized enums, something not
|
run time only. This library does provide scoped and sized enums, something not
|
||||||
built into C++98.
|
built into C++98.
|
||||||
|
|
||||||
See the [project page][project] for full documentation and [here][tutorial] for
|
See the [project page][project] for full documentation and [here][wandbox] for
|
||||||
a simple working program.
|
a simple working program.
|
||||||
|
|
||||||
[max]: http://aantron.github.io/better-enums/demo/BitSets.html
|
[max]: http://aantron.github.io/better-enums/demo/BitSets.html
|
||||||
[enforce]: http://aantron.github.io/better-enums/demo/SpecialValues.html
|
[enforce]: http://aantron.github.io/better-enums/demo/SpecialValues.html
|
||||||
[project]: http://aantron.github.io/better-enums
|
[project]: http://aantron.github.io/better-enums
|
||||||
[tutorial]: http://aantron.github.io/better-enums/tutorial/HelloWorld.html
|
[wandbox]: http://melpon.org/wandbox/permlink/pdlAAGoxnjqG6FRI
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@ -67,14 +67,19 @@ enums that are missing from standard C++.
|
|||||||
compiler as much as [just including `iostream` does][performance].
|
compiler as much as [just including `iostream` does][performance].
|
||||||
- Use any initializers and sparse ranges, just like with a built-in enum.
|
- Use any initializers and sparse ranges, just like with a built-in enum.
|
||||||
- Guaranteed size and alignment — you choose the representation type.
|
- Guaranteed size and alignment — you choose the representation type.
|
||||||
|
- Stream operators supported.
|
||||||
|
- Does not use the heap and can be compiled with exceptions disabled, for use in
|
||||||
|
minimal freestanding environments.
|
||||||
|
- The underlying type [can be an object type][underlying].
|
||||||
|
|
||||||
[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
|
||||||
|
|
||||||
The biggest current limitation is that the `ENUM` macro can't be used inside a
|
The biggest limitation is that the `ENUM` macro can't be used inside a class.
|
||||||
class. This seems difficult to remove, but I am looking into it. In the
|
This seems [difficult to remove][nested]. There is a workaround with `typedef`
|
||||||
meantime, there is a workaround with a `typedef` (or `using`):
|
(or `using`):
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
ENUM(UniquePrefix_Color, uint8_t, Red, Green, Blue)
|
ENUM(UniquePrefix_Color, uint8_t, Red, Green, Blue)
|
||||||
@ -89,6 +94,8 @@ triplet::Color color;
|
|||||||
|
|
||||||
You can, however, use `ENUM` inside a namespace.
|
You can, however, use `ENUM` inside a namespace.
|
||||||
|
|
||||||
|
[nested]: http://aantron.github.io/better-enums/DesignDecisionsFAQ.html#NoEnumInsideClass
|
||||||
|
|
||||||
## Contact
|
## Contact
|
||||||
|
|
||||||
Don't hesitate to contact me about features or bugs:
|
Don't hesitate to contact me about features or bugs:
|
||||||
|
|||||||
@ -51,6 +51,15 @@ 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
|
||||||
@ -150,9 +159,14 @@ using <em>optional</em> = <em>better_enums::optional</em><<em>Enum</em>>;
|
|||||||
The types and members described here have to do with the sequence of constants
|
The types and members described here have to do with the sequence of constants
|
||||||
declared, i.e. `A`, `B`, `C` in the [running example](#RunningExample).
|
declared, i.e. `A`, `B`, `C` in the [running example](#RunningExample).
|
||||||
|
|
||||||
#### static constexpr size_t <em>_size</em>
|
#### static constexpr size_t <em>_size</em>()
|
||||||
|
|
||||||
The number of constants declared. `Enum::_size == 3`.
|
The number of constants declared. `Enum::_size() == 3`.
|
||||||
|
|
||||||
|
#### static constexpr const size_t <em>_size_constant</em>
|
||||||
|
|
||||||
|
Same as [`_size`](#_size), but a constant instead of a function. This is
|
||||||
|
provided for use in $cxx98 constant expressions.
|
||||||
|
|
||||||
#### <em>typedef _value_iterable</em>
|
#### <em>typedef _value_iterable</em>
|
||||||
|
|
||||||
@ -255,7 +269,7 @@ case as in [`_from_string_nocase`](#_from_string_nocase).
|
|||||||
Evaluates to the name of the Better Enum type. `Enum::_name()` is the same
|
Evaluates to the name of the Better Enum type. `Enum::_name()` is the same
|
||||||
string as `"Enum"`.
|
string as `"Enum"`.
|
||||||
|
|
||||||
#### typedef <em>_name_iterable</em>
|
#### <em>typedef _name_iterable</em>
|
||||||
|
|
||||||
Type of object that permits iteration over names of declared constants. Has at
|
Type of object that permits iteration over names of declared constants. Has at
|
||||||
least `constexpr` `begin()`, `end()`, and `size()` methods. `operator[]` is also
|
least `constexpr` `begin()`, `end()`, and `size()` methods. `operator[]` is also
|
||||||
@ -263,7 +277,7 @@ available, but is `constexpr` if and only if [`_to_string`](#_to_string) is
|
|||||||
`constexpr`. Iteration visits constants in order of declaration. See usage
|
`constexpr`. Iteration visits constants in order of declaration. See usage
|
||||||
example under [`_names`](#_names).
|
example under [`_names`](#_names).
|
||||||
|
|
||||||
#### typedef <em>_name_iterator</em>
|
#### <em>typedef _name_iterator</em>
|
||||||
|
|
||||||
Random-access iterator type for `_name_iterable`. Most operations are
|
Random-access iterator type for `_name_iterable`. Most operations are
|
||||||
`constexpr`, but dereferencing is `constexpr` if and only if
|
`constexpr`, but dereferencing is `constexpr` if and only if
|
||||||
@ -322,7 +336,7 @@ example,
|
|||||||
(+<em>Enum::C</em>)<em>._to_integral</em>() == <em>2</em>
|
(+<em>Enum::C</em>)<em>._to_integral</em>() == <em>2</em>
|
||||||
|
|
||||||
Note that Better Enums are already implicitly convertible to their underlying
|
Note that Better Enums are already implicitly convertible to their underlying
|
||||||
integral types [by default](${prefix}OptInFeatures.html#StrictConversion).
|
integral types [by default](${prefix}OptInFeatures.html#StrictConversions).
|
||||||
You may still want to use this function, however, for clarity, and to ensure
|
You may still want to use this function, however, for clarity, and to ensure
|
||||||
that your code remains compatible if the strict conversions feature is enabled
|
that your code remains compatible if the strict conversions feature is enabled
|
||||||
later.
|
later.
|
||||||
@ -365,6 +379,151 @@ constants.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Stream operators
|
||||||
|
|
||||||
|
#### non-member std::ostream& <em>operator <<</em>(std::ostream&, const Enum&)
|
||||||
|
|
||||||
|
Formats the given enum to the given stream in the same way as
|
||||||
|
[`_to_string`](#_to_string).
|
||||||
|
|
||||||
|
#### non-member std::istream& <em>operator >></em>(std::istream&, Enum&)
|
||||||
|
|
||||||
|
Reads from the given stream and attempts to parse an enum value in the same way
|
||||||
|
as [`_from_string`](#_from_string). In case of failure, sets the stream's
|
||||||
|
`failbit`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
## Compiler support
|
## Compiler support
|
||||||
|
|
||||||
Better Enums aims to support all major compilers. It is known to definitely work
|
Better Enums aims to support all major compilers. It is known to work on:
|
||||||
on
|
|
||||||
|
|
||||||
- clang 3.3 to 3.6
|
- clang 3.3 to 3.6
|
||||||
- gcc 4.3 to 5.1
|
- gcc 4.3 to 5.1
|
||||||
@ -121,4 +120,6 @@ g++43 -std=c++0x -DBETTER_ENUMS_NO_CONSTEXPR
|
|||||||
g++43 -std=c++98
|
g++43 -std=c++98
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
%% description = Information about compiler support and feature detection.
|
%% description =
|
||||||
|
Better Enums compiler support, compatibility, feature detection, and automated
|
||||||
|
testing.
|
||||||
|
|||||||
@ -8,9 +8,11 @@
|
|||||||
I also watch the `enums` tag on Stack Overflow.
|
I also watch the `enums` tag on Stack Overflow.
|
||||||
|
|
||||||
I'm happy to hear any feedback. If you have any trouble using the library or
|
I'm happy to hear any feedback. If you have any trouble using the library or
|
||||||
parsing the documentation, please don't hesitate to let me know.
|
parsing the documentation, please don't hesitate to let me know. I'd also be
|
||||||
|
interested to hear about your use case, if you are willing to share :)
|
||||||
|
|
||||||
And, if you find this library helpful, give it a star on GitHub to let me know
|
And, if you find this library helpful, give it a star on GitHub to let me know
|
||||||
you use it. It will help keep me encouraged :)
|
you use it. It will help keep me encouraged :)
|
||||||
|
|
||||||
%% description = Contact information for bugs, issues, support, and feedback.
|
%% description =
|
||||||
|
Contact information for Better Enums bugs, issues, support, and feedback.
|
||||||
|
|||||||
250
doc/DesignDecisionsFAQ.md
Normal file
250
doc/DesignDecisionsFAQ.md
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
## Design decisions FAQ
|
||||||
|
|
||||||
|
$be pushes at the edges of what is possible in standard $cxx, and I've had to
|
||||||
|
make some difficult decisions as a result. You can imagine the set of
|
||||||
|
potential reflective enum implementations as a space, with axes such as "concise
|
||||||
|
syntax," "uniform interface," "compilation speed," "run-time performance," and
|
||||||
|
so on. As is typical in engineering, the constraints are such that as you move
|
||||||
|
to extremes along one axis, you have to retreat along others — for
|
||||||
|
example, some desirable aspects of concise syntax conflict with having a uniform
|
||||||
|
interface, which is nonetheless good for teachability, and compile-time
|
||||||
|
performance is, in some ways, at odds with run-time performance.
|
||||||
|
|
||||||
|
So, there are many variations possible on $be, and, in fact, I have tried and
|
||||||
|
maintained a few. This page describes how I chose the one that is published.
|
||||||
|
The choices are debatable, but I am attempting to record the debate. Hopefully,
|
||||||
|
this will either convince you that I have made a good choice, or, if you
|
||||||
|
disagree, you will have a good starting point for discussion, or even for
|
||||||
|
implementing an alternative.
|
||||||
|
|
||||||
|
I am always looking for new arguments and approaches. If you have an idea,
|
||||||
|
comment, criticism, please do [let me know][contact].
|
||||||
|
|
||||||
|
$internal_toc
|
||||||
|
|
||||||
|
### Why do enum members have underscores?
|
||||||
|
|
||||||
|
Enum members such as `_to_string` occupy the same scope as the names of
|
||||||
|
constants declared by the user. I chose to prefix members with underscores to
|
||||||
|
lessen the chances of collision. For example, take `_valid`, and suppose it was
|
||||||
|
`valid` instead. That would make this enum impossible:
|
||||||
|
|
||||||
|
<em>ENUM(Status, char, valid, invalid)</em>
|
||||||
|
|
||||||
|
because the constant `Status::valid` would clash with the member
|
||||||
|
`Status::valid`.
|
||||||
|
|
||||||
|
Of course, users could try to declare constants with underscores as well, but I
|
||||||
|
find it easier to ask users not to do that, rather than ask them to worry about
|
||||||
|
a potentially growing set of reserved names, which they wouldn't fully know even
|
||||||
|
for a single version of Better Enums, without frequently looking at the API
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
Alternatives to this involve separating the namespaces occupied by members and
|
||||||
|
constants. I don't think increasing nesting is an option, since nobody wants to
|
||||||
|
write `Status::values::valid` or `Status::operations::valid`. I don't think
|
||||||
|
moving constants out of `Status` is a good idea, since scoped constants is a
|
||||||
|
feature of Better Enums, which is especially important for $cxx98 usage.
|
||||||
|
|
||||||
|
This leaves the possibility of moving the operations performed by the members
|
||||||
|
into traits types, i.e. something like `traits<Status>::valid`. That is an
|
||||||
|
interesting option, and it has [its own section][traits]. I have tried it, but
|
||||||
|
the verbosity increase is much greater than the benefit of dropping underscores,
|
||||||
|
so I chose not to do it.
|
||||||
|
|
||||||
|
%% description = Better Enums design decisions and tradeoffs.
|
||||||
|
|
||||||
|
### Why does Better Enums use a macro at all?
|
||||||
|
|
||||||
|
Better Enums needs to turn the names of declared constants into strings, and I
|
||||||
|
don't believe there is any way to do this in standard $cxx except by using the
|
||||||
|
preprocessor's macro parameter stringization operator (`#`). So, at the top
|
||||||
|
level, Better Enums has to provide a macro. I am, however, trying to keep the
|
||||||
|
user-facing macros to a minimum; in particular there is only one.
|
||||||
|
|
||||||
|
I think that's the best that is possible. Furthermore, apart from the macro
|
||||||
|
itself, the declaration looks very similar to a $cxx11 `enum` declaration, with
|
||||||
|
an underlying type, comma-separated constant list, and the same support for
|
||||||
|
initializers as built-in enums. So, I am trying to keep even this one macro out
|
||||||
|
of the user's way. I wouldn't accept any change that involved turning the
|
||||||
|
declaration into a preprocessor sequence or tuple, i.e. something like
|
||||||
|
|
||||||
|
<em>ENUM(Channel, int, (Red)(Green)((Blue)(5)))</em>
|
||||||
|
|
||||||
|
even if it promised extra capabilities.
|
||||||
|
|
||||||
|
Better Enums uses additional macros internally, for two main purposes: to do the
|
||||||
|
actual work of stringizing the declared constants and assembling them into
|
||||||
|
arrays, and to configure itself by detecting which compiler it is running on.
|
||||||
|
|
||||||
|
I am not a fan of gratuitous macros, but in these cases they are unavoidable,
|
||||||
|
and, indeed, I am grateful for the stringization operator.
|
||||||
|
|
||||||
|
<a id="NoEnumInsideClass"></a>
|
||||||
|
### Why is it not possible to declare a Better Enum inside a class?
|
||||||
|
|
||||||
|
This is due to an interaction between linkage and `constexpr`.
|
||||||
|
|
||||||
|
1. Better Enums is a header-only library that declares arrays with static
|
||||||
|
storage, such as the array of constant names for each declared enum. Such
|
||||||
|
arrays can be declared in namespace scope, in class scope, or in function
|
||||||
|
scope, but they also need to be defined somewhere.
|
||||||
|
|
||||||
|
If `ENUM` is to be usable in both namespace and class scope, it already can't
|
||||||
|
assume that it can declare arrays in namespace scope, since if `ENUM` is used
|
||||||
|
in a class, its entire expansion will be enclosed in the declaration of that
|
||||||
|
class.
|
||||||
|
|
||||||
|
That leaves class scope and function scope. If the arrays are declared in
|
||||||
|
class scope, there needs to be a separate definition of them in some
|
||||||
|
translation unit. This is too burdensome for the user, because the separate
|
||||||
|
definition would involve repetition of the constants in the macro parameter
|
||||||
|
list, creating exactly the type of maintenance problem that Better Enums is
|
||||||
|
trying to eliminate.
|
||||||
|
|
||||||
|
Function scope is the only viable option remaining after considering linkage
|
||||||
|
constraints. Each array can be wrapped in a static function, which has a
|
||||||
|
static local variable, which is initialized with the array. The functions can
|
||||||
|
be called to get references to the arrays.
|
||||||
|
|
||||||
|
However....
|
||||||
|
|
||||||
|
2. These arrays need to be accessible to `constexpr` code in $cxx11, and
|
||||||
|
`constexpr` functions are not allowed to have static local variables.
|
||||||
|
|
||||||
|
Ironically, this seems like one place where $cxx98 is more "flexible," but only
|
||||||
|
for the reason that compile-time usage of Better Enums is not supported in
|
||||||
|
$cxx98.
|
||||||
|
|
||||||
|
<a id="Traits"></a>
|
||||||
|
### Should Better Enums provide enum "objects" or traits types?
|
||||||
|
|
||||||
|
A Better Enum value is an "object," whose memory representation is the same as
|
||||||
|
its underlying type. For example,
|
||||||
|
|
||||||
|
<em>ENUM(Channel, int, Red, Green, Blue)</em>
|
||||||
|
|
||||||
|
expands to something like
|
||||||
|
|
||||||
|
<em>struct Channel {
|
||||||
|
enum _enumerated : int { Red, Green, Blue };
|
||||||
|
int _value;</em>
|
||||||
|
// Strings array, _to_string, _from_string, etc.
|
||||||
|
<em>};</em>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
There is an alternative interpretation, in which the Better Enums library
|
||||||
|
generates enum traits instead, i.e. the generated arrays and members sit
|
||||||
|
alongside built-in enums instead of wrapping them:
|
||||||
|
|
||||||
|
<em>ENUM(Channel, int, Red, Green, Blue)</em>
|
||||||
|
|
||||||
|
generates
|
||||||
|
|
||||||
|
<em>enum class Channel : int { Red, Green, Blue };</em>
|
||||||
|
|
||||||
|
<em>template <>
|
||||||
|
struct ::better_enums::traits<Channel> {
|
||||||
|
using _enumerated = Channel;</em>
|
||||||
|
// Strings array, to_string, from_string, etc.
|
||||||
|
<em>};</em>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
There are a number of advantages to the traits approach.
|
||||||
|
|
||||||
|
- The underscore prefixes can be dropped from member names, since they no longer
|
||||||
|
share a namespace with user-declared constants.
|
||||||
|
- The interface, at first glance, becomes more uniform, since now every member
|
||||||
|
is a static member of the traits type. Without traits, `_to_string` is a
|
||||||
|
non-static member, while `_from_string` is a static member.
|
||||||
|
- `Channel` is one of the language's own enum types, instead of some mystery
|
||||||
|
type provided by Better Enums. This may make it easier to understand. It also
|
||||||
|
eliminates the problem with different syntaxes for `switch` statements
|
||||||
|
described [here][implicit].
|
||||||
|
|
||||||
|
However, it also introduces some difficulties.
|
||||||
|
|
||||||
|
- The syntax is more verbose, since everything becomes a member of the traits
|
||||||
|
type. For example, instead of `Channel::_from_string()`, you get
|
||||||
|
`better_enums::traits<Channel>::from_string()`. The underscore may be
|
||||||
|
unpleasant, but so far I have preferred the underscore to boilerplate.
|
||||||
|
- The uniform interface ends up getting wrapped behind functions anyway, for the
|
||||||
|
sake of type inference. For example, the "naked" `to_string` function is
|
||||||
|
called as `better_enums::traits<Channel>::to_string(channel)`, which is
|
||||||
|
redundant, because the compiler could infer the type parameter `Channel` if it
|
||||||
|
was the parameter of the function instead of the traits type. So, the obvious
|
||||||
|
thing is to define such a wrapper function, which can then be called as
|
||||||
|
`better_enums::to_string(channel)`. No such function can be easily defined for
|
||||||
|
`from_string` and other `from_*` functions, however, because the type
|
||||||
|
parameters can't be inferred from arguments. So, the signatures of `to_*` and
|
||||||
|
`from_*` functions again effectively diverge, negating this advantage of
|
||||||
|
traits. The closest I can get with wrappers is
|
||||||
|
`better_enums::from_string<Channel>`, which has the same signature only in the
|
||||||
|
formal sense, i.e. modulo the difference in type inference.
|
||||||
|
|
||||||
|
I actually think there is a way to infer the type parameter from the return
|
||||||
|
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.
|
||||||
|
- Traits types are not intuitive for some $cxx users, which would present a
|
||||||
|
barrier to usage.
|
||||||
|
|
||||||
|
Despite the disadvantages listed just above, I consider the traits approach
|
||||||
|
interesting — it's a close call. There is an
|
||||||
|
[out-of-date branch][traits-branch] containing a traits version of Better Enums.
|
||||||
|
You can see some of the usage in its [samples][traits-samples] directory. I may
|
||||||
|
update it from time to time, especially if there is interest.
|
||||||
|
|
||||||
|
### Why does Better Enums use linear scans for lookup?
|
||||||
|
|
||||||
|
It seems that Better Enums needs to use the same algorithms at compile time as
|
||||||
|
at run time, because I have not found a way (and doubt there is one) to
|
||||||
|
determine, during the execution of a `constexpr` function, whether it is
|
||||||
|
executing at compile time or at run time. So, whatever data structures I use to
|
||||||
|
accelerate lookup, I have to generate them at compile time, to be available as
|
||||||
|
early as possible.
|
||||||
|
|
||||||
|
I tried to generate various data structures at compile time, but so far,
|
||||||
|
generation has been too slow. The fastest algorithm so far, a compile-time merge
|
||||||
|
sort based on template parameter packs, took over 100ms to run on the constant
|
||||||
|
set of a large enum. I would have to run three of these per enum — for the
|
||||||
|
constants, for the names, and for the names with case-insensitive comparison.
|
||||||
|
This results in a 300ms slowdown per enum, which is not acceptable, given that
|
||||||
|
on my system the same compiler takes 370ms to process `iostream`, and less than
|
||||||
|
10ms to process an enum without acceleration data structures. Declaring five
|
||||||
|
large enums with accelerated lookup would take 1.5 seconds of compilation time.
|
||||||
|
This doesn't scale to large projects with many translation units.
|
||||||
|
|
||||||
|
I am continuing to look for faster algorithms or better approaches, so faster
|
||||||
|
lookup may be coming to Better Enums in the future.
|
||||||
|
|
||||||
|
So far, I have tried Boost.MPL sort, Eric Niebler's Meta sort, my own selection
|
||||||
|
sort based on `constexpr`, and an insertion and merge sort based on parameter
|
||||||
|
packs. I cannot use (Boost?).Hana sort, because that requires $cxx14. I am also
|
||||||
|
considering various hash table-like data structures, and providing two sets of
|
||||||
|
interfaces for compile-time and run-time usage, which is something I would
|
||||||
|
really rather not have to do. The latter option would be worth considering,
|
||||||
|
however, if I measured a significant improvement in running time from better
|
||||||
|
data structures — something I haven't gotten to yet because there doesn't
|
||||||
|
seem to be a data structure to measure that is not disqualified by the speed of
|
||||||
|
generation.
|
||||||
|
|
||||||
|
[contact]: ${prefix}Contact.html
|
||||||
|
[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
|
||||||
@ -19,7 +19,7 @@ constants of full-`constexpr` enums. To extend:
|
|||||||
example. Add 1 to the number of characters to account for the null
|
example. Add 1 to the number of characters to account for the null
|
||||||
terminator — our numbers are now 512 and 64.
|
terminator — our numbers are now 512 and 64.
|
||||||
2. Get `make_macros.py` from your copy of the full Better Enums distribution
|
2. Get `make_macros.py` from your copy of the full Better Enums distribution
|
||||||
or from <a href="https://raw.githubusercontent.com/aantron/better-enums/$version/script/make_macros.py" download>GitHub</a>.
|
or from <a href="https://raw.githubusercontent.com/aantron/better-enums/$ref/script/make_macros.py" download>GitHub</a>.
|
||||||
3. You will run this script to generate a header file containing some
|
3. You will run this script to generate a header file containing some
|
||||||
replacement macros for `enum.h` to use. Pick a name for this file and a
|
replacement macros for `enum.h` to use. Pick a name for this file and a
|
||||||
location somewhere in your include path. I will assume that this file is
|
location somewhere in your include path. I will assume that this file is
|
||||||
@ -31,10 +31,27 @@ constants of full-`constexpr` enums. To extend:
|
|||||||
|
|
||||||
- For g++ and clang++, `-DBETTER_ENUMS_MACRO_FILE='<common/enum_macros.h>'`
|
- For g++ and clang++, `-DBETTER_ENUMS_MACRO_FILE='<common/enum_macros.h>'`
|
||||||
- For VC++, `\DBETTER_ENUMS_MACRO_FILE='<common/enum_macros.h>'`
|
- For VC++, `\DBETTER_ENUMS_MACRO_FILE='<common/enum_macros.h>'`
|
||||||
|
- With CMake, you may need something like
|
||||||
|
`add_definitions(-DBETTER_ENUMS_MACRO_FILE="$${CMAKE_SOURCE_DIR}/src/enum-macros.h")`
|
||||||
|
|
||||||
6. Enjoy the looser limits. Just watch out — increasing the second
|
6. Enjoy the looser limits. Just watch out — increasing the second
|
||||||
number can really slow down compilation of full-`constexpr` enums.
|
number can really slow down compilation of full-`constexpr` enums.
|
||||||
7. You don't need `make_macros.py` anymore. It's not part of your build
|
7. You don't need `make_macros.py` anymore. It's not part of your build
|
||||||
process and you can delete it.
|
process and you can delete it.
|
||||||
|
|
||||||
%% description = How to extend limits imposed by internal macros.
|
---
|
||||||
|
|
||||||
|
I am paying attention to feedback, so if more than a few users say that the
|
||||||
|
default limit of 64 constants is too low, I will increase it to simplify
|
||||||
|
everyone's command line. The current choice of 64 is basically an arbitrary
|
||||||
|
guess, loosely informed by the following two facts about macro parameter limits:
|
||||||
|
|
||||||
|
- The default limit in Boost.Preprocessor is 64. Though Better Enums does not
|
||||||
|
use Boost, I took this number as a guideline.
|
||||||
|
- The next power of two, 128, is more than [the number Visual C++ supports][vc]
|
||||||
|
(127).
|
||||||
|
|
||||||
|
[vc]: https://msdn.microsoft.com/en-us/library/ft39hh4x.aspx
|
||||||
|
|
||||||
|
%% description =
|
||||||
|
How to extend limits imposed by internal macros in Better Enums.
|
||||||
|
|||||||
114
doc/GeneralUnderlyingTypes.md
Normal file
114
doc/GeneralUnderlyingTypes.md
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
## 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>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>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>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."
|
||||||
@ -64,5 +64,5 @@ Here they are:
|
|||||||
case <em>+</em>Channel::Blue: break;
|
case <em>+</em>Channel::Blue: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
%% description = Opting into features disabled by default for performance or
|
%% description = Optional Better Enums features, disabled by default for
|
||||||
compatibility reasons.
|
performance or compatibility reasons.
|
||||||
|
|||||||
@ -7,9 +7,9 @@ to get a rough idea of how long it takes to compile Better Enums.
|
|||||||
|
|
||||||
The files compared in the test are as follows:
|
The files compared in the test are as follows:
|
||||||
|
|
||||||
- [One file]($repo/blob/$version/test/performance/4-declare_enums.cc) includes
|
- [One file]($repo/blob/$ref/test/performance/4-declare_enums.cc) includes
|
||||||
`enum.h` and declares 647 constants across 36 Better Enums.
|
`enum.h` and declares 647 constants across 36 Better Enums.
|
||||||
- The [other file]($repo/blob/$version/test/performance/5-iostream.cc) *only*
|
- The [other file]($repo/blob/$ref/test/performance/5-iostream.cc) *only*
|
||||||
includes `iostream` and does nothing with it.
|
includes `iostream` and does nothing with it.
|
||||||
|
|
||||||
The argument is that if compiling a bunch of Better Enums is faster, or about as
|
The argument is that if compiling a bunch of Better Enums is faster, or about as
|
||||||
@ -29,4 +29,22 @@ compiled faster.
|
|||||||
- gcc 5.1, full `constexpr`: 4.23
|
- gcc 5.1, full `constexpr`: 4.23
|
||||||
- VC2015RC, $cxx98: 1.18
|
- VC2015RC, $cxx98: 1.18
|
||||||
|
|
||||||
%% description = Compilation performance testing results.
|
The time to merely include `enum.h` vary widely by compiler, with clang being
|
||||||
|
by far the fastest. The ratios to `iostream` are given below.
|
||||||
|
|
||||||
|
- clang 3.6: 0.15
|
||||||
|
- gcc 5.1: 0.77
|
||||||
|
- VC2015RC: 0.82
|
||||||
|
|
||||||
|
On my test machines, clang processed the file in 40ms, gcc took 230ms, and
|
||||||
|
VC2015 took 820ms. The first two are comparable to each other, but VC2015 runs
|
||||||
|
on a different machine.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
In general, I am very sensitive to performance. Better Enums was originally
|
||||||
|
developed in the context of a commercial project where slow running times *and*
|
||||||
|
slow compilation times were unacceptable. I am continuing to develop it in this
|
||||||
|
spirit.
|
||||||
|
|
||||||
|
%% description = Better Enums compilation speed and performance testing results.
|
||||||
|
|||||||
@ -11,6 +11,10 @@ pre, code, samp, h4, .contents ul {
|
|||||||
Consolas, "Liberation Mono", Menlo, "Courier New", Courier, monospace;
|
Consolas, "Liberation Mono", Menlo, "Courier New", Courier, monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1, .splash-text .self, nav .self {
|
||||||
|
font-family: Georgia, Times, "Times New Roman", serif;
|
||||||
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
background-color: #477093;
|
background-color: #477093;
|
||||||
padding: 1.5em 20px;
|
padding: 1.5em 20px;
|
||||||
@ -18,6 +22,8 @@ pre {
|
|||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
color: rgba(255, 255, 255, 0.6);
|
color: rgba(255, 255, 255, 0.6);
|
||||||
font-size: 78%;
|
font-size: 78%;
|
||||||
|
max-width: 84ex;
|
||||||
|
margin-left: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre em {
|
pre em {
|
||||||
@ -45,21 +51,23 @@ code, samp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
max-width: 760px;
|
margin-left: 150px;
|
||||||
margin-left: 230px;
|
margin-right: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1220px) {
|
@media (max-width: 1400px) {
|
||||||
.container {
|
.container {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
width: 1100px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 780px) {
|
@media (max-width: 1120px) {
|
||||||
.container {
|
.container {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
width: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,6 +87,17 @@ nav, .spacer {
|
|||||||
background-color: #222;
|
background-color: #222;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nav > * > span {
|
||||||
|
float: right;
|
||||||
|
opacity: 0.9;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav .self {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
nav a {
|
nav a {
|
||||||
margin-right: 2em;
|
margin-right: 2em;
|
||||||
}
|
}
|
||||||
@ -109,7 +128,33 @@ header {
|
|||||||
h1 {
|
h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 60px;
|
font-size: 60px;
|
||||||
font-weight: 100;
|
font-weight: normal;
|
||||||
|
text-shadow: -2px 2px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
header section {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
header section.buttons, header section.notes {
|
||||||
|
float: right;
|
||||||
|
margin-top: 1.75em;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header section.notes {
|
||||||
|
max-width: 20em;
|
||||||
|
font-size: 75%;
|
||||||
|
margin-right: 10px;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
header section.notes p {
|
||||||
|
margin: 0.35em 0.35em;
|
||||||
|
}
|
||||||
|
|
||||||
|
header section.notes code {
|
||||||
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
header h2 {
|
header h2 {
|
||||||
@ -129,10 +174,41 @@ header h3 {
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.buttons a {
|
||||||
|
display: block;
|
||||||
|
background-color: rgba(255, 255, 255, 0.2) !important;
|
||||||
|
width: 10em;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
padding: 0.25em 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons a:hover {
|
||||||
|
background-color: white !important;
|
||||||
|
color: #395E7E;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1000px) {
|
||||||
|
header .notes {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
header .buttons {
|
||||||
|
margin-right: 2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 660px) {
|
||||||
|
header .buttons {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
margin-top: 2em;
|
margin-top: 2em;
|
||||||
font-size: 36px;
|
font-size: 36px;
|
||||||
font-weight: 100;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
@ -142,9 +218,9 @@ hr {
|
|||||||
|
|
||||||
footer {
|
footer {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin-top: 150px;
|
margin-top: 100px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
opacity: 0.6;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
@ -215,7 +291,6 @@ span#note:target {
|
|||||||
|
|
||||||
.main h3 {
|
.main h3 {
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
font-weight: 100;
|
|
||||||
margin-top: 2em;
|
margin-top: 2em;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
@ -288,15 +363,45 @@ li {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.blurbs > li {
|
.blurbs > li {
|
||||||
width: 45%;
|
|
||||||
float: left;
|
float: left;
|
||||||
min-height: 5em;
|
min-height: 5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blurbs > li.even {
|
@media (min-width: 1076px) {
|
||||||
clear: both;
|
.blurbs > li {
|
||||||
margin-left: 3%;
|
width: 28%;
|
||||||
margin-right: 6%;
|
margin-right: 4%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blurbs > li.zero-mod-three {
|
||||||
|
clear: both;
|
||||||
|
margin-left: 2%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1075px) {
|
||||||
|
.blurbs > li {
|
||||||
|
width: 45%;
|
||||||
|
margin-right: 4%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blurbs > li.zero-mod-two {
|
||||||
|
clear: both;
|
||||||
|
margin-left: 2%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 620px) {
|
||||||
|
.blurbs > li {
|
||||||
|
float: none;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blurbs > li.zero-mod-two {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.blurbs strong {
|
.blurbs strong {
|
||||||
@ -311,19 +416,6 @@ li {
|
|||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 620px) {
|
|
||||||
.blurbs > li {
|
|
||||||
float: none;
|
|
||||||
width: 100%;
|
|
||||||
min-height: 3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blurbs > li.even {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.act strong {
|
.act strong {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 120%;
|
font-size: 120%;
|
||||||
@ -342,9 +434,25 @@ li {
|
|||||||
padding-left: 25px;
|
padding-left: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.splash-text {
|
||||||
|
padding-top: 1em;
|
||||||
|
font-size: 150%;
|
||||||
|
font-weight: normal;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.splash-text em {
|
||||||
|
border-bottom: 1px solid #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.splash-text .self {
|
||||||
|
color: #777;
|
||||||
|
letter-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.splash {
|
.splash {
|
||||||
|
float: right;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-left: -10%;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,12 +462,22 @@ li {
|
|||||||
|
|
||||||
.splash pre.left {
|
.splash pre.left {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
color: inherit;
|
color: black;
|
||||||
|
font-weight: bold;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.splash pre.right {
|
.splash pre.right {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1000px) {
|
||||||
|
.splash {
|
||||||
|
float: none;
|
||||||
|
margin-left: -10%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 700px) {
|
@media (max-width: 700px) {
|
||||||
@ -385,6 +503,7 @@ h4 {
|
|||||||
color: #888;
|
color: #888;
|
||||||
padding-top: 1em;
|
padding-top: 1em;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
font-size: 125%;
|
||||||
}
|
}
|
||||||
|
|
||||||
h4 em {
|
h4 em {
|
||||||
@ -394,9 +513,9 @@ h4 em {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.api ul.contents {
|
.api ul.contents {
|
||||||
-webkit-columns: 300px 2;
|
-webkit-columns: 300px 3;
|
||||||
-moz-columns: 300px 2;
|
-moz-columns: 300px 3;
|
||||||
columns: 300px 2;
|
columns: 300px 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.api ul.contents > li {
|
.api ul.contents > li {
|
||||||
@ -407,6 +526,7 @@ h4 em {
|
|||||||
|
|
||||||
.main h3 {
|
.main h3 {
|
||||||
margin-top: 4em;
|
margin-top: 4em;
|
||||||
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3.contents {
|
h3.contents {
|
||||||
|
|||||||
@ -170,5 +170,6 @@ There are many possible variations of these policies, but I think most of them
|
|||||||
can be encoded in a reasonable fashion using the tools Better Enums provides.
|
can be encoded in a reasonable fashion using the tools Better Enums provides.
|
||||||
Enjoy!
|
Enjoy!
|
||||||
|
|
||||||
%% description = Encoding project policies for static enforcement using Better
|
%% description = An example that uses Better Enums compile-time reflection to
|
||||||
Enums.
|
create invalid and default values for each enum, enforced statically by the
|
||||||
|
compiler, for readability and maintainability.
|
||||||
|
|||||||
161
doc/demo/102-any-underlying.md
Normal file
161
doc/demo/102-any-underlying.md
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
## 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.
|
||||||
|
|
||||||
|
Here's how to do it.
|
||||||
|
|
||||||
|
#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>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
|
||||||
|
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>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."
|
||||||
@ -65,5 +65,5 @@ static_assert(max<EFLAGS>()._to_integral() < 32,
|
|||||||
"some bit indices are out of range");
|
"some bit indices are out of range");
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
%% description = Finding the maximum value of a Better Enum constant for use in
|
%% description = Finding the maximum value of a Better Enum for use in declaring
|
||||||
declaring bit set types.
|
statically-sized bit set types.
|
||||||
@ -170,4 +170,6 @@ ENUM(Channel, int, Red = 0, Green = 1, Blue = 2)
|
|||||||
ENUM(Depth, int, TrueColor = 1, HighColor = 0)
|
ENUM(Depth, int, TrueColor = 1, HighColor = 0)
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
%% description = Contrived example that shows static memory allocation.
|
%% description = Have a Better Enum print its own definition. Shows how to
|
||||||
|
compute the amount of memory necessary from the reflective information provided
|
||||||
|
by a Better Enum.
|
||||||
20
doc/docs.py
20
doc/docs.py
@ -87,11 +87,17 @@ def compose_page(relative_path, definitions):
|
|||||||
definitions["class"] = ""
|
definitions["class"] = ""
|
||||||
|
|
||||||
text = templates["page"]
|
text = templates["page"]
|
||||||
text = scrub_comments(text)
|
|
||||||
|
|
||||||
while '$' in text:
|
while True:
|
||||||
text = apply_template(text, definitions)
|
new_text = scrub_comments(text)
|
||||||
text = scrub_comments(text)
|
new_text = re.sub("\$\$", "$$$$", new_text)
|
||||||
|
new_text = apply_template(new_text, definitions)
|
||||||
|
|
||||||
|
if new_text == text:
|
||||||
|
text = apply_template(new_text, definitions)
|
||||||
|
break
|
||||||
|
|
||||||
|
text = new_text
|
||||||
|
|
||||||
text = "<!-- Generated automatically - edit the templates! -->\n\n" + text
|
text = "<!-- Generated automatically - edit the templates! -->\n\n" + text
|
||||||
|
|
||||||
@ -143,7 +149,7 @@ def process_threaded(directory):
|
|||||||
|
|
||||||
source_file = \
|
source_file = \
|
||||||
os.path.splitext(os.path.basename(file))[0] + "." + CXX_EXTENSION
|
os.path.splitext(os.path.basename(file))[0] + "." + CXX_EXTENSION
|
||||||
source_link = "$repo/blob/$version/example/" + source_file
|
source_link = "$repo/blob/$ref/example/" + source_file
|
||||||
|
|
||||||
definitions[directory + "_body"] = definitions["body"]
|
definitions[directory + "_body"] = definitions["body"]
|
||||||
definitions["body"] = templates[directory]
|
definitions["body"] = templates[directory]
|
||||||
@ -183,6 +189,10 @@ def generate_sitemap():
|
|||||||
for url in generated:
|
for url in generated:
|
||||||
text += " <url>\n"
|
text += " <url>\n"
|
||||||
text += " <loc>%s</loc>\n" % url
|
text += " <loc>%s</loc>\n" % url
|
||||||
|
|
||||||
|
if ".html" not in url:
|
||||||
|
text += " <priority>1.0</priority>\n"
|
||||||
|
|
||||||
text += " </url>\n"
|
text += " </url>\n"
|
||||||
|
|
||||||
text += "</urlset>\n"
|
text += "</urlset>\n"
|
||||||
|
|||||||
198
doc/index.md
198
doc/index.md
@ -1,9 +1,3 @@
|
|||||||
Better Enums is a single header file that causes your compiler to generate
|
|
||||||
*reflective* enum types. This makes it easy to translate between enums and
|
|
||||||
strings, and much more.
|
|
||||||
|
|
||||||
Here's how to use a Better Enum:
|
|
||||||
|
|
||||||
<div class="splash">
|
<div class="splash">
|
||||||
<pre class="left">enable
|
<pre class="left">enable
|
||||||
|
|
||||||
@ -11,7 +5,7 @@ declare
|
|||||||
|
|
||||||
|
|
||||||
parse
|
parse
|
||||||
print
|
format
|
||||||
|
|
||||||
|
|
||||||
count
|
count
|
||||||
@ -19,6 +13,7 @@ iterate
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
switch
|
switch
|
||||||
|
|
||||||
|
|
||||||
@ -29,22 +24,25 @@ switch
|
|||||||
safe cast
|
safe cast
|
||||||
|
|
||||||
|
|
||||||
at compile time</pre>
|
during
|
||||||
<pre class="right"><em>#include</em> <<em>enum.h</em>>
|
compilation
|
||||||
|
</pre>
|
||||||
|
<pre class="right"><em>#include <enum.h></em>
|
||||||
|
|
||||||
<em>ENUM</em>(<em>Channel</em>, <em>int</em>, <em>Red</em> = <em>1</em>, <em>Green</em>, <em>Blue</em>)
|
<em>ENUM(Channel, int, Red = 1, Green, Blue)</em>
|
||||||
|
|
||||||
|
|
||||||
Channel c = <em>Channel::_from_string("Red")</em>;
|
Channel c = <em>Channel::_from_string("Red")</em>;
|
||||||
const char *s = <em>c._to_string()</em>;
|
const char *s = <em>c._to_string()</em>;
|
||||||
|
|
||||||
|
|
||||||
size_t n = <em>Channel::_size</em>;
|
size_t n = <em>Channel::_size()</em>;
|
||||||
<em>for</em> (<em>Channel c</em> : <em>Channel::_values()</em>)
|
<em>for (Channel c : Channel::_values())</em> {
|
||||||
run_some_function(<em>c</em>);
|
run_some_function(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
<em>switch</em> (<em>c</em>) {
|
<em>switch (c)</em> {
|
||||||
<em>case Channel::Red</em>: // ...
|
<em>case Channel::Red</em>: // ...
|
||||||
<em>case Channel::Green</em>: // ...
|
<em>case Channel::Green</em>: // ...
|
||||||
<em>case Channel::Blue</em>: // ...
|
<em>case Channel::Blue</em>: // ...
|
||||||
@ -54,35 +52,60 @@ size_t n = <em>Channel::_size</em>;
|
|||||||
Channel c = <em>Channel::_from_integral(3)</em>;
|
Channel c = <em>Channel::_from_integral(3)</em>;
|
||||||
|
|
||||||
|
|
||||||
<em>constexpr</em> Channel c = <em>Channel::_from_string("Blue")</em>;</pre>
|
<em>constexpr</em> Channel c =
|
||||||
|
<em>Channel::_from_string("Blue")</em>;</pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Jump to the [tutorial](${prefix}tutorial/HelloWorld.html) to see a complete
|
<p class="splash-text">
|
||||||
working example.
|
$be is a single, lightweight header file that makes your compiler generate
|
||||||
|
<em>reflective</em> enum types.
|
||||||
|
</p>
|
||||||
|
|
||||||
### What do you get?
|
That means you can easily <u>convert enums to and from strings</u>,
|
||||||
|
<u>validate</u> them, and <u>loop</u> over them. In $cxx11, you can do it all at
|
||||||
|
compile time.
|
||||||
|
|
||||||
|
It's what built-in enums ought to support. Better Enums simply adds the missing
|
||||||
|
features. And, it is based on the best known techniques, thoroughly tested,
|
||||||
|
fast, portable, and documented exhaustively.
|
||||||
|
|
||||||
|
All you have to do to use it is include <a $download><code>enum.h</code></a>.
|
||||||
|
|
||||||
|
Try it live online in
|
||||||
|
[Wandbox](http://melpon.org/wandbox/permlink/pdlAAGoxnjqG6FRI), or begin the
|
||||||
|
[tutorial](${prefix}tutorial/HelloWorld.html)!
|
||||||
|
|
||||||
|
<div class="hack"></div>
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
<ul class="blurbs">
|
<ul class="blurbs">
|
||||||
<li class="even">
|
<li class="zero-mod-two zero-mod-three">
|
||||||
<strong>Uniform interface for $cxx98 and $cxx11</strong>
|
<strong>Unobtrusive syntax</strong>
|
||||||
<em>Scoped, sized, reflective enums for $cxx98.</em>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Compile-time reflection</strong>
|
|
||||||
<em>
|
<em>
|
||||||
Have the compiler do additional enum processing using your own
|
No ugly macros. Use initializers as with built-in <code>enums</code>.
|
||||||
templates or <code>constexpr</code> functions.
|
Internal members have underscores to avoid clashing with your constant
|
||||||
|
names.
|
||||||
</em>
|
</em>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="even">
|
<li class="one-mod-two one-mod-three">
|
||||||
<strong>Non-contiguous sequences</strong>
|
<strong>No external dependencies</strong>
|
||||||
<em>
|
<em>
|
||||||
Iteration and count much easier to maintain than with an extra "count"
|
Uses only standard $cxx. Installation is simple — just download
|
||||||
constant and making assumptions.
|
<code>enum.h</code>. There are no objects or libraries to link with.
|
||||||
</em>
|
</em>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
|
<li class="zero-mod-two two-mod-three">
|
||||||
|
<strong>No generator program needed</strong>
|
||||||
|
<em>
|
||||||
|
Just include <code>enum.h</code>. It's a metaprogram executed by your
|
||||||
|
compiler.
|
||||||
|
</em>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="one-mod-two zero-mod-three">
|
||||||
<strong>Plays nice with <code>switch</code></strong>
|
<strong>Plays nice with <code>switch</code></strong>
|
||||||
<em>
|
<em>
|
||||||
Use a Better Enum like a built-in <code>enum</code>, and still have the
|
Use a Better Enum like a built-in <code>enum</code>, and still have the
|
||||||
@ -90,15 +113,7 @@ working example.
|
|||||||
</em>
|
</em>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="even">
|
<li class="zero-mod-two one-mod-three">
|
||||||
<strong>Unobtrusive syntax</strong>
|
|
||||||
<em>
|
|
||||||
No ugly macros. Use initializers just like with built-in
|
|
||||||
<code>enums</code>. Generated members have underscores to avoid conflicts
|
|
||||||
with your constant names.
|
|
||||||
</em>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Don't repeat yourself</strong>
|
<strong>Don't repeat yourself</strong>
|
||||||
<em>
|
<em>
|
||||||
No more unmaintanable maps or <code>switch</code> statements for
|
No more unmaintanable maps or <code>switch</code> statements for
|
||||||
@ -106,29 +121,56 @@ working example.
|
|||||||
</em>
|
</em>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="even">
|
<li class="one-mod-two two-mod-three">
|
||||||
<strong>No build-time generator needed</strong>
|
<strong>Non-contiguous sequences</strong>
|
||||||
<em>
|
<em>
|
||||||
Just include <code>enum.h</code>. It's a metaprogram executed by your
|
Iteration and counting are much easier to maintain than with an extra
|
||||||
compiler.
|
<code>Count</code> constant and assuming a dense range.
|
||||||
</em>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Fast compilation</strong>
|
|
||||||
<em>
|
|
||||||
Much less impact on build time than even just including
|
|
||||||
<code>iostream</code>.
|
|
||||||
</em>
|
</em>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="even">
|
<li class="zero-mod-two zero-mod-three">
|
||||||
<strong>No external dependencies</strong>
|
<strong>Fast compilation</strong>
|
||||||
<em>
|
<em>
|
||||||
Uses standard $cxx and supported on major compilers. Installation is
|
Much less impact on build time than even just including
|
||||||
simple — just download <code>enum.h</code>.
|
<code>iostream</code>. <code>enum.h</code> is only slightly more than 1000
|
||||||
|
lines long.
|
||||||
</em>
|
</em>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
|
<li class="one-mod-two one-mod-three">
|
||||||
|
<strong>Compile-time reflection</strong>
|
||||||
|
<em>
|
||||||
|
Have the compiler do additional enum processing using your own
|
||||||
|
templates or <code>constexpr</code> functions.
|
||||||
|
</em>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="zero-mod-two two-mod-three">
|
||||||
|
<strong>Uniform interface for $cxx98, $cxx11</strong>
|
||||||
|
<em>
|
||||||
|
Scoped, sized, reflective enums for $cxx98, and an easy upgrade
|
||||||
|
path.
|
||||||
|
</em>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="one-mod-two zero-mod-three">
|
||||||
|
<strong>Stream operators</strong>
|
||||||
|
<em>
|
||||||
|
Write enum names directly to <code>std::cout</code> or use
|
||||||
|
<code>boost::lexical_cast</code>.
|
||||||
|
</em>
|
||||||
|
</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>
|
<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.
|
||||||
@ -138,40 +180,18 @@ working example.
|
|||||||
|
|
||||||
<div class="hack"></div>
|
<div class="hack"></div>
|
||||||
|
|
||||||
### It's what built-in enums ought to do.
|
### Documentation
|
||||||
|
|
||||||
The library notionally <em>extends</em> $cxx, adding oft-needed features.
|
|
||||||
|
|
||||||
<ul class="blurbs act">
|
|
||||||
<li class="even">
|
|
||||||
<strong>Download <a $download><code>enum.h</code></a></strong>
|
|
||||||
<em>
|
|
||||||
Current version: $version<br />
|
|
||||||
To install, just add the file to your project.
|
|
||||||
</em>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Visit on <a href="$repo">GitHub</a></strong>
|
|
||||||
<em>
|
|
||||||
Follow development, report issues, and let me know if you find this
|
|
||||||
library useful!
|
|
||||||
</em>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="hack"></div>
|
|
||||||
|
|
||||||
### Resources
|
|
||||||
|
|
||||||
<ul class="blurbs resources">
|
<ul class="blurbs resources">
|
||||||
<li class="even">
|
<li class="zero-mod-two zero-mod-three">
|
||||||
<a id="Tutorial"></a>
|
<a id="Tutorial"></a>
|
||||||
<strong>Tutorial</strong>
|
<strong>Tutorial</strong>
|
||||||
<ol>
|
<ol>
|
||||||
$tutorial_toc
|
$tutorial_toc
|
||||||
</ol>
|
</ol>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
|
<li class="one-mod-two one-mod-three">
|
||||||
<strong>Reference</strong>
|
<strong>Reference</strong>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="${prefix}ApiReference.html">API reference</a></li>
|
<li><a href="${prefix}ApiReference.html">API reference</a></li>
|
||||||
@ -179,12 +199,15 @@ The library notionally <em>extends</em> $cxx, adding oft-needed features.
|
|||||||
<li><a href="${prefix}OptInFeatures.html">Opt-in features</a></li>
|
<li><a href="${prefix}OptInFeatures.html">Opt-in features</a></li>
|
||||||
<li><a href="${prefix}ExtendingLimits.html">Extending limits</a></li>
|
<li><a href="${prefix}ExtendingLimits.html">Extending limits</a></li>
|
||||||
<li><a href="${prefix}Performance.html">Performance</a></li>
|
<li><a href="${prefix}Performance.html">Performance</a></li>
|
||||||
|
<li>
|
||||||
|
<a href="${prefix}DesignDecisionsFAQ.html">Design decisions FAQ</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="even">
|
<li class="zero-mod-two two-mod-three">
|
||||||
<a id="CompileTimeDemos"></a>
|
<a id="CompileTimeDemos"></a>
|
||||||
<strong>Compile-time demos</strong>
|
<strong>Advanced</strong>
|
||||||
<ul>
|
<ul>
|
||||||
$demo_toc
|
$demo_toc
|
||||||
</ul>
|
</ul>
|
||||||
@ -195,8 +218,9 @@ The library notionally <em>extends</em> $cxx, adding oft-needed features.
|
|||||||
|
|
||||||
%% title = Clean reflective enums for C++
|
%% title = Clean reflective enums for C++
|
||||||
|
|
||||||
%% description = Reflective enums for C++ with clean syntax, in a header-only
|
%% description = Better Enums is a single header C++ library providing
|
||||||
library. Can be converted to strings, iterated, counted, and used for
|
reflective enums with clean syntax. Better Enums can be converted to and from
|
||||||
metaprogramming. Free under the BSD license.
|
strings, be iterated, counted, and used at run time or for template and
|
||||||
|
constexpr metaprogramming. Free and open source under the BSD license.
|
||||||
|
|
||||||
%% class = index
|
%% class = index
|
||||||
|
|||||||
1
doc/template/cxx14.tmpl
vendored
Normal file
1
doc/template/cxx14.tmpl
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
<span class="cpp">C++</span><span class="eleven">14</span>
|
||||||
7
doc/template/demo.tmpl
vendored
7
doc/template/demo.tmpl
vendored
@ -1,7 +1,8 @@
|
|||||||
<p>
|
<p>
|
||||||
This page is an advanced demo showing the kind of compile-time code you can
|
This page is an advanced demo showing the kind of code you can write on top of
|
||||||
write on top of Better Enums. You can <a href="$source">download</a> it and
|
Better Enums. It's a valid program — you can
|
||||||
try it out.
|
<a href="$source">download</a> it and try it out. The program runs as part of
|
||||||
|
the automated test suite.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
$demo_body
|
$demo_body
|
||||||
|
|||||||
2
doc/template/download.tmpl
vendored
2
doc/template/download.tmpl
vendored
@ -1,2 +1,2 @@
|
|||||||
href="https://raw.githubusercontent.com/aantron/better-enums/$version/enum.h"
|
href="https://raw.githubusercontent.com/aantron/better-enums/$ref/enum.h"
|
||||||
download
|
download
|
||||||
22
doc/template/header.tmpl
vendored
22
doc/template/header.tmpl
vendored
@ -31,11 +31,25 @@
|
|||||||
|
|
||||||
<header>
|
<header>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="back">{}</div>
|
<section>
|
||||||
|
<h1><a href="${prefix}index.html">Better Enums</a></h1>
|
||||||
|
<h2>Reflective compile-time enums for $cxx</h2>
|
||||||
|
<h3>Open-source under the BSD license</h3>
|
||||||
|
</section>
|
||||||
|
|
||||||
<h1><a href="${prefix}index.html">Better Enums</a></h1>
|
<section class="notes">
|
||||||
<h2>Reflective compile-time enums for $cxx</h2>
|
<p>Version $version</p>
|
||||||
<h3>Open-source under the BSD license</h3>
|
<p>To install, just add <code>enum.h</code> to your project.</p>
|
||||||
|
<p>
|
||||||
|
Visit the GitHub repo for issues, feedback, and let me know if you
|
||||||
|
found this library useful!
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="buttons">
|
||||||
|
<a $download>Download <code>enum.h</code></a>
|
||||||
|
<a href="$repo">GitHub</a>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|||||||
1
doc/template/ref.tmpl
vendored
Normal file
1
doc/template/ref.tmpl
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
0.10.0
|
||||||
3
doc/template/tutorial.tmpl
vendored
3
doc/template/tutorial.tmpl
vendored
@ -1,6 +1,7 @@
|
|||||||
<p>
|
<p>
|
||||||
Welcome to the Better Enums tutorials! The code in this tutorial forms a
|
Welcome to the Better Enums tutorials! The code in this tutorial forms a
|
||||||
valid program, which you can <a href="$source">download</a> and play with.
|
valid program, which you can <a href="$source">download</a> and play with. The
|
||||||
|
program runs as part of the automated test suite.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
$tutorial_body
|
$tutorial_body
|
||||||
|
|||||||
2
doc/template/version.tmpl
vendored
2
doc/template/version.tmpl
vendored
@ -1 +1 @@
|
|||||||
0.9.0
|
0.10.0
|
||||||
@ -1,6 +1,6 @@
|
|||||||
## Hello, World!
|
## Hello, World!
|
||||||
|
|
||||||
Download <a $download><code>enum.h</code></a>, then build this program with it:
|
Download <a $download><code>enum.h</code></a>, then compile this program:
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
<em>#include "enum.h"</em>
|
<em>#include "enum.h"</em>
|
||||||
@ -20,4 +20,5 @@ Run it, and you should see the output "Hello, World!"
|
|||||||
|
|
||||||
Congratulations, you have just created your first Better Enum!
|
Congratulations, you have just created your first Better Enum!
|
||||||
|
|
||||||
%% description = Introductory Better Enums tutorial.
|
%% description = Introductory Better Enums tutorial - a simple, but complete,
|
||||||
|
Hello World program.
|
||||||
|
|||||||
@ -149,4 +149,6 @@ reference has a
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
%% description = Walkthrough of Better Enums conversion functions.
|
%% description = Better Enums conversion functions. Converting to string, from
|
||||||
|
string, to int, from int, and validation, both case-sensitive and
|
||||||
|
case-insensitive. Exception-throwing and non-throwing variants presented.
|
||||||
|
|||||||
@ -46,4 +46,5 @@ If you are using $cxx11, you can have much nicer syntax:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
%% description = Iterating over all Better Enums constants.
|
%% description = Using Better Enums to iterate over all the constants of an
|
||||||
|
enum, as well as over its names. Also shows the same with C++11 for-each syntax.
|
||||||
|
|||||||
@ -27,4 +27,6 @@ you a warning — try it!
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
%% description = Usage in switch statements.
|
%% description = Better Enums can be used directly in switch statements like
|
||||||
|
normal enums, making it possible for the compiler to check that all cases are
|
||||||
|
listed, increasing the safety of your code.
|
||||||
|
|||||||
34
doc/tutorial/5-iostreams.md
Normal file
34
doc/tutorial/5-iostreams.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
## Stream operators
|
||||||
|
|
||||||
|
These work *almost* as you'd expect. First, make sure you include `iostream`
|
||||||
|
before `enum.h` in any translation unit in which you intend to use the
|
||||||
|
operators:
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <enum.h>
|
||||||
|
|
||||||
|
<em>ENUM(Channel, int, Red, Green, Blue)</em>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
std::cout << <em>+Channel::Red</em> << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
The thing to watch for is the `+`: without it, `Channel::Red` is a value of type
|
||||||
|
`Channel::_enumerated`, a $cxx98 enum type, so writing that to `cout` will
|
||||||
|
output an integer. `+Channel::Red`, however, is a value of type `Channel`, and
|
||||||
|
writing *that* instead will output the string `"Red"`.
|
||||||
|
|
||||||
|
Input is also supported:
|
||||||
|
|
||||||
|
~~~comment
|
||||||
|
Channel channel = Channel::Blue;
|
||||||
|
std::cin >> channel; // Expects input such as "Green".
|
||||||
|
~~~
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Only `char` streams are supported for the time being.
|
||||||
|
|
||||||
|
%% description = Using Better Enums with stream input and output operators.
|
||||||
@ -76,4 +76,4 @@ the one in that demo.
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
%% description = Type safety features and limitations.
|
%% description = Better Enums type safety features and limitations.
|
||||||
@ -80,4 +80,5 @@ types containg enums. The enums will behave as expected.
|
|||||||
compatible with $cxx98, where those names aren't available in a portable
|
compatible with $cxx98, where those names aren't available in a portable
|
||||||
manner.
|
manner.
|
||||||
|
|
||||||
%% description = Underlying representation.
|
%% description = The underlying memory representation of a Better Enum,
|
||||||
|
including size and alignment.
|
||||||
@ -48,4 +48,5 @@ You can also do things such as:
|
|||||||
Which prints "5", the length of "Green". That 5 was also computed during
|
Which prints "5", the length of "Green". That 5 was also computed during
|
||||||
compilation.
|
compilation.
|
||||||
|
|
||||||
%% description = Introduction to compile-time conversions.
|
%% description = Better Enums can be used entirely at compile time in C++11. All
|
||||||
|
conversion functions are available for constexpr functions or templates.
|
||||||
153
example/102-any-underlying.cc
Normal file
153
example/102-any-underlying.cc
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// Here's how to do it.
|
||||||
|
|
||||||
|
#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.
|
||||||
|
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:
|
||||||
|
//
|
||||||
|
// 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:
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
29
example/5-iostreams.cc
Normal file
29
example/5-iostreams.cc
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// This file was generated automatically.
|
||||||
|
|
||||||
|
// Stream operators
|
||||||
|
//
|
||||||
|
// These work almost as you'd expect. Just make sure you include iostream before
|
||||||
|
// enum.h in any translation unit in which you intend to use the operators.
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <enum.h>
|
||||||
|
|
||||||
|
ENUM(Channel, int, Red, Green, Blue)
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
std::cout << +Channel::Red << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The thing to watch for is the +: without it, Channel::Red is a value of type
|
||||||
|
// Channel::_enumerated, a C++98 enum type, so writing that to cout will output
|
||||||
|
// an integer. +Channel::Red, however, is a value of type Channel, and writing
|
||||||
|
// that instead will output the string "Red".
|
||||||
|
//
|
||||||
|
// Input is also supported:
|
||||||
|
//
|
||||||
|
// Channel channel = Channel::Blue;
|
||||||
|
// std::cin >> channel; // Expects input such as "Green".
|
||||||
|
//
|
||||||
|
// Only char streams are supported for the time being.
|
||||||
4
test/expect/102-any-underlying
Normal file
4
test/expect/102-any-underlying
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
Red component: c4
|
||||||
|
Green component: 74
|
||||||
|
Blue component: 51
|
||||||
|
darksalmon
|
||||||
1
test/expect/5-iostreams
Normal file
1
test/expect/5-iostreams
Normal file
@ -0,0 +1 @@
|
|||||||
|
Red
|
||||||
@ -152,8 +152,11 @@ class Configuration(object):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
skip_cxx98 = ["101-special-values", "102-bitset", "103-quine", "7-constexpr"]
|
skip_cxx98 = [
|
||||||
skip_strict = ["4-switch"]
|
"101-special-values", "102-any-underlying", "103-bitset", "104-quine",
|
||||||
|
"8-constexpr"
|
||||||
|
]
|
||||||
|
skip_strict = ["4-switch", "102-any-underlying"]
|
||||||
|
|
||||||
def modern_gnu(command):
|
def modern_gnu(command):
|
||||||
return [
|
return [
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user