Updated documentation.

This commit is contained in:
Anton Bachin 2015-06-20 12:58:33 -05:00
parent d90bfd6f18
commit f5f669277a
47 changed files with 1307 additions and 171 deletions

View File

@ -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
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.
[max]: http://aantron.github.io/better-enums/demo/BitSets.html
[enforce]: http://aantron.github.io/better-enums/demo/SpecialValues.html
[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
@ -67,14 +67,19 @@ enums that are missing from standard C++.
compiler as much as [just including `iostream` does][performance].
- Use any initializers and sparse ranges, just like with a built-in enum.
- 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
[underlying]: http://aantron.github.io/better-enums/demo/NonIntegralUnderlyingTypes.html
## Limitations
The biggest current limitation is that the `ENUM` macro can't be used inside a
class. This seems difficult to remove, but I am looking into it. In the
meantime, there is a workaround with a `typedef` (or `using`):
The biggest limitation is that the `ENUM` macro can't be used inside a class.
This seems [difficult to remove][nested]. There is a workaround with `typedef`
(or `using`):
```cpp
ENUM(UniquePrefix_Color, uint8_t, Red, Green, Blue)
@ -89,6 +94,8 @@ triplet::Color color;
You can, however, use `ENUM` inside a namespace.
[nested]: http://aantron.github.io/better-enums/DesignDecisionsFAQ.html#NoEnumInsideClass
## Contact
Don't hesitate to contact me about features or bugs:

View File

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

View File

@ -1,7 +1,6 @@
## Compiler support
Better Enums aims to support all major compilers. It is known to definitely work
on
Better Enums aims to support all major compilers. It is known to work on:
- clang 3.3 to 3.6
- gcc 4.3 to 5.1
@ -121,4 +120,6 @@ g++43 -std=c++0x -DBETTER_ENUMS_NO_CONSTEXPR
g++43 -std=c++98
~~~
%% description = Information about compiler support and feature detection.
%% description =
Better Enums compiler support, compatibility, feature detection, and automated
testing.

View File

@ -8,9 +8,11 @@
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
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
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
View 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 &mdash; 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 &mdash; 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 &mdash; 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 &mdash; 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

View File

@ -19,7 +19,7 @@ constants of full-`constexpr` enums. To extend:
example. Add 1 to the number of characters to account for the null
terminator &mdash; our numbers are now 512 and 64.
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
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
@ -31,10 +31,27 @@ constants of full-`constexpr` enums. To extend:
- For g++ and clang++, `-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 &mdash; increasing the second
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
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.

View 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` &mdash; everything will be done by Better Enums at run time.
Doing this enables the following usage:
// The type. A color triplet.
<em>struct html_color {
uint8_t r, g, b;
constexpr html_color(uint8_t _r, uint8_t g, uint8_t b) :
r(_r), g(_g), b(_b) { }
};</em>
// The enum.
<em>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."

View File

@ -64,5 +64,5 @@ Here they are:
case <em>+</em>Channel::Blue: break;
}
%% description = Opting into features disabled by default for performance or
compatibility reasons.
%% description = Optional Better Enums features, disabled by default for
performance or compatibility reasons.

View File

@ -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:
- [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.
- 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.
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
- 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.

View File

@ -11,6 +11,10 @@ pre, code, samp, h4, .contents ul {
Consolas, "Liberation Mono", Menlo, "Courier New", Courier, monospace;
}
h1, .splash-text .self, nav .self {
font-family: Georgia, Times, "Times New Roman", serif;
}
pre {
background-color: #477093;
padding: 1.5em 20px;
@ -18,6 +22,8 @@ pre {
overflow: scroll;
color: rgba(255, 255, 255, 0.6);
font-size: 78%;
max-width: 84ex;
margin-left: 1em;
}
pre em {
@ -45,21 +51,23 @@ code, samp {
}
.container {
max-width: 760px;
margin-left: 230px;
margin-left: 150px;
margin-right: 150px;
}
@media (max-width: 1220px) {
@media (max-width: 1400px) {
.container {
margin-left: auto;
margin-right: auto;
width: 1100px;
}
}
@media (max-width: 780px) {
@media (max-width: 1120px) {
.container {
margin-left: 10px;
margin-right: 10px;
width: auto;
}
}
@ -79,6 +87,17 @@ nav, .spacer {
background-color: #222;
}
nav > * > span {
float: right;
opacity: 0.9;
margin-right: 1em;
}
nav .self {
font-size: 16px;
padding-right: 0.5em;
}
nav a {
margin-right: 2em;
}
@ -109,7 +128,33 @@ header {
h1 {
margin: 0;
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 {
@ -129,10 +174,41 @@ header h3 {
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 {
margin-top: 2em;
font-size: 36px;
font-weight: 100;
font-weight: 300;
}
hr {
@ -142,9 +218,9 @@ hr {
footer {
font-size: 14px;
margin-top: 150px;
margin-top: 100px;
margin-bottom: 20px;
opacity: 0.6;
opacity: 0.5;
}
a {
@ -215,7 +291,6 @@ span#note:target {
.main h3 {
font-size: 30px;
font-weight: 100;
margin-top: 2em;
color: black;
}
@ -288,15 +363,45 @@ li {
}
.blurbs > li {
width: 45%;
float: left;
min-height: 5em;
}
.blurbs > li.even {
@media (min-width: 1076px) {
.blurbs > li {
width: 28%;
margin-right: 4%;
}
.blurbs > li.zero-mod-three {
clear: both;
margin-left: 3%;
margin-right: 6%;
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 {
@ -311,19 +416,6 @@ li {
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 {
font-weight: bold;
font-size: 120%;
@ -342,9 +434,25 @@ li {
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 {
float: right;
text-align: center;
margin-left: -10%;
white-space: nowrap;
}
@ -354,12 +462,22 @@ li {
.splash pre.left {
text-align: right;
color: inherit;
color: black;
font-weight: bold;
background-color: transparent;
padding-right: 0;
}
.splash pre.right {
text-align: left;
background-color: black;
}
@media (max-width: 1000px) {
.splash {
float: none;
margin-left: -10%;
}
}
@media (max-width: 700px) {
@ -385,6 +503,7 @@ h4 {
color: #888;
padding-top: 1em;
white-space: nowrap;
font-size: 125%;
}
h4 em {
@ -394,9 +513,9 @@ h4 em {
}
.api ul.contents {
-webkit-columns: 300px 2;
-moz-columns: 300px 2;
columns: 300px 2;
-webkit-columns: 300px 3;
-moz-columns: 300px 3;
columns: 300px 3;
}
.api ul.contents > li {
@ -407,6 +526,7 @@ h4 em {
.main h3 {
margin-top: 4em;
clear: both;
}
h3.contents {

View File

@ -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.
Enjoy!
%% description = Encoding project policies for static enforcement using Better
Enums.
%% description = An example that uses Better Enums compile-time reflection to
create invalid and default values for each enum, enforced statically by the
compiler, for readability and maintainability.

View 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
&mdash; though then, of course, `T` doesn't have to be literal and the mapping
doesn't have to be `constexpr`. In $cxx98, everything involving `T` will simply
be done by Better Enums at run time.
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."

View File

@ -65,5 +65,5 @@ static_assert(max<EFLAGS>()._to_integral() < 32,
"some bit indices are out of range");
~~~
%% description = Finding the maximum value of a Better Enum constant for use in
declaring bit set types.
%% description = Finding the maximum value of a Better Enum for use in declaring
statically-sized bit set types.

View File

@ -170,4 +170,6 @@ ENUM(Channel, int, Red = 0, Green = 1, Blue = 2)
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.

View File

@ -87,11 +87,17 @@ def compose_page(relative_path, definitions):
definitions["class"] = ""
text = templates["page"]
text = scrub_comments(text)
while '$' in text:
text = apply_template(text, definitions)
text = scrub_comments(text)
while True:
new_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
@ -143,7 +149,7 @@ def process_threaded(directory):
source_file = \
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["body"] = templates[directory]
@ -183,6 +189,10 @@ def generate_sitemap():
for url in generated:
text += " <url>\n"
text += " <loc>%s</loc>\n" % url
if ".html" not in url:
text += " <priority>1.0</priority>\n"
text += " </url>\n"
text += "</urlset>\n"

View File

@ -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">
<pre class="left">enable
@ -11,7 +5,7 @@ declare
parse
print
format
count
@ -19,6 +13,7 @@ iterate
switch
@ -29,22 +24,25 @@ switch
safe cast
at compile time</pre>
<pre class="right"><em>#include</em> &lt;<em>enum.h</em>&gt;
during
compilation
</pre>
<pre class="right"><em>#include &lt;enum.h&gt;</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>;
const char *s = <em>c._to_string()</em>;
size_t n = <em>Channel::_size</em>;
<em>for</em> (<em>Channel c</em> : <em>Channel::_values()</em>)
run_some_function(<em>c</em>);
size_t n = <em>Channel::_size()</em>;
<em>for (Channel c : Channel::_values())</em> {
run_some_function(c);
}
<em>switch</em> (<em>c</em>) {
<em>switch (c)</em> {
<em>case Channel::Red</em>: // ...
<em>case Channel::Green</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>;
<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>
Jump to the [tutorial](${prefix}tutorial/HelloWorld.html) to see a complete
working example.
<p class="splash-text">
$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">
<li class="even">
<strong>Uniform interface for $cxx98 and $cxx11</strong>
<em>Scoped, sized, reflective enums for $cxx98.</em>
</li>
<li>
<strong>Compile-time reflection</strong>
<li class="zero-mod-two zero-mod-three">
<strong>Unobtrusive syntax</strong>
<em>
Have the compiler do additional enum processing using your own
templates or <code>constexpr</code> functions.
No ugly macros. Use initializers as with built-in <code>enums</code>.
Internal members have underscores to avoid clashing with your constant
names.
</em>
</li>
<li class="even">
<strong>Non-contiguous sequences</strong>
<li class="one-mod-two one-mod-three">
<strong>No external dependencies</strong>
<em>
Iteration and count much easier to maintain than with an extra "count"
constant and making assumptions.
Uses only standard $cxx. Installation is simple &mdash; just download
<code>enum.h</code>. There are no objects or libraries to link with.
</em>
</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>
<em>
Use a Better Enum like a built-in <code>enum</code>, and still have the
@ -90,15 +113,7 @@ working example.
</em>
</li>
<li class="even">
<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>
<li class="zero-mod-two one-mod-three">
<strong>Don't repeat yourself</strong>
<em>
No more unmaintanable maps or <code>switch</code> statements for
@ -106,29 +121,56 @@ working example.
</em>
</li>
<li class="even">
<strong>No build-time generator needed</strong>
<li class="one-mod-two two-mod-three">
<strong>Non-contiguous sequences</strong>
<em>
Just include <code>enum.h</code>. It's a metaprogram executed by your
compiler.
</em>
</li>
<li>
<strong>Fast compilation</strong>
<em>
Much less impact on build time than even just including
<code>iostream</code>.
Iteration and counting are much easier to maintain than with an extra
<code>Count</code> constant and assuming a dense range.
</em>
</li>
<li class="even">
<strong>No external dependencies</strong>
<li class="zero-mod-two zero-mod-three">
<strong>Fast compilation</strong>
<em>
Uses standard $cxx and supported on major compilers. Installation is
simple &mdash; just download <code>enum.h</code>.
Much less impact on build time than even just including
<code>iostream</code>. <code>enum.h</code> is only slightly more than 1000
lines long.
</em>
</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>
<em>
Released under the BSD license for use in any project, free or commercial.
@ -138,40 +180,18 @@ working example.
<div class="hack"></div>
### It's what built-in enums ought to do.
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
### Documentation
<ul class="blurbs resources">
<li class="even">
<li class="zero-mod-two zero-mod-three">
<a id="Tutorial"></a>
<strong>Tutorial</strong>
<ol>
$tutorial_toc
</ol>
</li>
<li>
<li class="one-mod-two one-mod-three">
<strong>Reference</strong>
<ul>
<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}ExtendingLimits.html">Extending limits</a></li>
<li><a href="${prefix}Performance.html">Performance</a></li>
<li>
<a href="${prefix}DesignDecisionsFAQ.html">Design decisions FAQ</a>
</li>
</ul>
</li>
<li class="even">
<li class="zero-mod-two two-mod-three">
<a id="CompileTimeDemos"></a>
<strong>Compile-time demos</strong>
<strong>Advanced</strong>
<ul>
$demo_toc
</ul>
@ -195,8 +218,9 @@ The library notionally <em>extends</em> $cxx, adding oft-needed features.
%% title = Clean reflective enums for C++
%% description = Reflective enums for C++ with clean syntax, in a header-only
library. Can be converted to strings, iterated, counted, and used for
metaprogramming. Free under the BSD license.
%% description = Better Enums is a single header C++ library providing
reflective enums with clean syntax. Better Enums can be converted to and from
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

1
doc/template/cxx14.tmpl vendored Normal file
View File

@ -0,0 +1 @@
<span class="cpp">C++</span><span class="eleven">14</span>

View File

@ -1,7 +1,8 @@
<p>
This page is an advanced demo showing the kind of compile-time code you can
write on top of Better Enums. You can <a href="$source">download</a> it and
try it out.
This page is an advanced demo showing the kind of code you can write on top of
Better Enums. It's a valid program &mdash; you can
<a href="$source">download</a> it and try it out. The program runs as part of
the automated test suite.
</p>
$demo_body

View File

@ -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

View File

@ -31,11 +31,25 @@
<header>
<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>
<section class="notes">
<p>Version $version</p>
<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>
</header>

1
doc/template/ref.tmpl vendored Normal file
View File

@ -0,0 +1 @@
0.10.0

View File

@ -1,6 +1,7 @@
<p>
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>
$tutorial_body

View File

@ -1 +1 @@
0.9.0
0.10.0

View File

@ -1,6 +1,6 @@
## 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>
<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!
%% description = Introductory Better Enums tutorial.
%% description = Introductory Better Enums tutorial - a simple, but complete,
Hello World program.

View File

@ -149,4 +149,6 @@ reference has a
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.

View File

@ -46,4 +46,5 @@ If you are using $cxx11, you can have much nicer syntax:
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.

View File

@ -27,4 +27,6 @@ you a warning &mdash; try it!
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.

View 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.

View File

@ -76,4 +76,4 @@ the one in that demo.
return 0;
}
%% description = Type safety features and limitations.
%% description = Better Enums type safety features and limitations.

View File

@ -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
manner.
%% description = Underlying representation.
%% description = The underlying memory representation of a Better Enum,
including size and alignment.

View File

@ -48,4 +48,5 @@ You can also do things such as:
Which prints "5", the length of "Green". That 5 was also computed during
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.

View 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
View 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.

View File

@ -0,0 +1,4 @@
Red component: c4
Green component: 74
Blue component: 51
darksalmon

1
test/expect/5-iostreams Normal file
View File

@ -0,0 +1 @@
Red

View File

@ -152,8 +152,11 @@ class Configuration(object):
skip_cxx98 = ["101-special-values", "102-bitset", "103-quine", "7-constexpr"]
skip_strict = ["4-switch"]
skip_cxx98 = [
"101-special-values", "102-any-underlying", "103-bitset", "104-quine",
"8-constexpr"
]
skip_strict = ["4-switch", "102-any-underlying"]
def modern_gnu(command):
return [