Updated and improved documentation.

This commit is contained in:
Anton Bachin 2015-06-05 13:01:28 -05:00
parent 3a316e6f79
commit b24d155b7b
45 changed files with 1559 additions and 888 deletions

View File

@ -1,7 +1,9 @@
# Better Enums
Reflective compile-time C++ enum library with clean syntax. For example:
Reflective compile-time C++ enum library with clean syntax, in a single header
file. For example:
#include <enum.h>
ENUM(Channel, int, Red = 1, Green, Blue)
defines a type `Channel`. You can then do natural things such as:
@ -10,56 +12,73 @@ defines a type `Channel`. You can then do natural things such as:
Channel channel = Channel::Green;
channel._to_string(); // Results in the string "Green"
channel._to_integral(); // Results in the int 2
Channel::_from_string("Red"); // Results in Channel::Red
Channel::_from_integral(3); // Results in Channel::Blue
constexpr auto channel = Channel::_from_integral(3);
// Do it at compile time (C++11 only)
Channel::_from_integral(3); // Checked cast, Channel::Blue
Channel::_size; // Number of channels (3)
Channel::_values()[0]; // Get the first channel
for (Channel channel : Channel::_values()) {
// Iterate over all channels
process(channel); // Iterate over all channels
}
// Natural switch, compiler checks the cases
switch (channel) {
case Channel::Red: break;
case Channel::Green: break;
case Channel::Blue: break;
}
```
...and more. See the [project page](http://aantron.github.io/better-enums).
...and more.
In C++11, *everything* is available at compile time. You can convert your enums,
loop over them, [find their max][max],
[statically enforce conventions][enforce], and pass along the results as
template arguments or to `constexpr` functions. All the reflection is available
for your metaprogramming needs.
The interface is the same for C++98 &mdash; 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.
[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
## Installation
Simply add `enum.h` to your project.
Simply add `enum.h` to your project &mdash; that's it.
## Features
Then, include it and use the `ENUM` macro. Your compiler will generate the rich
enums that are missing from standard C++.
- Requires no external utility.
- Safe conversions to/from integers and strings.
- Iteration over declared values.
- Switch case checking.
- All operations are `constexpr` and can be used at compile time in your own
`constexpr` code. See demos on the
[project page](http://aantron.github.io/better-enums) for how to define
default values, for example.
- Constant values can be initialized with expressions (`Red = 1`) and aliased
(`Favorite = Green`), just like with built-in enums.
- Generating a large number of enums is about as fast as including a typical
standard header like `iostream` performance test included.
- Explicit choice of underlying representation type.
- Header-only.
- No dependencies besides the standard library.
- Tested on gcc 4.3 to 5.1, clang 3.3 to 3.6, and VS2013, VS2015.
## Additional features
- No dependencies and no external build tools. Uses only standard C++, though,
for C++98, variadic macro support is required.
- Supported and tested on clang, gcc, and msvc.
- Fast compilation. You have to declare a few dozen enums to slow down your
compiler as much as [just including `iostream` does][performance].
- Use any initializers, just like with a built-in enum.
- Guaranteed size and alignment &mdash; you choose the representation type.
[performance]: http://aantron.github.io/better-enums/Performance.html
## Contact
Don't hesitate to contact me about features (or bugs!):
<a href="mailto:antonbachin@yahoo.com">antonbachin@yahoo.com</a>
Don't hesitate to contact me about features or bugs:
[antonbachin@yahoo.com](mailto:antonbachin@yahoo.com), or open an issue on
GitHub.
## License
## License and history
Better Enums is released under the BSD 2-clause license. See
[LICENSE](https://github.com/aantron/better-enums/blob/master/LICENSE).
## History
The library was originally developed by the author in the winter of 2012-2013 at
Hudson River Trading, as a replacement for an older generator called
`BETTER_ENUM`.

View File

@ -1,272 +1,370 @@
## API reference
Table of contents
$internal_toc
### Overview
The following declaration
The declaration
#include <enum.h>
ENUM(Enum, underlying_type, A, B, C, ...);
<em>ENUM</em>(<em>Enum</em>, <em>underlying_type</em>, <em>A</em>, <em>B</em>, <em>C</em>, ...)
generates a new type `Enum`. It is notionally similar to the type created by
this $cxx11 declaration:
generates a new class type `Enum` which is notionally similar to the type
created by this $cxx11 declaration:
enum class Enum : underlying_type {A, B, C, ...};
~~~comment
<em>enum class Enum</em> : <em>underlying_type</em> {<em>A</em>, <em>B</em>, <em>C</em>, ...};
~~~
that is, `Enum` is a scoped enumerated type with constants `Enum::A`, `Enum::B`,
That is, `Enum` is a scoped enumerated type with constants `Enum::A`, `Enum::B`,
`Enum::C`, and so on, with memory representation the same as `underlying_type`.
It is possible to supply initializers for any of the constants:
ENUM(Enum, underlying_type, A = 1, B = constant_expression, C, ...);
<em>ENUM</em>(Enum, underlying_type, <em>A</em> = <em>1</em>, <em>B</em> = <em>constant_expression</em>, <em>C</em> = <em>A</em>, ...)
The initializers have the same meaning and constraints as in an `enum class`
declaration.
The initializers have the same meaning and constraints as in a built-in `enum`
or `enum class` declaration.
---
The principal differences between the types declared by the `ENUM` macro and
`enum class` are:
- `ENUM` is available for $cxx98 for compilers supporting `__VA_ARGS__`,
- `ENUM` is available for $cxx98
[compilers](${prefix}CompilerSupport.html) supporting `__VA_ARGS__` &mdash;
all major compilers &mdash; while `enum class` is restricted to $cxx11,
- the `ENUM` type is implicitly convertible to integral types, though this can
be disabled when using $cxx11, [How]() and
be [disabled](${prefix}OptInFeatures.html#StrictConversions) when using
$cxx11, and
- the `ENUM` type supports a set of reflective operations, detailed in the
rest of this reference.
The types produced by the `ENUM` macro are sometimes called Better Enums in the
rest of this reference, and the running example declaration is
---
ENUM(Enum, int, A, B, C);
The types produced by the `ENUM` macro are called *Better Enums* in the rest of
this reference.
For the purposes of argument passing, Better Enums should be thought of as
equivalent to their underlying type. This means that Better Enums are typically
integers that fit into a register or a stack word, and should be passed by
value.
Better Enums are similar to their underlying type for the purposes of argument
passing. This means that they typically fit into a machine word, and should be
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.
### General
### Running example
#### typedef _enumerated
The rest of this reference uses the following declaration as a running example:
An internal type used to declare constants. `ENUM` generates something similar
to
<em>ENUM</em>(<em>Enum</em>, <em>int</em>, <em>A</em>, <em>B</em>, <em>C</em>)
struct Enum {
enum _enumerated : int { A, B, C };
// ...
};
The user needs to be aware of this type in only one situation. A literal constant
such as `Enum::A` is an expression of type `Enum::_enumerated`, not `Enum`. It
is not possible to directly call a method on the value, as in
`Enum::A._to_string()`. This problem is addressed by operator `+` below.
#### non-member constexpr Enum unary operator +(_enumerated)
### Helper functions and types
Forces promotion of `Enum::_enumerated` to `Enum`. Provided to solve the problem
described under `_enumerated` above. So, for example, it is necessary to write
`(+Enum::A)._to_string()` instead of `Enum::A._to_string`.
The types and functions described here make it possible to use Better Enums with
the rest of $cxx in a reasonable fashion, or else they are referenced in the
rest of the documentation.
#### constexpr implicit constructor Enum(_enumerated)
#### <em>typedef _enumerated</em>
A constructor that performs implicit conversions of `Enum::_enumerated` to
`Enum`. This allows code to use a literal constant where `Enum` is expected in
most situations &mdash; those where the compiler can do an implicit conversion.
For example, if there is a function `void output(Enum)`, it can be called as
`output(Enum::A)`, without having to write `output(+Enum::A)`. This constructor
is also used for the typical initialization
An internal type used to declare constants. The `ENUM` macro generates something
similar to
Enum value = Enum::A;
~~~comment
<em>struct Enum</em> {
<em>enum _enumerated</em> : <em>int</em> {<em>A</em>, <em>B</em>, <em>C</em>};
// ...
};
~~~
The user needs to be aware of `_enumerated` in only one situation. A literal
constant such as `Enum::A` is an expression of type `Enum::_enumerated`, not
`Enum`. It is not possible to directly call a method on the constant, as in
`Enum::A._to_string()`. This problem is addressed by operator `+`
[below](#Operator+).
#### non-member constexpr Enum unary <em>operator +</em>(_enumerated)
Forces promotion of [`Enum::_enumerated`](#Typedef_enumerated) to `Enum`.
Provided to solve the problem described [above](#Typedef_enumerated). So:
// Does not compile
<em>Enum::A</em>.<em>_to_string</em>()
// Compiles
(<em>+Enum::A</em>).<em>_to_string</em>()
#### constexpr implicit <em>constructor Enum(_enumerated)</em>
A constructor that performs implicit conversions of
[`Enum::_enumerated`](#Typedef_enumerated) to `Enum`. This allows code to use a
literal constant where `Enum` is expected, and the compiler can do an implicit
conversion. For example:
void <em>do_something</em>(<em>Enum value</em>);
do_something(<em>+Enum::A</em>); // Not necessary
do_something(<em>Enum::A</em>); // Implicit conversion available
<em>Enum value</em> = <em>Enum::A</em>; // Implicit conversion
The other constructors of `Enum` are the implicitly-generated copy and move
constructors. There is no default constructor.
constructors. There is no default constructor. If you have comments on what a
default constructor should do, please [let me know](${prefix}Contact.html).
#### static constexpr size_t _size
#### non-member <em>struct better_enums::optional</em>&lt;Enum&gt;
An optional `Enum` value. These are returned by the various `_nothrow`
functions, such as [`_from_string_nothrow`](#_from_string_nothrow). This type is
meant to represent the possibility of failure. For example, suppose you have:
<em>better_enums::optional</em>&lt;<em>Enum</em>&gt; <em>maybe</em> = <em>_from_string_nothrow</em>(<em>"A"</em>);
An optional value such as `maybe` is convertible to `bool`. If it converts to
`true`, it holds a valid `Enum` value. Otherwise, if it converts to `false`, the
operation that produced the optional value failed. So, you can continue with
<em>if</em> (<em>maybe</em>) {
// The string conversion succeeded
do_something(<em>*maybe</em>);
}
<em>else</em> {
// The string conversion failed
}
As you can see, `*maybe` evaluates to the `Enum` value, in this case `Enum::A`.
The rest of this reference refers to this type as simply `optional`, as if you
had entered
~~~comment
using <em>optional</em> = <em>better_enums::optional</em>&lt;<em>Enum</em>&gt;;
~~~
### Value count and iteration
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>
The number of constants declared. `Enum::_size == 3`.
#### typedef _value_iterable
#### <em>typedef _value_iterable</em>
Type of object that permits iteration over the constants. Has at least
`constexpr` `begin()`, `end()`, and `size()` methods, and `operator[]`.
Iteration visits each declared constant, even if multiple constants have the
same value, and visits them in order of declaration. See usage examples under
`_values`.
`constexpr` `begin()`, `end()`, and `size()` methods, and `constexpr`
`operator[]`. Iteration visits each *declared* constant, even if multiple
constants have the same value, and visits them in order of declaration. See
usage examples under [`_values`](#_values).
#### typedef _value_iterator
#### <em>typedef _value_iterator</em>
Random-access iterator type for `_value_iterable`. Most operations, including
dereferencing, are `constexpr`. The exceptions are mutating operators such as
`operator++`. In `constexpr` code, that can be replaced with addition of `1`.
Random-access iterator type for [`_value_iterable`](#_value_iterable). Most
operations, including dereferencing, are `constexpr`. The exceptions are
mutating operators such as `operator++`. In `constexpr` code, that can be
replaced with addition of `1`. You typically don't have to refer to this type
directly.
#### static constexpr _value_iterable _values()
#### static constexpr _value_iterable <em>_values</em>()
`constexpr` access to the collection of declared constants. For example:
`constexpr` access to the sequence of declared constants. For example:
for (size_t index = 0; index < Enum::_values().size(); ++index)
output(Enum::_values()[index]);
<em>for</em> (size_t index = 0; <em>index</em> < <em>Enum::_values().size()</em>; ++index)
do_something(<em>Enum::_values()[index]</em>);
or, using iterators:
for (Enum::_value_iterator iterator = Enum::_values().begin();
iterator != Enum::_values().end(); ++iterator) {
<em>for</em> (Enum::_value_iterator iterator = <em>Enum::_values().begin()</em>;
iterator != <em>Enum::_values().end()</em>; ++iterator) {
output(*iterator);
do_something(<em>*iterator</em>);
}
or, in $cxx11:
for (Enum value : Enum::_values())
output(value);
#### non-member struct better_enums::optional<Enum>
Optional `Enum` value. An optional value `maybe` can be in one of two states:
- `(bool)maybe == true`, meaning that there is `Enum` value, and `*maybe`
evaluates to it, and
- `(bool)maybe == false`, meaning no `Enum` value is available.
This type is referred to as simply `optional` in the rest of the reference, as
if `using better_enums::optional;` has been entered.
<em>for</em> (Enum value : <em>Enum::_values()</em>)
do_something(<em>value</em>);
### Strings
### String conversion and iteration
#### member constexpr? const char* _to_string() const
#### member constexpr? const char* <em>_to_string</em>() const
Returns the string representation of the Better Enum value on which it is
called. For example, if `value == Enum::A`, `value._to_string()` is the same
string as `"A"`.
Returns the string representation a Better Enum value. For example:
If two constants have the same numeric representation, and `value` is equal to
one of those constants, then it has that numeric representation, and it is
undefined which constant's name `_to_string` will choose.
Enum value = <em>Enum::A</em>;
value.<em>_to_string</em>(); // Same as "A".
If two or more constants have the same numeric value, it is undefined which name
`_to_string` will choose, but it will choose one of them.
If `value` is not equal to the representation of any declared constant, for
example if it was obtained using an unchecked cast such as:
example if it was obtained using an unchecked conversion such as
Enum value = Enum::_from_integral_unchecked(0xbadc0de);
Enum value = <em>Enum::_from_integral_unchecked</em>(<em>0xbadc0de</em>);
then the behavior of `value._to_string` is undefined.
Runnig time is linear in the number of declared constants.
Running time is linear in the number of declared constants.
This method is not `constexpr` by default because making it so entails a large
slowdown during compilation. [See here]() for how to make it `constexpr` for
some or all Better Enums.
This method is not `constexpr` by default. Read
[here](${prefix}OptInFeatures.html#CompileTimeNameTrimming) for information
about making it `constexpr`.
#### static constexpr Enum _from_string(const char*)
#### static constexpr Enum <em>_from_string</em>(const char*)
If the given string is the exact name of a declared constant, returns its value.
Otherwise, throws `std::runtime_error`. Running time is linear in the number of
declared constants multiplied by the length of the longest constant.
If the given string is the exact name of a declared constant, returns the
constant. Otherwise, throws `std::runtime_error`. Running time is linear in the
number of declared constants multiplied by the length of the longest constant.
#### static constexpr optional<Enum> _from_string_nothrow(const char*)
#### static constexpr optional<Enum> <em>_from_string_nothrow</em>(const char*)
The same as `_from_string`, but does not throw an exception on failure. Returns
an [optional]() value instead.
Same as [`_from_string`](#_from_string), but does not throw an exception on
failure. Returns an [optional value](#StructBetter_enumsoptional) instead.
#### static constexpr Enum _from_string_nocase(const char*)
#### static constexpr Enum <em>_from_string_nocase</em>(const char*)
The same as `_from_string`, but comparison is up to case, in the usual sense in
the Latin-1 encoding.
Same as [`_from_string`](#_from_string), but comparison is up to case, in the
usual sense in the Latin-1 encoding.
#### static constexpr optional<Enum> _from_string_nocase_nothrow(const char*)
#### static constexpr optional<Enum> <em>_from_string_nocase_nothrow</em>(const char*)
Is to `_from_string_nocase` as `_from_string_nothrow` is to `_from_string`.
Is to [`_from_string_nocase`](#_from_string_nocase) as
[`_from_string_nothrow`](#_from_string_nothrow) is to
[`_from_string`](#_from_string).
#### static constexpr bool _is_valid(const char*)
#### static constexpr bool <em>_is_valid(const char*)</em>
Evaluates to `true` if and only if the given string is the exact name of a
declared constant. Running time is the same as for `_from_string`.
declared constant. Running time is the same as for
[`_from_string`](#_from_string).
#### static constexpr bool _is_valid_nocase(const char*)
#### static constexpr bool <em>_is_valid_nocase</em>(const char*)
The same as `_is_valid`, but comparison is done up to case as in
`_from_string_nocase`.
The same as [`_is_valid`](#_is_validconstChar*), but comparison is done up to
case as in [`_from_string_nocase`](#_from_string_nocase).
#### static constexpr const char* _name()
#### static constexpr const char* <em>_name</em>()
Evaluates to the name of the Better Enum type. `Enum::_name()` is the same
string as `"Enum"`.
#### typedef _name_iterable
#### typedef <em>_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
available, but is `constexpr` if and only if `_to_string` is `constexpr`.
Iteration visits constants in order of declaration. See usage example under
`_names`.
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 _name_iterator
#### typedef <em>_name_iterator</em>
Random-access iterator type for `_name_iterable`. Most operations are
`constexpr`, but dereferencing is `constexpr` if and only if `_to_string` is
`constexpr`. Mutating operators such as `operator++` are not `constexpr` due to
their nature &mdash; adding `1` is a `constexpr` alternative.
`constexpr`, but dereferencing is `constexpr` if and only if
[`_to_string`](#_to_string) is `constexpr`. Mutating operators such as
`operator++` are not `constexpr` due to their nature &mdash; adding `1` is a
`constexpr` alternative. You typically don't have to refer to this type
directly.
#### static constexpr? _name_iterable _names()
#### static constexpr? _name_iterable <em>_names</em>()
Access to the collection of declared constant names. For example:
Access to the sequence of declared constant names. For example:
for (size_t index = 0; index < Enum::_names().size(); ++index)
std::cout << Enum::_names()[index] << std::endl;
<em>for</em> (size_t index = 0; <em>index</em> < <em>Enum::_names().size()</em>; ++index)
std::cout << <em>Enum::_names()[index]</em> << std::endl;
or, using iterators:
for (Enum::_name_iterator iterator = Enum::_names().begin();
iterator != Enum::_names().end(); ++iterator) {
<em>for</em> (Enum::_name_iterator iterator = <em>Enum::_names().begin()</em>;
iterator != <em>Enum::_names().end()</em>; ++iterator) {
std::cout << *iterator << std::endl;
std::cout << <em>*iterator</em> << std::endl;
}
or, in $cxx11:
for (const char *name : Enum::_names())
std::cout << name << std::endl;
<em>for</em> (const char *name : <em>Enum::_names()</em>)
std::cout << <em>name</em> << std::endl;
`constexpr` if and only if `_to_string` is `constexpr`.
`constexpr` if and only if [`_to_string`](#_to_string) is `constexpr`.
### Integers
### Integer conversion
#### typedef _integral
Better Enums are already represented as integers at run time. Values of the
[running example](#RunningExample) type `Enum` are the same as `ints`. However,
`Enum` is a distinct type from `int` during type checking, the main difference
being that its range of valid values is restricted to only the ones you have
declared.
This section describes the various translations between `Enum` and `int` that
are available. Each one translates the type, but at run time, most are no-ops,
or validity checks followed by no-ops.
#### <em>typedef _integral</em>
The *underlying* or *representation* type of the Better Enum. For example,
`Enum::_integral` is the same type as `int`. Each Better Enum has the same size
and alignment requirement as its representation type. This is true even under
$cxx98.
and alignment requirement as its representation type.
#### member constexpr _integral _to_integral() const
#### member constexpr _integral <em>_to_integral</em>() const
No-op conversion of a Better Enum to a value of its representation type. For
example, `(+Enum::C)._to_integral() == 2`.
example,
#### static constexpr Enum _from_integral(_integral)
(+<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).
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.
#### static constexpr Enum <em>_from_integral</em>(_integral)
Checked conversion of an integer to a Better Enum value. The check runs in time
linear in the number of declared constants, but the conversion itself is a
no-op. Throws `std::runtime_error` if the given integer is not the numeric value
of one of the declared constants.
#### static constexpr Enum _from_integral_unchecked(_integral)
<em>Enum::_from_integral</em>(<em>2</em>); // Enum::C
<em>Enum::_from_integral</em>(<em>42</em>); // std::runtime_error
#### static constexpr optional<Enum> <em>_from_integral_nothrow</em>(_integral)
Checked conversion as [`_from_integral`](#_from_integral), but does not throw an
exception on failure. Returns an [optional value](#StructBetter_enumsoptional)
instead.
#### static constexpr Enum <em>_from_integral_unchecked</em>(_integral)
No-op unchecked conversion of an integer to a Better Enum value. If the given
integer is not the numeric value of one of the declared constants, the behavior
of all subsequent operations on the Better Enum value is undefined.
#### static constexpr optional<Enum> _from_integral_nothrow(_integral)
This is the direct inverse of [`_to_integral`](#_to_integral). Here are no-op
round trips between `int` and `Enum`:
Checked conversion as `_from_integral`, but does not throw an exception on
failure. Returns an [optional]() value instead.
<em>Enum::_from_integral_unchecked</em>(value.<em>_to_integral</em>());
<em>Enum::_from_integral_unchecked</em>(integer).<em>_to_integral</em>();
#### static constexpr bool _is_valid(_integral)
You should not use this function on untrusted input, however.
Evaluates to `true if and only if the given integer is the numeric value of one
of the declared constants.
#### static constexpr bool <em>_is_valid(_integral)</em>
Evaluates to `true` if and only if the given integer is the numeric value of one
of the declared constants. Running time is linear in the number of declared
constants.
%% class = api
%% description = Detailed description of the Better Enums API.

View File

@ -1,23 +1,73 @@
## Compiler support
Better Enums aims to support as many compilers as is reasonable. It has been
tested with clang++ and g++, and Visual C++:
Better Enums aims to support all major compilers. It is known to definitely work
on
- clang++ 3.3 to 3.6
- g++ 4.3 to 5.1
- Visual C++ 2013 Update 4, Visual C++ 2015 RC
- clang 3.3 to 3.6
- gcc 4.3 to 5.1
- Visual C++ 2013U4, 2015RC.
In principle, Better Enums can be used with any compiler that supports either
The library can be used with any compiler that supports either $cxx11, or $cxx98
with the `__VA_ARGS__` extension. This includes every version of gcc and clang I
have ever heard of, and Visual C++ down to 2005.
- $cxx11
- $cxx98 with the variadic macro (`__VA_ARGS__`) extension
To ensure that nothing is broken, every release of Better Enums is
[tested]($repo/tree/master/test) in multiple configuratins on the compilers
listed above. Testing includes the code in the tutorials, the unit tests, and a
multiple translation unit linking test. The full list of tested compilers and
configurations is given at [the end of this page](#TestedConfigurations).
To ensure that nothing is broken, every release of Better Enums is tested
automatically with a large number of combinations of compiler and configuration.
The full list is given at the end of this page.
### Compile-time reflection configurations
The tests include compiling and running unit tests, all the examples in the
demos and tutorials, and a multiple translation unit linking test.
Read this section if:
- you want to use Better Enums reflection at compile time, and need to know
exactly what features are supported on your compiler, or
- Better Enums is choosing the wrong configuration automatically and you need
to force a different choice.
All features of Better Enums are always available for run-time use. However, for
compile-time use, Better Enums has two main configurations: $cxx98 mode and
`constexpr` mode. Better Enums tries to detect which compiler is compiling it
and select the appropriate mode.
For performance reasons, `constexpr` mode is subdivided into a "fast"
`constexpr` mode and an
[opt-in](${prefix}OptInFeatures.html#CompileTimeNameTrimming) "full" (slow)
`constexpr` mode. The three modes can be ranked, with each next mode including
all the features of the preceding ones:
- $cxx98
- fast `constexpr`
- full `constexpr`
Only `_size` is supported at compile time in $cxx98 mode. Fast `constexpr` mode
adds all other members besides `_to_string` and `_names`. Full `constexpr` mode
supports those at compile time as well.
---
The mode selection code works as follows:
- First, as of the time of this writing, only clang and gcc support
`constexpr`. So, if you are using any other compiler, Better Enums is in
$cxx98 mode.
- If you are using gcc 4.7 or higher or clang, Better Enums uses those
compilers' predefined macros to detect whether `constexpr` support is
enabled with compiler flags. If so, it is in one of the `constexpr` mode.
Otherwise, it falls back to $cxx98 mode.
- The default `constexpr` mode is fast `constexpr`. If you want to enable
full `constexpr` mode for some or all of your enums, follow
[these](${prefix}OptInFeatures.html#CompileTimeNameTrimming) instructions.
If Better Enums picks the wrong mode, you can force `constexpr` mode by defining
`BETTER_ENUMS_CONSTEXPR` before including `enum.h`, typically by passing an
option to your compiler, or you can force $cxx98 mode by defining
`BETTER_ENUMS_NO_CONSTEXPR`.
If you are using a compiler for which Better Enums makes the wrong choice,
please [let me know](${prefix}Contact.html). I will fix it and you won't have to
define these macros anymore.
### Tested configurations
@ -70,3 +120,5 @@ g++44 -std=c++98
g++43 -std=c++0x -DBETTER_ENUMS_NO_CONSTEXPR
g++43 -std=c++98
~~~
%% description = Information about compiler support and feature detection.

15
doc/Contact.md Normal file
View File

@ -0,0 +1,15 @@
## Contact
- Send me an email: [antonbachin@yahoo.com](mailto:antonbachin@yahoo.com).
- Visit the [GitHub]($repo) project to open an issue or get a development
version.
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.
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.

40
doc/ExtendingLimits.md Normal file
View File

@ -0,0 +1,40 @@
## Extending limits
The `ENUM` macro makes heavy use of the preprocessor, and some of the internal
macros have size limits. There are two: on the number of constants you can
declare, and on the maximum length of a constant name under very specific
conditions. If you run into either one, you can extend the limit by following
the instructions on this page.
The second limit, on the maximum length of a constant name, applies only when
you are compiling an enum in
["full" `constexpr`](${prefix}OptInFeatures.html#CompileTimeNameTrimming) mode
*and* the constant has an initializer. Otherwise, your constants can have names
of arbitrary length.
The default limits are 64 constants in an enum and 23 characters for initialized
constants of full-`constexpr` enums. To extend:
1. Pick your desired limits. I will use 512 constants and 63 characters as an
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>.
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
`common/enum_macros.h` in your project.
4. Run `python make_macros.py 512 64 > common/enum_macros.h`.
5. Define `BETTER_ENUMS_MACRO_FILE <common/enum_macros.h>` before including
`enum.h`. This is typically done by supplying extra flags to the compiler
on the command line:
- For g++ and clang++, `-DBETTER_ENUMS_MACRO_FILE='<common/enum_macros.h>'`
- For VC++, `\DBETTER_ENUMS_MACRO_FILE='<common/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.

View File

@ -1,41 +0,0 @@
## Extending macro limits
The `ENUM` macro makes heavy use of the preprocessor. The preprocessor can't
perform intelligent operations on sequences of tokens with arbitrary lengths
&mdash; the operations must be bounded. The result is that there are two size
limits: on the number of constants that a Better Enum can have, and on the
maximum length of a constant name under certain conditions. If you run into
either of these limits, follow the steps on this page to increase them.
The conditions for the constant name length limit rarely apply. They are:
- you are compiling an enum with compile-time string trimming, a.k.a. a
"slow enum", which is only enabled when you do [this](), and
- you have a constant with an initializer.
Constants without initializers can always have arbitrarily-long names.
The default limits are 64 constants and 23 characters for names of slow enum
constants that have initializers. To extend these limits:
1. Pick your desired limits. I will use 512 constants and 63 characters as an
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 Better Enums distribution or
from [GitHub]().
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
`common/enum_macros.h`.
4. Run `python make_macros.py 512 64 > common/enum_macros.h`.
5. Define `BETTER_ENUMS_MACRO_FILE <common/enum_macros.h>` before including
`enum.h`. This is typically done by supplying extra flags to the compiler
on the command line:
- For g++ and clang++, `-DBETTER_ENUMS_MACRO_FILE='<common/enum_macros.h>'`
- For VC++, `\DBETTER_ENUMS_MACRO_FILE='<common/enum_macros.h>'`
6. Enjoy the looser limits. Just watch out &mdash; increasing the second
number can really slow down compilation of compile-time trimming enums.
7. You don't need `make_macros.py` anymore. It's not part of your build
process and you can delete it.

View File

@ -1,34 +1,41 @@
## Opt-in features
Better Enums has a few opt-in features that affect how enums are generated. The
default configuration is suitable for general use, but you might have more
stringent requirements. This page describes the optional features and how to
enable them.
Better Enums has two opt-in features. They are both "good," but they either hurt
compilation time or break compatibility with $cxx98, so they are disabled by
default. Read this page if you want to enable them.
$internal_toc
### Compile-time name trimming
This makes `_to_string` and `_names` `constexpr`, i.e. "full" `constexpr` mode.
To enable this for all enums, define `BETTER_ENUMS_CONSTEXPR_TO_STRING` before
including `enum.h`. Typically, you would pass an option to your compiler to do
this.
You can also enable this feature for individual enums instead, by declaring them
using the alternative `SLOW_ENUM` macro.
The feature is disabled because it increases compilation times by a factor of
about 4. Compilation is still relatively fast &mdash; you need about a dozen
slow enums to get the same penalty as including `iostream` &mdash; but it is
a steep penalty nonetheless. I don't think most people need this feature most of
the time, so it's too high a price to pay. If I improve compilation times to the
point where compile-time name trimming can be the default, I will simply
redefine `SLOW_ENUM` as `ENUM` and deprecate it, so your code will still work.
### Strict conversions
Each Better Enum is implicitly convertible to its member `enum _enumerated`
type. This is meant to support usage of Better Enums directly in `switch`
statements. When you write
This disables implicit conversions to underlying integral types. At the moment,
you can only enable this globally for all enums, by defining
`BETTER_ENUMS_STRICT_CONVERSION` before including `enum.h`.
switch (channel) {
...
}
the compiler applies the implicit conversion, and is also able to do case
exhaustiveness checking. Unfortunately, `_enumerated` is always a regular $cxx98
enum type, meaning that it has standard conversions to integral types. The end
result is `channel` is implicitly convertible all the way to `int` &mdash;
behavior that is often considered a violation of type safety.
In $cxx11, you can force Better Enums to declare an internal `enum class` type
to use for `switch` statements. Each enum will then be only convertible to its
`enum class`, and won't be implicitly convertible to integers. This is done by
defining `BETTER_ENUMS_STRICT_CONVERSION` before including `enum.h`. You would
typically do this on the compiler's command line.
The reason strict conversions aren't enabled by default in $cxx11 is that doing
so would break compatibility with the $cxx98 interface.
The reason Better Enums have implicit conversions to integral types in the first
place is that in $cxx98, Better Enums have to be implicitly convertible to their
member [`_enumerated`](${prefix}ApiReference.html#Typedef_enumerated) types to
be [usable](${prefix}tutorial/SafeSwitch.html) in `switch` statements. It is
possible to avoid this in $cxx11 and convert to `enum class` types instead, but
at the cost of breaking interface compatibility with $cxx98.
- The "weaker" incompatibility is that you could write a bunch of $cxx98 code
that relies on implicit integer conversions, and then try to switch to
@ -39,71 +46,23 @@ so would break compatibility with the $cxx98 interface.
issue, I decided not to do that.
- The "stronger" incompatibility is a difference in how `switch` cases must be
written. The syntaxes for the two variants, implicitly-converting and
strict, are mutually exclusive.
strict, are mutually exclusive. They differ by a `+` character.
Here they are:
// Default variant
switch (channel) {
<em>switch</em> (<em>channel</em>) {
case Channel::Red: break;
case Channel::Green: break;
case Channel::Blue: break;
}
// Strict variant
switch (channel) {
case +Channel::Red: break;
case +Channel::Green: break;
case +Channel::Blue: break;
<em>switch</em> (<em>channel</em>) {
case <em>+</em>Channel::Red: break;
case <em>+</em>Channel::Green: break;
case <em>+</em>Channel::Blue: break;
}
I would be very happy to make conversion to `enum class` the default whenever
`enum class` is available, but how to do so without breaking compatibility is so
far an open problem.
### Compile-time name trimming
Better Enums is able to do all of its work at compile time. There is one task,
however, at which my current method is pretty slow on modern compilers. The
performance is still reasonable, but it makes enums take about four times longer
to compile, compared to deferring the task to program startup. The task is
trimming stringized constant names.
The problem arises when the preprocessor stringizes an initializer. For example,
ENUM(Channel, int, Red = 1, Green, Blue);
results in an internal array, somewhere inside the generated Better Enum, that
looks like
names = {"Red = 1", "Green", "Blue"}
Before the name of `Channel::Red` can be returned to the user, the initializer
` = 1` must be trimmed off. This is the part that is slow to compile, and is
deferred to startup by default.
If you want to enable it at compile time, you have two options. The first is to
use an alternative `SLOW_ENUM` macro to declare your enum. It will enable
compile-time trimming for that enum only. If you only do this for a few enums,
you probably won't notice the difference in compilation time.
You can also enable compile-time trimming globally for all enums by defining
`BETTER_ENUMS_CONSTEXPR_TO_STRING` before including `enum.h`. Typically, you
would do this by supplying a command-line argument to your compiler.
The result of doing either one is that `_to_string` and `_names` will become
`constexpr` for your compile-time enjoyment.
The reason this option is not enabled by default when avaialble, besides the
fact that the current implementation is slow, is that I don't believe most users
need it most of the time.
As a note, the performance is not *that* bad. You still have to define on the
order of 10+ slow enums in order to slow compilation down as much as merely
including `iostream` does. However, it is shockingly slower than the faster
implementation, where you have the leeway to define 40+ enums before you reach
the same level of slowdown as `iostream` gives you.
There are enough other problems with slow enums, however, like potential symbol
pollution in the final binaries, that I decided to leave them as an opt-in
feature until they improve to the point where they can be the default.
%% description = Opting into features disabled by default for performance or
compatibility reasons.

32
doc/Performance.md Normal file
View File

@ -0,0 +1,32 @@
## Performance
A basic performance test is run on
[every compiler tested](${prefix}CompilerSupport.html#TestedConfigurations). It
doesn't try to be very accurate &mdash; it just stress-tests the compiler once
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
`enum.h` and declares 647 constants across 36 Better Enums.
- The [other file]($repo/blob/$version/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
fast as, including a single standard header such as `iostream`, then Better
Enums is fast enough for general use.
Results are given for select compilers and
[configurations](${prefix}CompilerSupport.html#CompileTimeReflectionConfigurations)
as ratios of how long it took to compile the Better Enums file to how long it
took to compile the `iostream` file. The less the ratio, the better. Ratios less
than 1 mean the enums compiled faster, and ratios greater than 1 mean `iostream`
compiled faster.
- clang 3.6, fast `constexpr`: 0.66
- clang 3.6, full `constexpr`: 2.25
- gcc 5.1, fast `constexpr`: 1.58
- gcc 5.1, full `constexpr`: 4.23
- VC2015RC, $cxx98: 1.18
%% description = Compilation performance testing results.

View File

@ -6,7 +6,7 @@ body {
font-weight: 300;
}
pre, code, samp {
pre, code, samp, h4, .contents ul {
font-family:
Consolas, "Liberation Mono", Menlo, "Courier New", Courier, monospace;
}
@ -17,7 +17,7 @@ pre {
border-radius: 5px;
overflow: scroll;
color: rgba(255, 255, 255, 0.6);
font-size: 14px;
font-size: 78%;
}
pre em {
@ -41,38 +41,25 @@ code, samp {
background-color: #EEF4F9;
padding: 1px 3px;
border-radius: 3px;
font-size: 14px;
font-size: 78%;
}
nav > *, header > *, main, footer {
.container {
max-width: 760px;
padding-left: 230px;
margin-left: 230px;
}
@media(max-width: 1240px) {
nav > *, header > *, main > *, footer {
padding-left: 150px;
@media (max-width: 1220px) {
.container {
margin-left: auto;
margin-right: auto;
}
}
@media(max-width: 1060px) {
nav > *, header > *, main > *, footer {
padding-left: 100px;
padding-right: 100px;
}
}
@media(max-width: 900px) {
nav > *, header > *, main > *, footer {
padding-left: 50px;
padding-right: 50px;
}
}
@media(max-width: 680px) {
nav > *, header > *, main > *, footer {
padding-left: 20px;
padding-right: 20px;
@media (max-width: 780px) {
.container {
margin-left: 10px;
margin-right: 10px;
}
}
@ -93,15 +80,24 @@ nav, .spacer {
}
nav a {
margin-left: 2em;
margin-right: 2em;
}
nav a.first {
margin-left: 0;
font-weight: 600;
font-size: 16px;
}
@media (max-width: 560px) {
nav {
position: initial;
}
.spacer {
display: none;
}
}
header {
background-color: #4C6F8C;
background: linear-gradient(#395E7E, #4A79A0);
@ -116,7 +112,7 @@ h1 {
font-weight: 100;
}
header > h2 {
header h2 {
margin: 0;
font-size: 24px;
font-weight: 100;
@ -124,7 +120,7 @@ header > h2 {
left: 3px;
}
header > h3 {
header h3 {
margin: 0;
font-size: 14px;
font-weight: 300;
@ -170,7 +166,7 @@ header a:hover {
text-decoration: none;
}
main a[href], footer a[href] {
.main a[href], footer a[href] {
background-color: #edd;
color: #844;
letter-spacing: -0.5px;
@ -217,7 +213,7 @@ span#note:target {
margin-left: 10px;
}
main > h3 {
.main h3 {
font-size: 30px;
font-weight: 100;
margin-top: 2em;
@ -230,6 +226,10 @@ h3 {
font-weight: inherit;
}
h3.contents {
font-size: 22px;
}
.pane pre {
font-size: 14px;
padding-top: 20px;
@ -238,13 +238,16 @@ h3 {
}
header {
position: relative;
overflow: hidden;
}
header .container {
position: relative;
}
div.back {
position: absolute;
bottom: -0.2em;
bottom: -0.35em;
left: -40px;
font-size: 288px;
font-weight: bold;
@ -265,15 +268,160 @@ div.back {
margin-top: 4em;
}
.tutorial-footer.next {
.tutorial-footer .next {
font-weight: 100;
font-size: 24px;
}
.tutorial-footer.next a[href] {
.tutorial-footer .next a[href] {
font-weight: 300;
}
li {
margin-top: 5px;
}
.blurbs {
padding-left: 0;
list-style-type: none;
margin-top: 30px;
}
.blurbs > li {
width: 45%;
float: left;
min-height: 5em;
}
.blurbs > li.even {
clear: both;
margin-left: 3%;
margin-right: 6%;
}
.blurbs strong {
font-weight: inherit;
font-size: 110%;
}
.blurbs em {
display: block;
margin-bottom: 1em;
font-size: 80%;
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%;
}
.act strong code {
font-size: 110%;
}
.resources > li {
margin-bottom: 3.5em;
}
.resources li ul, .resources li ol {
margin-top: 1.5em;
padding-left: 25px;
}
.splash {
text-align: center;
margin-left: -10%;
white-space: nowrap;
}
.splash pre {
display: inline-block;
}
.splash pre.left {
text-align: right;
color: inherit;
background-color: transparent;
}
.splash pre.right {
text-align: left;
}
@media (max-width: 700px) {
.splash {
margin-left: 0;
}
.splash pre.left {
display: none;
}
}
a[id] {
display: block;
position: relative;
top: -25px;
}
h4 {
font-weight: normal;
margin-top: 3em;
letter-spacing: -1px;
color: #888;
padding-top: 1em;
white-space: nowrap;
}
h4 em {
color: #555;
font-style: normal;
font-weight: bold;
}
.api ul.contents {
-webkit-columns: 300px 2;
-moz-columns: 300px 2;
columns: 300px 2;
}
.api ul.contents > li {
-webkit-column-break-inside: avoid;
page-break-inside: avoid;
break-inside: avoid;
}
.main h3 {
margin-top: 4em;
}
h3.contents {
margin-top: 2em;
}
.index .main h3 {
margin-top: 2em;
}
.api .contents ul {
font-size: 75%;
}
.api .contents > li {
margin-top: 0;
padding-bottom: 1em;
}

View File

@ -1,122 +1,165 @@
## Special values
Suppose your project has a convention where each enum has special *invalid* and
*default* values. With Better Enums, you can encode that directly at compile
time, and then access each enum's special values using syntax like
`Channel c = default_` and `Channel c = invalid`. This can make your code adapt
automatically to changes in enum definitions, as well as make it easier to read
and understand its intent.
*default* values &mdash; for example, `Enum::Invalid` is *invalid*, and the
first valid constant is *default*. With Better Enums, you can get the compiler
to enforce the convention. At the end of this demo, we will have defined
functions and templates that allow us to write:
---
~~~comment
<em>Channel channel</em> = <em>default_</em>;
<em>Channel channel</em> = <em>invalid</em>;
void do_something(<em>Channel channel</em>);
do_something(<em>default_</em>);
do_something(<em>invalid</em>);
~~~
The compiler will compute default and invalid values automatically, but the
programmer will also be able to override the choice. Obviously, the syntax above
is very legible and maintainable &mdash; the intent is clear and your code base
will respond automatically to changes in enum definitions.
$internal_toc
### Invalid values
Let's start by defining the invalid values.
#include <iostream>
#include <stdexcept>
<em>#include <enum.h></em>
### Invalid
Perhaps the invalid value is usually called `Invalid`, but not in all enums. You
can encode that using a function template for the common case, and a macro that
creates specializations:
Perhaps the convention is that the invalid value is usually called `Invalid`,
but not for all enums. We will encode that using a template function. The
unspecialized version will encode the default policy:
<em>template</em> <<em>typename Enum</em>>
constexpr Enum find_invalid() { return Enum::Invalid; }
constexpr <em>Enum invalid_impl</em>() { return <em>Enum::Invalid</em>; }
A macro allows us to override the invalid value by specializing the template:
<em>#define OVERRIDE_INVALID</em>(<em>Enum</em>, <em>Value</em>) \
template<> \
constexpr Enum <em>find_invalid<Enum></em>() { return <em>Enum::Value</em>; }
constexpr <em>Enum invalid_impl</em><<em>Enum</em>>() { return <em>Enum::Value</em>; }
Now, you can declare enums like these:
Now, we can declare enums like these:
ENUM(<em>Channel</em>, int, Red, Green, Blue, <em>Invalid</em>)
// Invalid is the invalid value by default
ENUM(<em>Compression</em>, int, <em>Undefined</em>, None, Huffman)
<em>OVERRIDE_INVALID</em>(<em>Compression</em>, <em>Undefined</em>)
OVERRIDE_INVALID(<em>Compression</em>, <em>Undefined</em>)
and use them:
static_assert(<em>find_invalid</em><<em>Channel</em>>() == <em>+Channel::Invalid</em>, "");
static_assert(<em>find_invalid</em><<em>Compression</em>>() == <em>+Compression::Undefined</em>, "");
static_assert(<em>invalid_impl</em><<em>Channel</em>>() == +<em>Channel::Invalid</em>, "");
static_assert(<em>invalid_impl</em><<em>Compression</em>>() == +<em>Compression::Undefined</em>, "");
This even supports enums that don't have an invalid value at all. As long as
they don't have a constant called `Invalid`, you will get a compile-time error
if you try to call `invalid()` on them &mdash; as you probably should!
if you try to call `invalid_impl<>()` on them &mdash; as you probably should!
### Default
### Default values
To encode the policy on default values, we need to do a compile-time check that
the first value is not invalid. Otherwise, the technique is the same.
Perhaps here the convention is the first value that is not invalid is default,
unless, again, overridden by the programmer. This can be encoded using only a
slightly more complex template function for the general case:
template <typename Enum>
constexpr Enum find_default()
<em>template</em> <<em>typename Enum</em>>
constexpr <em>Enum default_imp</em>l()
{
return
Enum::_size < 2 ?
<em>Enum::_size < 2 ?
throw std::logic_error("enum has no valid constants") :
Enum::_values()[0] == find_invalid<Enum>() ?
Enum::_values()[0] == invalid_impl<Enum>() ?
Enum::_values()[1] :
Enum::_values()[0];
Enum::_values()[0]</em>;
}
#define OVERRIDE_DEFAULT(Enum, Value) \
static_assert(Enum::Value != Enum::Invalid, \
#Enum ": default cannot equal invalid"); \
The above code gives us the first value if it is not invalid, otherwise the
second value.
The companion macro for overriding the choice of default value is almost the
same as it was for invalid. The difference is that we do an extra sanity check
to make sure the programmer doesn't declare the invalid value to be the default.
If the sanity check fails, we produce a nice error message. Again, we are
assuming that this is dictated by policy.
<em>#define OVERRIDE_DEFAULT</em>(<em>Enum</em>, <em>Value</em>) \
static_assert(<em>Enum::Value</em> != <em>Enum::Invalid</em>, \
<em>#Enum ": default cannot equal invalid"</em>); \
template<> \
constexpr Enum find_default<Enum>() { return Enum::Value; }
constexpr <em>Enum default_impl</em><<em>Enum</em>>() { return <em>Enum::Value</em>; }
Usage:
And, as before, the usage:
static_assert(find_default<Channel>() == +Channel::Red, "");
static_assert(find_default<Compression>() == +Compression::None, "");
static_assert(<em>default_impl</em><<em>Channel</em>>() == +<em>Channel::Red</em>, "");
static_assert(<em>default_impl</em><<em>Compression</em>>() == +<em>Compression::None</em>, "");
And, if you do
ENUM(Answer, int, Yes, No, Invalid)
// OVERRIDE_DEFAULT(Answer, Invalid)
ENUM(<em>Answer</em>, int, Yes, No, <em>Invalid</em>)
// OVERRIDE_DEFAULT(<em>Answer</em>, <em>Invalid</em>)
you will get a helpful compile-time error saying
`Answer: default cannot equal invalid`.
### Making the syntax nicer
For the final touch, we will make the syntax better by introducing new
"keywords" called `default_` and `invalid` in such a way that we cause the
compiler to do type inference:
At this point, our policy is encoded by the ugly-looking functions
`invalid_impl` and `default_impl`. We want a nicer syntax. The main reason we
don't just use these functions directly is that the compiler wouldn't infer
their template arguments from the context. For example, we would have to write
things like
template <typename Enum>
struct assert_enum {
using check = typename Enum::_enumerated;
using type = Enum;
~~~comment
Channel channel = invalid_impl<Channel>();
~~~
which is unfortunate, because it results in repetition.
In this section, we introduce two global objects called `invalid` and `default_`
that will implicitly convert to any Better Enum type, and provide the invalid
or default value, respectively, when they do so. They will act as new
"keywords".
<em>struct invalid_t</em> {
template <<em>typename To</em>>
constexpr <em>operator To</em>() const { return <em>invalid_impl<To>()</em>; }
};
struct invalid_t {
template <typename To>
constexpr operator To() const { return find_invalid<To>(); }
template <typename To>
constexpr To convert() const { return find_invalid<To>(); }
<em>struct default_t</em> {
template <<em>typename To</em>>
constexpr <em>operator To</em>() const { return <em>default_impl<To>()</em>; }
};
struct default_t {
template <typename To>
constexpr operator To() const { return find_default<To>(); }
};
constexpr <em>invalid_t invalid</em>{};
constexpr <em>default_t default_</em>{};
constexpr invalid_t invalid{};
constexpr default_t default_{};
As you can see, both of these provide the families of implicit conversions that
we need. Now, we can test:
static_assert(+Channel::Invalid == invalid, "");
static_assert(+Compression::Undefined == invalid, "");
static_assert(+<em>Channel::Invalid</em> == <em>invalid</em>, "");
static_assert(+<em>Compression::Undefined</em> == <em>invalid</em>, "");
static_assert(+Channel::Red == default_, "");
static_assert(+Compression::None == default_, "");
static_assert(+<em>Channel::Red</em> == <em>default_</em>, "");
static_assert(+<em>Compression::None</em> == <em>default_</em>, "");
We can now have nice code such as this:
Finally, we can have nice code such as this:
void dump(<em>Channel channel</em>)
{
std::cout << channel._to_string() << std::endl;
}
int main()
{
Channel channel = default_;
std::cout << channel._to_string() << std::endl;
dump(<em>invalid</em>);
<em>Channel channel</em> = <em>default_</em>;
dump(channel);
return 0;
}
@ -126,3 +169,6 @@ We can now have nice code such as this:
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.

View File

@ -1,6 +1,6 @@
## Bit sets
If you want to use `std::bitset` or a similar library to use enums as keys into
If you want to use `std::bitset` or a similar library to have enums be keys into
a bit set, you need to know the number of bits at compile time. You can easily
automate this with Better Enums, even when constants are not declared in
increasing order.
@ -10,41 +10,60 @@ increasing order.
We simply need to find the maximum value of any given enum type.
#include <bitset>
#include <enum.h>
#include <iostream>
<em>#include <enum.h></em>
template <typename Enum>
constexpr Enum max_loop(Enum accumulator, size_t index)
template <<em>typename Enum</em>>
constexpr <em>Enum max_loop</em>(Enum accumulator, size_t index)
{
return
index >= Enum::_size ? accumulator :
<em>index >= Enum::_size ? accumulator :
Enum::_values()[index] > accumulator ?
max_loop<Enum>(Enum::_values()[index], index + 1) :
max_loop<Enum>(accumulator, index + 1);
max_loop<Enum>(accumulator, index + 1)</em>;
}
template <typename Enum>
constexpr Enum max()
template <<em>typename Enum</em>>
constexpr <em>Enum max</em>()
{
return max_loop<Enum>(Enum::_values()[0], 1);
return <em>max_loop<Enum>(Enum::_values()[0], 1)</em>;
}
And use that to declare a bit set template:
template <typename Enum>
using EnumSet = std::bitset<max<Enum>()._to_integral() + 1>;
template <<em>typename Enum</em>>
using <em>EnumSet</em> = <em>std::bitset</em><<em>max<Enum>()._to_integral()</em> + <em>1</em>>;
Then rest is straightforward. The only issue is that, in $cxx11, it is necessary
to keep calling `to_integral` on the enums when passing them to `bitset`
functions. You may want to implement a more enum-friendly bit set type, or
overload unary `operator -`.
Now, we can have bit sets that are wide enough to hold whatever range we
declared. We just declare enums, and the numeric values of their constants will
be bit indices. The rest is straightforward.
ENUM(Channel, int, Red, Green, Blue)
ENUM(Depth, int, TrueColor = 1, HighColor = 0)
ENUM(<em>EFLAGS</em>, int,
<em>Carry</em>, <em>Parity</em> = 2, <em>Adjust</em> = 4, <em>Zero</em>, <em>Sign</em>, <em>Trap</em>, <em>Interrupt</em>, <em>Direction</em>,
<em>Overflow</em>, <em>NestedTask</em> = 14, <em>Resume</em> = 16, <em>V8086</em>, <em>AlignmentCheck</em>,
<em>CPUIDPresent</em> = 21)
int main()
{
EnumSet<Channel> channels;
EnumSet<Depth> depths;
<em>EnumSet</em><<em>EFLAGS</em>> eflags = <em>1 << EFLAGS::Carry</em> | <em>1 << EFLAGS::Zero</em>;
if (eflags.test(<em>EFLAGS::Carry</em>))
eflags.set(<em>EFLAGS::Trap</em>);
std::cout << <em>eflags</em> << std::endl;
return 0;
}
---
If we want bit sets of fixed known width instead, we can use the code above to
check that we haven't declared any bit indices out of range:
~~~comment
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.

View File

@ -1,117 +1,173 @@
## Semi-quine
Let's make a Better Enum compose its own definition. It won't be literally as
defined, since we will lose some information about initializers, but we will be
able to preserve their numeric values. We will reserve the memory buffers at
compile time.
Let's make a Better Enum assemble its own definition in memory. It won't be
literally as defined, since we will lose the exact initializer expressions, but
we will be able to preserve the numeric values. We will reserve the memory
buffer for the definition at compile time.
There are actually better ways to do this than shown here. You could define a
macro that expands to an `ENUM` declaration and also stringizes it. The point
here is to show some of the reflective capabilities of Better Enums, so you can
adapt them for cases where a macro is not sufficient.
Ok, so it's not really a quine, because we won't be writing all the code needed
to generate the definition to the buffer as well. And, there are better ways to
dump the definition than shown here. You could simply define a macro that
expands to an `ENUM` declaration and also stringizes it.
But that's not the point here. The point of this page is to show some of the
reflective capabilities of Better Enums, so you can adapt them for cases where a
macro is not sufficient :)
$internal_toc
---
#include <cassert>
#include <cstdio>
#include <iostream>
#ifndef BETTER_ENUMS_CONSTEXPR_TO_STRING
#define BETTER_ENUMS_CONSTEXPR_TO_STRING
---
First, we will need
[full compile-time reflection](${prefix}OptInFeatures.html#CompileTimeNameTrimming),
since we will be calling `_to_string`. Let's make sure it's enabled by defining
`BETTER_ENUMS_CONSTEXPR_TO_STRING` before including `enum.h`:
#ifndef <em>BETTER_ENUMS_CONSTEXPR_TO_STRING</em>
#define <em>BETTER_ENUMS_CONSTEXPR_TO_STRING</em>
#endif
#include <enum.h>
<em>#include <enum.h></em>
#define HIGH_COLOR 0
Now, let's declare some enums to dump later:
ENUM(Channel, int, Red, Green, Blue)
ENUM(Depth, int, TrueColor = 1, HighColor = HIGH_COLOR)
ENUM(<em>Channel</em>, int, Red, Green, Blue)
ENUM(<em>Depth</em>, int, TrueColor = 1, HighColor = 0)
First, we need to be able to get the length of each definition above. We will
### Computing the size of the buffer
First, we need to be able to get the length of each declaration above. We will
assume that the underlying type is always `int`, and that the spacing convention
is followed as above. This allows us to write:
is followed as above.
constexpr size_t value_length(int n, int bound = 10, size_t digits = 1)
First, let's get the lengths of basic components:
// Returns the length of the string representation of the number n
constexpr <em>size_t value_length</em>(int <em>n</em>, int bound = 10, size_t digits = 1)
{
return
n < bound ? digits : value_length(n, bound * 10, digits + 1);
<em>n < bound ? digits : value_length(n, bound * 10, digits + 1)</em>;
}
constexpr size_t string_length(const char *s, size_t index = 0)
// Returns the length of s
constexpr <em>size_t string_length</em>(const char *<em>s</em>, size_t index = 0)
{
return s[index] == '\0' ? index : string_length(s, index + 1);
return <em>s[index] == '\0' ? index : string_length(s, index + 1)</em>;
}
template <typename Enum>
constexpr size_t constants_length(size_t index = 0, size_t accumulator = 0)
Now, the length of the constant declaration. Here is where we lose information
about initializers. We are going to format the constant declarations like this:
~~~comment
Red = 0, Green = 1, Blue = 2
TrueColor = 1, HighColor = 0
~~~
This is because Better Enums doesn't provide a way to know what the exact
initializer was or whether there even was one &mdash; just the numeric value of
each constant. If we were trying to be clever, we could avoid formatting
initializers for sequential values, but I won't go through this exercise here.
// Returns the length of the constants portion of the declaration of Enum,
// as described above.
template <<em>typename Enum</em>>
constexpr <em>size_t constants_length</em>(size_t index = 0, size_t accumulator = 0)
{
return
index >= Enum::_size ? accumulator :
<em>index >= Enum::_size ? accumulator :
constants_length<Enum>(
index + 1, accumulator
+ string_length(", ")
+ string_length(Enum::_names()[index])
+ string_length(" = ")
+ value_length(
Enum::_values()[index]._to_integral()));
Enum::_values()[index]._to_integral()))</em>;
}
template <typename Enum>
constexpr size_t declaration_length()
Finally, we can combine these to get the length of the formatted declaration of
the whole enum:
// Returns the length of the whole declaration of Enum, assuming the
// underlying type is int, and the constants are initialized as assumed by
// constants_length() above.
template <<em>typename Enum</em>>
constexpr <em>size_t declaration_length</em>()
{
return
string_length("ENUM(")
<em>string_length("ENUM(")
+ string_length(Enum::_name())
+ string_length(", int")
+ constants_length<Enum>()
+ string_length(")");
+ string_length(")")</em>;
}
Now, we can declare:
char channel_definition[declaration_length<Channel>() + 1];
char depth_definition[declaration_length<Depth>() + 1];
And finally, the formatting function:
### Formatting the enums
template <typename Enum>
size_t format(char *buffer)
Now, we can declare the buffers. The memory will be reserved at load time by the
binary's loader. The extra one byte in each buffer is for the null terminator.
<em>char</em> channel_definition[<em>declaration_length<Channel>() + 1</em>];
<em>char</em> depth_definition[<em>declaration_length<Depth>() + 1</em>];
Let's also create the formatting function. This is executed at run time, but we
will be giving it pointers to our statically-allocated buffers. It will format
the enum declaration and then return the number of bytes it wrote to the buffer,
so that we can do a sanity check on it.
template <<em>typename Enum</em>>
<em>size_t format</em>(char *<em>buffer</em>)
{
size_t offset = 0;
offset += std::sprintf(buffer, "ENUM(%s, int", Enum::_name());
offset += std::sprintf(buffer, <em>"ENUM(%s, int", Enum::_name()</em>);
for (Enum value : Enum::_values()) {
<em>for</em> (<em>Enum value</em> : <em>Enum::_values()</em>) {
offset +=
std::sprintf(buffer + offset,
", %s = %i",
value._to_string(), value._to_integral());
<em>", %s = %i",
value._to_string(), value._to_integral()</em>);
}
offset += std::sprintf(buffer + offset, ")");
offset += std::sprintf(buffer + offset, <em>")"</em>);
return offset;
return <em>offset</em>;
}
### Checking our work
Now, we can write and run this code.
int main()
{
size_t channel_length = format<Channel>(channel_definition);
size_t channel_length = <em>format<Channel></em>(channel_definition);
assert(channel_length + 1 == sizeof(channel_definition));
size_t depth_length = format<Depth>(depth_definition);
size_t depth_length = <em>format<Depth></em>(depth_definition);
assert(depth_length + 1 == sizeof(depth_definition));
std::cout << channel_definition << std::endl;
std::cout << depth_definition << std::endl;
std::cout << <em>channel_definition</em> << std::endl;
std::cout << <em>depth_definition</em> << std::endl;
return 0;
}
This outputs:
It prints:
~~~comment
ENUM(Channel, int, Red = 0, Green = 1, Blue = 2)
ENUM(Depth, int, TrueColor = 1, HighColor = 0)
~~~
This does have the advantage of not depending on anything else defined in the
program, which isn't as easy to achieve with stringization.
%% description = Contrived example that shows static memory allocation.

View File

@ -16,6 +16,8 @@ CXX_EXTENSION = "cc"
templates = {}
generated = []
def load_templates():
listing = os.listdir(TEMPLATE_DIRECTORY)
@ -45,7 +47,7 @@ def path_to_md(relative_path):
return os.path.splitext(relative_path)[0] + ".md"
def path_to_output(relative_path):
path = os.path.join(OUTPUT_DIRECTORY, templates["version"], relative_path)
path = os.path.join(OUTPUT_DIRECTORY, relative_path)
directory = os.path.dirname(path)
if not os.path.lexists(directory):
@ -54,7 +56,7 @@ def path_to_output(relative_path):
return path
def remove_output_directory():
path = os.path.join(OUTPUT_DIRECTORY, templates["version"])
path = OUTPUT_DIRECTORY
if os.path.lexists(path):
shutil.rmtree(path)
@ -66,9 +68,13 @@ def compose_page(relative_path, definitions):
if html_file == "index.html":
canonical = templates["location"]
definitions["title"] = \
definitions["project"] + " - " + definitions["title"]
definitions["project"] + " - " + definitions["title"]
else:
canonical = os.path.join(templates["location"], "current", html_file)
canonical = os.path.join(templates["location"], html_file)
definitions["title"] = \
definitions["title"] + " - " + definitions["project"]
generated.append(canonical)
prefix = re.sub("[^/]+", r"..", os.path.split(relative_path)[0])
if len(prefix) > 0:
@ -77,6 +83,9 @@ def compose_page(relative_path, definitions):
definitions["canonical"] = canonical
definitions["prefix"] = prefix
if "class" not in definitions:
definitions["class"] = ""
text = templates["page"]
text = scrub_comments(text)
@ -84,13 +93,12 @@ def compose_page(relative_path, definitions):
text = apply_template(text, definitions)
text = scrub_comments(text)
text = "<!-- Automatically generated - edit the templates! -->\n\n" + text
text = "<!-- Generated automatically - edit the templates! -->\n\n" + text
return text
def write_page(relative_path, text):
html_file = path_to_html(relative_path)
path = path_to_output(html_file)
def write_file(relative_path, text):
path = path_to_output(relative_path)
stream = open(path, "w")
try:
@ -98,6 +106,9 @@ def write_page(relative_path, text):
finally:
stream.close()
def write_page(relative_path, text):
write_file(path_to_html(relative_path), text)
def copy_static_file(relative_path):
output_path = path_to_output(relative_path)
shutil.copy(relative_path, output_path)
@ -127,11 +138,8 @@ def process_threaded(directory):
for file in sources:
definitions = read_extended_markdown(file)
title_components = re.split("[ -]+", definitions["title"])
title_components = map(lambda s: s.capitalize(), title_components)
html_title = "".join(title_components)
html_title = filter(lambda c: c not in ",!:-", html_title)
html_file = os.path.join(directory, html_title + ".html")
transformed_title = transform.camel_case(definitions["title"])
html_file = os.path.join(directory, transformed_title + ".html")
source_file = \
os.path.splitext(os.path.basename(file))[0] + "." + CXX_EXTENSION
@ -166,6 +174,21 @@ def process_threaded(directory):
templates[directory + "_toc"] = text
def generate_sitemap():
text = ""
text += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
text += "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n"
for url in generated:
text += " <url>\n"
text += " <loc>%s</loc>\n" % url
text += " </url>\n"
text += "</urlset>\n"
write_file("sitemap.xml", text)
def main():
load_templates()
@ -180,5 +203,7 @@ def main():
copy_static_file("better-enums.css")
generate_sitemap()
if __name__ == "__main__":
main()

View File

@ -1,125 +1,199 @@
Have you noticed the awkward situation with enums in $cxx? They are missing
basic reflective features, such as string conversions. You are forced to put
them through big `switch` statements and write duplicate enum names. It's a
maintenance nightmare.
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.
$be is a short header file that gives you rich, reflective enums, with the
nicest syntax yet seen. Just include it, and you are ready to go. You get
scoped, sized, printable, iterable enums that support initializers and still
play nice with `switch` case checking!
Here's how to use a Better Enum:
<div class="panes">
<div class="pane left">
<h3>$cxx11</h3>
<div class="splash">
<pre class="left">enable
<pre>#include &lt;iostream&gt;
<em>#include &lt;enum.h&gt;</em>
declare
<em>ENUM(Channel, int,
Red = 1, Green, Blue)</em>
int main()
{
<em>Channel c = Channel::Red</em>;
std::cout &lt;&lt; <em>c._to_string()</em>;
parse
print
<em>for (Channel c : Channel::_values())</em>
std::cout &lt;&lt; <em>c._to_string()</em>;
<em>switch (c)</em> {
<em>case Channel::Red</em>:
return <em>c._to_integral()</em>;
<em>case Channel::Green</em>: return 15;
<em>case Channel::Blue</em>: return 42;
}
count
iterate
switch
safe cast
at compile time</pre>
<pre class="right"><em>#include</em> &lt;<em>enum.h</em>&gt;
<em>ENUM</em>(<em>Channel</em>, <em>int</em>, <em>Red</em> = <em>1</em>, <em>Green</em>, <em>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>);
<em>switch</em> (<em>c</em>) {
<em>case Channel::Red</em>: // ...
<em>case Channel::Green</em>: // ...
<em>case Channel::Blue</em>: // ...
}
<em>constexpr</em> Channel c =
<em>Channel::_from_string("Blue")</em>;</pre>
</div>
<div class="pane right">
<h3>$cxx98</h3>
<pre>#include &lt;iostream&gt;
<em>#include &lt;enum.h&gt;</em>
<em>ENUM(Channel, int,
Red = 1, Green, Blue)</em>
Channel c = <em>Channel::_from_integral(3)</em>;
int main()
{
<em>Channel c = Channel::Red</em>;
std::cout &lt;&lt; <em>c._to_string()</em>;
for (size_t i = 0;
<em>i < Channel::_size</em>; ++i) {
c = <em>Channel::_values()[i]</em>;
std::cout &lt;&lt; <em>c._to_string()</em>;
}
<em>switch (c)</em> {
<em>case Channel::Red</em>:
return <em>c._to_integral()</em>;
<em>case Channel::Green</em>: return 15;
<em>case Channel::Blue</em>: return 42;
}
}</pre>
</div>
<div class="hack"></div>
<em>constexpr</em> Channel c = <em>Channel::_from_string("Blue")</em>;</pre>
</div>
To install, simply <a $download>download enum.h</a> and add it to your project.
### What do you get?
That's all. The library is header-only and has no dependencies. It is published
under the BSD license, so you can do pretty much anything you want with it.
<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>
<em>
Have the compiler do additional enum processing using your own
templates or <code>constexpr</code> functions.
</em>
</li>
---
<li class="even">
<strong>Non-contiguous sequences</strong>
<em>
Iteration and count much easier to maintain than with an extra "count"
constant and making assumptions.
</em>
</li>
<li>
<strong>Plays nice with <code>switch</code></strong>
<em>
Use a Better Enum like a built-in <code>enum</code>, and still have the
compiler do case checking.
</em>
</li>
Better Enums is under active development and will always be supported. Follow
the project on [GitHub]($repo) for updates.
<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>
<strong>Don't repeat yourself</strong>
<em>
No more unmaintanable maps or <code>switch</code> statements for
converting enums to strings.
</em>
</li>
---
<li class="even">
<strong>No build-time generator needed</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>.
</em>
</li>
<div class="panes">
<div class="pane left">
<h3>Tutorials</h3>
<ol>
$tutorial_toc
</ol>
</div>
<div class="pane right">
<h3>Advanced demos</h3>
<ul>
$demo_toc
</ul>
</div>
</div>
<div class="panes">
<div class="pane left">
<h3>Reference</h3>
<ul>
<li><a href="${prefix}ApiReference.html">API reference</a></li>
<li><a href="${prefix}OptInFeatures.html">Opt-in features</a></li>
<li>
<a href="${prefix}ExtendingMacroLimits.html">Extending macro limits</a>
</li>
<li><a href="${prefix}CompilerSupport.html">Compiler support</a></li>
<li><a href="${prefix}performance.html">Performance</a></li>
</ul>
</div>
</div>
<li class="even">
<strong>No external dependencies</strong>
<em>
Uses standard $cxx and supported on major compilers. Installation is
simple &mdash; just download <code>enum.h</code>.
</em>
</li>
<li>
<strong>Free and open source</strong>
<em>
Released under the BSD license for use in any project, free or commercial.
</em>
</li>
</ul>
<div class="hack"></div>
<!-- Contact -->
### It's what built-in enums ought to do.
<!-- omfg -->
The library notionally <em>extends</em> $cxx, adding oft-needed features.
<!-- Development blurb -->
<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>
<!-- Prompts and such -->
<div class="hack"></div>
### Resources
<ul class="blurbs resources">
<li class="even">
<a id="Tutorial"></a>
<strong>Tutorial</strong>
<ol>
$tutorial_toc
</ol>
</li>
<li>
<strong>Reference</strong>
<ul>
<li><a href="${prefix}ApiReference.html">API reference</a></li>
<li><a href="${prefix}CompilerSupport.html">Compiler support</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}Performance.html">Performance</a></li>
</ul>
</li>
<li class="even">
<a id="CompileTimeDemos"></a>
<strong>Compile-time demos</strong>
<ul>
$demo_toc
</ul>
</li>
</ul>
<div class="hack"></div>
%% 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.
%% class = index

View File

@ -1,7 +0,0 @@
## Performance
This is a placeholder page &mdash; the only testing done so far shows that
compiling a large (30+) enums on clang is faster than merely including
`iostream`. More conclusive information coming today or in a few days! For the
clang test, compilation of all those Better Enums takes about 60% of the time
that processing `iostream` does.

View File

@ -1,6 +1,13 @@
<p>
The code on this page is an advanced demo 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 compile-time code you can
write on top of Better Enums. You can <a href="$source">download</a> it and
try it out.
</p>
$demo_body
<section class="tutorial-footer">
<p class="up">
Return to the <a href="${prefix}index.html#CompileTimeDemos">demo index</a>.
</p>
</section>

View File

@ -1,2 +1,2 @@
href="https://raw.githubusercontent.com/aantron/better-enums/$ref/enum.h"
href="https://raw.githubusercontent.com/aantron/better-enums/$version/enum.h"
download

View File

@ -1,11 +1,14 @@
</main>
</div>
</div>
<footer>
Copyright &copy; 2015 Anton Bachin. Released under the BSD 2-clause license.
See <a href="https://github.com/aantron/better-enums/blob/master/LICENSE">
LICENSE</a>.
<br />
This page is part of the documentation for Better Enums $version.
<div class="container">
Copyright &copy; 2015 Anton Bachin. Released under the BSD 2-clause license.
See <a href="https://github.com/aantron/better-enums/blob/master/LICENSE">
LICENSE</a>.
<br />
This page is part of the documentation for Better Enums $version.
</div>
</footer>
</body>

View File

@ -6,7 +6,7 @@
<title>$title</title>
<link rel="canonical" href="$canonical" />
<!-- <meta name="description" content="$description" /> -->
<meta name="description" content="$description" />
<meta name="author" content="Anton Bachin" />
<meta name="viewport" content="width=device-width" />
@ -14,30 +14,30 @@
<link rel="stylesheet" href="${prefix}better-enums.css" />
</head>
<body>
<body class="$class">
<nav>
<div>
<div class="container">
<a class="first" $download>Download</a>
<a href="$repo">GitHub</a>
<a href="${prefix}index.html">Home</a>
<a href="${prefix}tutorial/HelloWorld.html">Tutorial</a>
<!-- <a href="https://github.com/aantron/better-enums/tree/master/example">
Samples
</a> -->
<!-- <a href="${prefix}api.html">Reference</a> -->
<!-- <a href="mailto:antonbachin@yahoo.com">Contact</a> -->
<a href="${prefix}ApiReference.html">Reference</a>
<a href="${prefix}Contact.html">Contact</a>
</div>
</nav>
<div class="spacer">&nbsp;</div>
<header>
<div class="back">{}</div>
<div class="container">
<div class="back">{}</div>
<h1><a href="${prefix}index.html">Better Enums</a></h1>
<h2>Fast, intuitive enums for $cxx</h2>
<h3>Open-source under the BSD license</h3>
<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>
</div>
</header>
<main>
<div class="main">
<div class="container">

View File

@ -4,6 +4,7 @@
</p>
<p class="up">
Or, return to the <a href="${prefix}index.html">home page</a>.
Or, return to the
<a href="${prefix}index.html#Tutorial">tutorial index</a>.
</p>
</section>

View File

@ -1 +0,0 @@
master

View File

@ -1 +1 @@
master
0.9.0

View File

@ -9,7 +9,7 @@ import mistune
import re
import sys
parser = argparse.ArgumentParser(description = "Translate markdown tutorials.",
parser = argparse.ArgumentParser(description = "Translate markdown pages.",
epilog = "At least one output file must be specified")
parser.add_argument("--o-html", metavar = "HTML", dest = "html_file",
help = "HTML output file name")
@ -46,17 +46,44 @@ def pretty_print(text, prefix, start_with_prefix = True):
return result
def camel_case(text):
components = re.split("[ -]+", text)
components = map(lambda s: s.capitalize(), components)
result = "".join(components)
result = filter(lambda c: c not in ",!:-$()", result)
return result
class HtmlRenderer(mistune.Renderer):
def __init__(self):
super(HtmlRenderer, self).__init__()
self._definitions = {}
self._table_of_contents = []
self._second_level = None
def header(self, text, level, raw = None):
if level == 2:
if "title" not in self._definitions:
self._definitions["title"] = text
return super(HtmlRenderer, self).header(text, level, raw)
if level == 3:
anchor_name = camel_case(text)
prefix = "<a id=\"%s\"></a>" % anchor_name
self._second_level = []
self._table_of_contents.append(
(anchor_name, text, self._second_level))
elif level == 4:
anchor_text = re.search("<em>(.+)</em>", text).group(1)
anchor_name = camel_case(anchor_text)
prefix = "<a id=\"%s\"></a>" % anchor_name
self._second_level.append((anchor_name, anchor_text))
else:
prefix = ""
return prefix + super(HtmlRenderer, self).header(text, level, raw)
def paragraph(self, text):
if text.startswith("%%"):
@ -76,7 +103,6 @@ class HtmlRenderer(mistune.Renderer):
escaped = mistune.escape(re.sub("\n*$", "", code))
replaced = re.sub("&lt;em&gt;", "<em>", escaped)
replaced = re.sub("&lt;/em&gt;", "</em>", replaced)
replaced = re.sub("#(ifn?def|endif).*\n?", "", replaced)
if lang == "comment":
start_tag = "<pre class=\"comment\">"
@ -86,6 +112,26 @@ class HtmlRenderer(mistune.Renderer):
return start_tag + replaced + "</pre>"
def definitions(self):
toc_text = "<a id=\"contents\"></a>"
toc_text += "<h3 class=\"contents\">Contents</h3>"
toc_text += "<ul class=\"contents\">"
for entry in self._table_of_contents:
anchor, title, second_level = entry
toc_text += "<li><a href=\"#%s\">%s</a>" % (anchor, title)
if len(second_level) > 0:
toc_text += "<ul>"
for entry in second_level:
toc_text += "<li><a href=\"#%s\">%s</a></li>" % entry
toc_text += "</ul>"
toc_text += "</li>"
toc_text += "</ul>"
self._definitions["internal_toc"] = toc_text
return self._definitions
def to_html(text):
@ -95,6 +141,13 @@ def to_html(text):
definitions["body"] = html
return definitions
def clean_text(text):
text = re.sub("<(?P<tag>[^> ]+)[^>]*>(.*?)</(?P=tag)>", "\g<2>", text)
text = re.sub("<(?P<tag>[^> ]+)[^>]*>(.*?)</(?P=tag)>", "\g<2>", text)
text = re.sub("&mdash;", "-", text)
text = re.sub("\$cxx", "C++", text)
return text
class CxxRenderer(mistune.Renderer):
def __init__(self):
super(CxxRenderer, self).__init__()
@ -109,18 +162,25 @@ class CxxRenderer(mistune.Renderer):
if text.startswith("%%"):
return ""
if text == "$internal_toc":
return ""
self._not_in_list()
text = clean_text(text)
return self._join_paragraph() + pretty_print(text, "// ")
def codespan(self, text):
return text
def link(self, link, title, content):
return content
def list(self, body, ordered = True):
return self._join_paragraph() + body
def list_item(self, text):
return ("// %i. " % self._number_list_item()) + \
pretty_print(text, "// ", False)
pretty_print(clean_text(text), "// ", False)
def block_code(self, code, lang):
self._not_in_list()
@ -138,7 +198,7 @@ class CxxRenderer(mistune.Renderer):
def hrule(self):
self._not_in_list()
self._not_paragraph()
return ""
return "\n"
def footnote_ref(self, key, index):
return ""
@ -185,7 +245,7 @@ def main(md_file, html_file, cxx_file):
renderer = CxxRenderer()
source = mistune.Markdown(renderer = renderer).render(markdown)
source = re.sub(r"\n*$", "\n", source)
source = "// This file was generated automatically\n\n" + source
source = "// This file was generated automatically.\n\n" + source
open(cxx_file, "w").write(source)
if __name__ == "__main__":

View File

@ -1,11 +1,11 @@
## Hello, World!
Download <a $download>enum.h</a>, then build this program with it:
Download <a $download><code>enum.h</code></a>, then build this program with it:
#include <iostream>
<em>#include "enum.h"</em>
<em>ENUM(Word, int, Hello, World)</em>
<em>ENUM</em>(<em>Word</em>, <em>int</em>, <em>Hello</em>, <em>World</em>)
int main()
{
@ -19,3 +19,5 @@ Download <a $download>enum.h</a>, then build this program with it:
Run it, and you should see the output "Hello, World!"
Congratulations, you have just created your first Better Enum!
%% description = Introductory Better Enums tutorial.

View File

@ -7,7 +7,7 @@ Let's begin by including `enum.h` and declaring our enum:
<em>#include <enum.h></em>
<em>ENUM(Channel, int, Cyan = 1, Magenta, Yellow, Black)</em>
<em>ENUM</em>(<em>Channel</em>, <em>int</em>, <em>Cyan</em> = <em>1</em>, <em>Magenta</em>, <em>Yellow</em>, <em>Black</em>)
We now have an `int`-sized enum with four constants.
@ -15,6 +15,8 @@ There are three groups of conversion functions: for strings, case-insensitive
strings, and integers. They all follow the same pattern, so I'll explain the
string functions in detail, and the rest can be understood by analogy.
$internal_toc
### Strings
There are three functions:
@ -117,12 +119,6 @@ use it carefully.
channel = <em>Channel::_from_integral_unchecked(0)</em>;
// <em>Invalid</em> - better not to try converting it to string!
### Aside
You have certainly noticed that all the method names begin with underscores.
This is because they share scope with the enum constants that you declare.
Better Enums is trying to stay out of your way by using a prefix.
### Validity checking
For completeness, Better Enums also provides three validity checking functions,
@ -144,14 +140,13 @@ There is one unfortunate wrinkle. You cannot convert a literal constant such as
std::cout << (<em>+Channel::Cyan</em>)._to_string();
This is due to some type gymnastics in the implementation of Better Enums. The
<a>Reference</a> section has a full explanation.
---
This concludes the first tutorial!
reference has a
[full explanation](${prefix}ApiReference.html#HelperFunctionsAndTypes).
---
std::cout << std::endl;
return 0;
}
%% description = Walkthrough of Better Enums conversion functions.

View File

@ -32,16 +32,18 @@ will print "Red Green Blue".
If you are using $cxx11, you can have much nicer syntax:
~~~comment
<em>for (Channel channel : Channel::_values())</em>
std::cout << <em>channel._to_integral()</em> << " ";
std::cout << std::endl;
<em>for (Channel channel : Channel::_values())</em>
std::cout << <em>channel._to_integral()</em> << " ";
std::cout << std::endl;
<em>for (const char *name : Channel::_names())</em>
std::cout << <em>name</em> << " ";
std::cout << std::endl;
<em>for (const char *name : Channel::_names())</em>
std::cout << <em>name</em> << " ";
std::cout << std::endl;
~~~
---
return 0;
}
%% description = Iterating over all Better Enums constants.

View File

@ -26,3 +26,5 @@ you a warning &mdash; try it!
std::cout << n << std::endl;
return 0;
}
%% description = Usage in switch statements.

View File

@ -3,6 +3,8 @@
This tutorial shows some of the safety features of Better Enums: scope, how to
control conversions, and the lack of a default constructor.
$internal_toc
### Scope
You have probably noticed by now that Better Enums are scoped: when you declare
@ -10,7 +12,7 @@ You have probably noticed by now that Better Enums are scoped: when you declare
#include <cassert>
<em>#include <enum.h></em>
<em>ENUM(Channel, int, Red = 1, Green, Blue)</em>
<em>ENUM</em>(<em>Channel</em>, <em>int</em>, <em>Red</em> = <em>1</em>, <em>Green</em>, <em>Blue</em>)
you don't get names such as `Red` in the global namespace. Instead, you get
`Channel`, and `Red` is accessible as `Channel::Red`. This is no big deal in
@ -18,7 +20,7 @@ $cxx11, which has `enum class`. In $cxx98, however, this typically requires
effort. Better Enums brings scope uniformly to both variants. So, despite the
above declaration, you can safely declare
<em>ENUM(Node, char, Red, Black)</em>
<em>ENUM</em>(<em>Node</em>, <em>char</em>, <em>Red</em>, <em>Black</em>)
and everything will work as expected.
@ -42,22 +44,8 @@ will not compile:
int n = channel;
~~~
The reason you have to opt into this feature with a macro is because it breaks
compatibility with the $cxx98 version of Better Enums. Specifically, when
writing a switch statement, you now have to do
~~~comment
switch (channel) {
case <em>+</em>Channel::Red: return 13;
case <em>+</em>Channel::Green: return 37;
case <em>+</em>Channel::Blue: return 42;
}
~~~
The difference is the explicit promotion with `+`. And, of course, if you had a
bunch of code that relies on implicitly converting $cxx98 Better Enums to
integers, it would break when switching to $cxx11 if strict conversions were the
default.
The reason this is not enabled by default is explained in the reference page on
[strict conversions](${prefix}OptInFeatures.html#StrictConversions).
### Default constructor
@ -73,29 +61,19 @@ Better Enums don't have a default constructor, for three reasons.
So, if you uncomment this code, the file won't compile:
// Channel channel;
~~~comment
Channel channel;
~~~
This may seem very strict, and I may relax it in the future. However, my guess
is that there are few places where a default constructor is truly needed.
- If you want to opt in to a notion of default values, you can encode your
project's policy into $cxx templates with ease, using building blocks Better
Enums provides. The solution sketched
[here](${prefix}demo/SpecialValues.html) is arguably more flexible than any
fixed choice Better Enums could impose on you.
- If a Better Enum value is the result of a large sequence of statements,
you may be able to move those statements into a separate function that
returns the value, and call it to initialize the Better Enum.
- If you need to reserve memory for a Better Enum before it is created, you
can do so by declaring a value of type `Enum::_integral`, as described in
the [next tutorial](${prefix}tutorial/RepresentationAndAlignment.html).
- I may add an ability to extend Better Enums, in which case you could add a
default constructor on a per-type or global basis and have it do anything
you want. I'd be glad to hear any feedback about your actual usage and
needs.
- Finally, Better Enums is under the BSD license so you can fork it and change
it directly, though of course this can have some administration overhead.
This may be too strict, and I may relax it in the future. In the meantime, the
solution sketched in the [special values demo](${prefix}demo/SpecialValues.html)
can replace default constructors for some purposes, and in a more flexible way.
I may eventually have the default constructor calling a template function like
the one in that demo.
---
return 0;
}
%% description = Type safety features and limitations.

View File

@ -1,4 +1,4 @@
## Representation and alignment
## Representation
Let's go over some of the low-level properties of a Better Enum. This time, we
will declare a more unusual enum than the ones we have seen.
@ -7,8 +7,8 @@ will declare a more unusual enum than the ones we have seen.
#include <iostream>
<em>#include <enum.h></em>
<em>ENUM(ContentType, short,
CompressedVideo = 5, PCM = 8, Subtitles = 17, Comment = 44)</em>
<em>ENUM</em>(<em>ContentType</em>, <em>short</em>,
<em>CompressedVideo</em> = <em>5</em>, <em>PCM</em> = <em>8</em>, <em>Subtitles</em> = <em>17</em>, <em>Comment</em> = <em>44</em>)
This is for a hypothetical multimedia container file format. Perhaps the files
have sections, and each one has a header:
@ -25,22 +25,21 @@ Here is what we have.
int main()
{
assert(<em>sizeof(ContentType) == 2</em>);
assert(<em>sizeof(ContentType)</em> == <em>2</em>);
As you can see, `ContentType` behaves just like a `short`[^*], in fact it simply
wraps one. This makes it possible to lay out structures in a predictable
fashion:
`ContentType` behaves just like a `short`[^*], in fact it simply wraps one. This
makes it possible to lay out structures in a predictable fashion:
Header header = {ContentType::PCM, 0, 0};
Header <em>header</em> = {ContentType::PCM, 0, 0};
assert(<em>sizeof(header) == 8</em>);
assert((size_t)&<em>header.flags -</em> (size_t)&<em>header.type == 2</em>);
assert(<em>sizeof(header)</em> == <em>8</em>);
assert((size_t)&<em>header.flags -</em> (size_t)&<em>header.type</em> == <em>2</em>);
---
`uint16_t` is called `ContentType`'s *underlying* or *representation* type. If
you want to know the representation type of any enum you have declared, it is
available as `::_integral`:
available as the member type `::_integral`:
<em>ContentType::_integral</em> untrusted_value = 44;
@ -80,3 +79,5 @@ types containg enums. The enums will behave as expected.
should also be explicitly sized. However, this code is trying to be
compatible with $cxx98, where those names aren't available in a portable
manner.
%% description = Underlying representation.

View File

@ -2,27 +2,33 @@
When used with $cxx11, Better Enums are generated entirely during compilation.
All the data is available for use by your own `constexpr` functions. The
examples in *this* tutorial aren't very useful, but read the following tutorials
to get an idea of what can be done. Here, you will see the basics.
examples in *this* tutorial aren't very useful, but look at the
[demos](${prefix}index.html#CompileTimeDemos) at the bottom of the main page to
get an idea of what can be done. Here, you will see the basics.
#include <iostream>
// The reason for this is explained below.
#ifndef BETTER_ENUMS_CONSTEXPR_TO_STRING
#define BETTER_ENUMS_CONSTEXPR_TO_STRING
#endif
<em>#include <enum.h></em>
<em>ENUM(Channel, int, Red = 1, Green = 2, Blue = 3)</em>
<em>ENUM</em>(<em>Channel</em>, <em>int</em>, <em>Red</em> = <em>1</em>, <em>Green</em> = <em>2</em>, <em>Blue</em> = <em>3</em>)
<em>constexpr</em> Channel channel = Channel::Green;
<em>constexpr</em> Channel channel = <em>Channel::_from_integral(2)</em>;
<em>constexpr</em> int value = <em>channel._to_integral()</em>;
<em>constexpr</em> const char *name = <em>channel._to_string()</em>;
<em>constexpr</em> Channel parsed = <em>Channel::_from_string("Red")</em>;
All of the above are computed during compilation. You can do apparently useless
things such as:
All of the above are computed during compilation. The reason for the macro
definition at the top of the file is explained on the
[opt-in features page](${prefix}OptInFeatures.html#CompileTimeNameTrimming).
Basically, it makes `_to_string` `constexpr`, but slows down compilation.
You can also do things such as:
<em>constexpr size_t length</em>(<em>const char *s</em>, <em>size_t index = 0</em>)
{
@ -41,3 +47,5 @@ things such as:
Which prints "5", the length of "Green". That 5 was also computed during
compilation.
%% description = Introduction to compile-time conversions.

2
enum.h
View File

@ -1,8 +1,6 @@
// This file is part of Better Enums, released under the BSD 2-clause license.
// See LICENSE for details, or visit http://github.com/aantron/better-enums.
// Version 0.9.0
#pragma once
#ifndef _BETTER_ENUMS_ENUM_H_

View File

@ -1,8 +1,8 @@
// This file was generated automatically
// This file was generated automatically.
// Hello, World!
//
// Download <a $download>enum.h</a>, then build this program with it:
// Download enum.h, then build this program with it:
#include <iostream>
#include "enum.h"

View File

@ -1,73 +1,100 @@
// This file was generated automatically
// This file was generated automatically.
// Special values
//
// Suppose your project has a convention where each enum has special
// <em>invalid</em> and <em>default</em> values. With Better Enums, you can
// encode that directly at compile time, and then access each enum's special
// values using syntax like Channel c = default_ and Channel c = invalid. This
// can make your code adapt automatically to changes in enum definitions, as
// well as make it easier to read and understand its intent.
// Suppose your project has a convention where each enum has special invalid and
// default values - for example, Enum::Invalid is invalid, and the first valid
// constant is default. With Better Enums, you can get the compiler to enforce
// the convention. At the end of this demo, we will have defined functions and
// templates that allow us to write:
//
// Channel channel = default_;
// Channel channel = invalid;
//
// void do_something(Channel channel);
//
// do_something(default_);
// do_something(invalid);
//
// The compiler will compute default and invalid values automatically, but the
// programmer will also be able to override the choice. Obviously, the syntax
// above is very legible and maintainable - the intent is clear and your code
// base will respond automatically to changes in enum definitions.
//
// Invalid values
//
// Let's start by defining the invalid values.
#include <iostream>
#include <stdexcept>
#include <enum.h>
// Invalid
//
// Perhaps the invalid value is usually called Invalid, but not in all enums.
// You can encode that using a function template for the common case, and a
// macro that creates specializations:
// Perhaps the convention is that the invalid value is usually called Invalid,
// but not for all enums. We will encode that using a template function. The
// unspecialized version will encode the default policy:
template <typename Enum>
constexpr Enum find_invalid() { return Enum::Invalid; }
constexpr Enum invalid_impl() { return Enum::Invalid; }
// A macro allows us to override the invalid value by specializing the template:
#define OVERRIDE_INVALID(Enum, Value) \
template<> \
constexpr Enum find_invalid<Enum>() { return Enum::Value; }
constexpr Enum invalid_impl<Enum>() { return Enum::Value; }
// Now, you can declare enums like these:
// Now, we can declare enums like these:
ENUM(Channel, int, Red, Green, Blue, Invalid)
// Invalid is the invalid value by default
ENUM(Compression, int, Undefined, None, Huffman)
OVERRIDE_INVALID(Compression, Undefined)
// and use them:
static_assert(find_invalid<Channel>() == +Channel::Invalid, "");
static_assert(find_invalid<Compression>() == +Compression::Undefined, "");
static_assert(invalid_impl<Channel>() == +Channel::Invalid, "");
static_assert(invalid_impl<Compression>() == +Compression::Undefined, "");
// This even supports enums that don't have an invalid value at all. As long as
// they don't have a constant called Invalid, you will get a compile-time error
// if you try to call invalid() on them &mdash; as you probably should!
// if you try to call invalid_impl<>() on them - as you probably should!
//
// Default
// Default values
//
// To encode the policy on default values, we need to do a compile-time check
// that the first value is not invalid. Otherwise, the technique is the same.
// Perhaps here the convention is the first value that is not invalid is
// default, unless, again, overridden by the programmer. This can be encoded
// using only a slightly more complex template function for the general case:
template <typename Enum>
constexpr Enum find_default()
constexpr Enum default_impl()
{
return
Enum::_size < 2 ?
throw std::logic_error("enum has no valid constants") :
Enum::_values()[0] == find_invalid<Enum>() ?
Enum::_values()[0] == invalid_impl<Enum>() ?
Enum::_values()[1] :
Enum::_values()[0];
}
// The above code gives us the first value if it is not invalid, otherwise the
// second value.
//
// The companion macro for overriding the choice of default value is almost the
// same as it was for invalid. The difference is that we do an extra sanity
// check to make sure the programmer doesn't declare the invalid value to be the
// default. If the sanity check fails, we produce a nice error message. Again,
// we are assuming that this is dictated by policy.
#define OVERRIDE_DEFAULT(Enum, Value) \
static_assert(Enum::Value != Enum::Invalid, \
#Enum ": default cannot equal invalid"); \
template<> \
constexpr Enum find_default<Enum>() { return Enum::Value; }
constexpr Enum default_impl<Enum>() { return Enum::Value; }
// Usage:
// And, as before, the usage:
static_assert(find_default<Channel>() == +Channel::Red, "");
static_assert(find_default<Compression>() == +Compression::None, "");
static_assert(default_impl<Channel>() == +Channel::Red, "");
static_assert(default_impl<Compression>() == +Compression::None, "");
// And, if you do
@ -79,48 +106,61 @@ ENUM(Answer, int, Yes, No, Invalid)
//
// Making the syntax nicer
//
// For the final touch, we will make the syntax better by introducing new
// "keywords" called default_ and invalid in such a way that we cause the
// compiler to do type inference:
template <typename Enum>
struct assert_enum {
using check = typename Enum::_enumerated;
using type = Enum;
};
// At this point, our policy is encoded by the ugly-looking functions
// invalid_impl and default_impl. We want a nicer syntax. The main reason we
// don't just use these functions directly is that the compiler wouldn't infer
// their template arguments from the context. For example, we would have to
// write things like
//
// Channel channel = invalid_impl<Channel>();
//
// which is unfortunate, because it results in repetition.
//
// In this section, we introduce two global objects called invalid and default_
// that will implicitly convert to any Better Enum type, and provide the invalid
// or default value, respectively, when they do so. They will act as new
// "keywords".
struct invalid_t {
template <typename To>
constexpr operator To() const { return find_invalid<To>(); }
template <typename To>
constexpr To convert() const { return find_invalid<To>(); }
constexpr operator To() const { return invalid_impl<To>(); }
};
struct default_t {
template <typename To>
constexpr operator To() const { return find_default<To>(); }
constexpr operator To() const { return default_impl<To>(); }
};
constexpr invalid_t invalid{};
constexpr default_t default_{};
// As you can see, both of these provide the families of implicit conversions
// that we need. Now, we can test:
static_assert(+Channel::Invalid == invalid, "");
static_assert(+Compression::Undefined == invalid, "");
static_assert(+Channel::Red == default_, "");
static_assert(+Compression::None == default_, "");
// We can now have nice code such as this:
// Finally, we can have nice code such as this:
void dump(Channel channel)
{
std::cout << channel._to_string() << std::endl;
}
int main()
{
dump(invalid);
Channel channel = default_;
std::cout << channel._to_string() << std::endl;
dump(channel);
return 0;
}
// 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!

View File

@ -1,14 +1,16 @@
// This file was generated automatically
// This file was generated automatically.
// Bit sets
//
// If you want to use std::bitset or a similar library to use enums as keys into
// a bit set, you need to know the number of bits at compile time. You can
// If you want to use std::bitset or a similar library to have enums be keys
// into a bit set, you need to know the number of bits at compile time. You can
// easily automate this with Better Enums, even when constants are not declared
// in increasing order.
// We simply need to find the maximum value of any given enum type.
#include <bitset>
#include <iostream>
#include <enum.h>
template <typename Enum>
@ -32,18 +34,30 @@ constexpr Enum max()
template <typename Enum>
using EnumSet = std::bitset<max<Enum>()._to_integral() + 1>;
// Then rest is straightforward. The only issue is that, in $cxx11, it is
// necessary to keep calling to_integral on the enums when passing them to
// bitset functions. You may want to implement a more enum-friendly bit set
// type, or overload unary operator -.
// Now, we can have bit sets that are wide enough to hold whatever range we
// declared. We just declare enums, and the numeric values of their constants
// will be bit indices. The rest is straightforward.
ENUM(Channel, int, Red, Green, Blue)
ENUM(Depth, int, TrueColor = 1, HighColor = 0)
ENUM(EFLAGS, int,
Carry, Parity = 2, Adjust = 4, Zero, Sign, Trap, Interrupt, Direction,
Overflow, NestedTask = 14, Resume = 16, V8086, AlignmentCheck,
CPUIDPresent = 21)
int main()
{
EnumSet<Channel> channels;
EnumSet<Depth> depths;
EnumSet<EFLAGS> eflags = 1 << EFLAGS::Carry | 1 << EFLAGS::Zero;
if (eflags.test(EFLAGS::Carry))
eflags.set(EFLAGS::Trap);
std::cout << eflags << std::endl;
return 0;
}
// If we want bit sets of fixed known width instead, we can use the code above
// to check that we haven't declared any bit indices out of range:
//
// static_assert(max<EFLAGS>()._to_integral() < 32,
// "some bit indices are out of range");

View File

@ -1,53 +1,85 @@
// This file was generated automatically
// This file was generated automatically.
// Semi-quine
//
// Let's make a Better Enum compose its own definition. It won't be literally as
// defined, since we will lose some information about initializers, but we will
// be able to preserve their numeric values. We will reserve the memory buffers
// at compile time.
// Let's make a Better Enum assemble its own definition in memory. It won't be
// literally as defined, since we will lose the exact initializer expressions,
// but we will be able to preserve the numeric values. We will reserve the
// memory buffer for the definition at compile time.
//
// There are actually better ways to do this than shown here. You could define a
// macro that expands to an ENUM declaration and also stringizes it. The point
// here is to show some of the reflective capabilities of Better Enums, so you
// can adapt them for cases where a macro is not sufficient.
// Ok, so it's not really a quine, because we won't be writing all the code
// needed to generate the definition to the buffer as well. And, there are
// better ways to dump the definition than shown here. You could simply define a
// macro that expands to an ENUM declaration and also stringizes it.
//
// But that's not the point here. The point of this page is to show some of the
// reflective capabilities of Better Enums, so you can adapt them for cases
// where a macro is not sufficient :)
#include <cassert>
#include <cstdio>
#include <iostream>
// First, we will need full compile-time reflection, since we will be calling
// _to_string. Let's make sure it's enabled by defining
// BETTER_ENUMS_CONSTEXPR_TO_STRING before including enum.h:
#ifndef BETTER_ENUMS_CONSTEXPR_TO_STRING
#define BETTER_ENUMS_CONSTEXPR_TO_STRING
#endif
#include <enum.h>
#define HIGH_COLOR 0
// Now, let's declare some enums to dump later:
ENUM(Channel, int, Red, Green, Blue)
ENUM(Depth, int, TrueColor = 1, HighColor = HIGH_COLOR)
ENUM(Depth, int, TrueColor = 1, HighColor = 0)
// First, we need to be able to get the length of each definition above. We will
// assume that the underlying type is always int, and that the spacing
// convention is followed as above. This allows us to write:
// Computing the size of the buffer
//
// First, we need to be able to get the length of each declaration above. We
// will assume that the underlying type is always int, and that the spacing
// convention is followed as above.
//
// First, let's get the lengths of basic components:
// Returns the length of the string representation of the number n
constexpr size_t value_length(int n, int bound = 10, size_t digits = 1)
{
return
n < bound ? digits : value_length(n, bound * 10, digits + 1);
}
// Returns the length of s
constexpr size_t string_length(const char *s, size_t index = 0)
{
return s[index] == '\0' ? index : string_length(s, index + 1);
}
// Now, the length of the constant declaration. Here is where we lose
// information about initializers. We are going to format the constant
// declarations like this:
//
// Red = 0, Green = 1, Blue = 2
// TrueColor = 1, HighColor = 0
//
// This is because Better Enums doesn't provide a way to know what the exact
// initializer was or whether there even was one - just the numeric value of
// each constant. If we were trying to be clever, we could avoid formatting
// initializers for sequential values, but I won't go through this exercise
// here.
// Returns the length of the constants portion of the declaration of Enum,
// as described above.
template <typename Enum>
constexpr size_t constants_length(size_t index = 0, size_t accumulator = 0)
{
return
index >= Enum::_size ? accumulator :
constants_length<Enum>(
index + 1, accumulator
+ string_length(", ")
@ -57,6 +89,12 @@ constexpr size_t constants_length(size_t index = 0, size_t accumulator = 0)
Enum::_values()[index]._to_integral()));
}
// Finally, we can combine these to get the length of the formatted declaration
// of the whole enum:
// Returns the length of the whole declaration of Enum, assuming the
// underlying type is int, and the constants are initialized as assumed by
// constants_length() above.
template <typename Enum>
constexpr size_t declaration_length()
{
@ -68,12 +106,21 @@ constexpr size_t declaration_length()
+ string_length(")");
}
// Now, we can declare:
// Formatting the enums
//
// Now, we can declare the buffers. The memory will be reserved at load time by
// the binary's loader. The extra one byte in each buffer is for the null
// terminator.
char channel_definition[declaration_length<Channel>() + 1];
char depth_definition[declaration_length<Depth>() + 1];
// And finally, the formatting function:
// Let's also create the formatting function. This is executed at run time, but
// we will be giving it pointers to our statically-allocated buffers. It will
// format the enum declaration and then return the number of bytes it wrote to
// the buffer, so that we can do a sanity check on it.
template <typename Enum>
size_t format(char *buffer)
@ -94,6 +141,12 @@ size_t format(char *buffer)
return offset;
}
// Checking our work
//
// Now, we can write and run this code.
int main()
{
size_t channel_length = format<Channel>(channel_definition);
@ -108,10 +161,7 @@ int main()
return 0;
}
// This outputs:
// It prints:
//
// ENUM(Channel, int, Red = 0, Green = 1, Blue = 2)
// ENUM(Depth, int, TrueColor = 1, HighColor = 0)
//
// This does have the advantage of not depending on anything else defined in the
// program, which isn't as easy to achieve with stringization.

View File

@ -1,4 +1,4 @@
// This file was generated automatically
// This file was generated automatically.
// Conversions
//
@ -32,14 +32,16 @@ int main()
// As you'd expect, the code above prints "Cyan".
//
// If channel is invalid &mdash; for example, if you simply cast the number "42"
// to Channel &mdash; then the result of to_string is undefined.
// If channel is invalid - for example, if you simply cast the number "42" to
// Channel - then the result of to_string is undefined.
channel = Channel::_from_string("Magenta");
std::cout << channel._to_string() << " ";
// This is also straightforward. If you pass a string which is not the name of a
// declared value, _from_string throws std::runtime_error.
// If you don't want an exception, there is _from_string_nothrow:
better_enums::optional<Channel> maybe_channel =
@ -50,10 +52,8 @@ int main()
else
std::cout << maybe_channel->_to_string() << " ";
// This returns an <em>optional value</em>, in the style of <a
// href="http://www.boost.org/doc/libs/1_58_0/libs/optional/doc/html/index.html">boost::optional</a>
// or the proposed <a
// href="http://en.cppreference.com/w/cpp/experimental/optional">std::optional</a>.
// This returns an optional value, in the style of boost::optional or the
// proposed std::optional.
//
// What that means for the above code is:
//
@ -61,7 +61,7 @@ int main()
// *maybe_channel is the converted value of type Channel,
// 2. if the conversion fails, maybe_channel converts to false.
//
// In $cxx11, you can use auto to avoid writing out the optional type:
// In C++11, you can use auto to avoid writing out the optional type:
//
// auto maybe_channel = Channel::_from_string_nothrow("Yellow");
// if (!maybe_channel)
@ -85,7 +85,7 @@ int main()
// Integers
//
// And, it is similar with the <em>representation type</em> int:
// And, it is similar with the representation type int:
//
// 1. ._to_integral
// 2. ::_from_integral
@ -109,22 +109,17 @@ int main()
channel = Channel::_from_integral_unchecked(0);
// Invalid - better not to try converting it to string!
// Aside
//
// You have certainly noticed that all the method names begin with underscores.
// This is because they share scope with the enum constants that you declare.
// Better Enums is trying to stay out of your way by using a prefix.
//
// Validity checking
//
// For completeness, Better Enums also provides three validity checking
// functions, one for each of the groups of conversions &mdash; string,
// functions, one for each of the groups of conversions - string,
// case-insensitive string, and integer:
assert(Channel::_is_valid(3));
assert(Channel::_is_valid("Magenta"));
assert(Channel::_is_valid_nocase("cYaN"));
// Almost done.
//
// There is one unfortunate wrinkle. You cannot convert a literal constant such
@ -134,8 +129,8 @@ int main()
std::cout << (+Channel::Cyan)._to_string();
// This is due to some type gymnastics in the implementation of Better Enums.
// The <a>Reference</a> section has a full explanation.
// This concludes the first tutorial!
// The reference has a full explanation.
std::cout << std::endl;
return 0;

View File

@ -1,4 +1,4 @@
// This file was generated automatically
// This file was generated automatically.
// Iteration
//
@ -28,15 +28,17 @@ int main()
std::cout << std::endl;
// will print "Red Green Blue".
// If you are using $cxx11, you can have much nicer syntax:
// If you are using C++11, you can have much nicer syntax:
//
// for (Channel channel : Channel::_values())
// std::cout << channel._to_integral() << " ";
// std::cout << std::endl;
// for (Channel channel : Channel::_values())
// std::cout << channel._to_integral() << " ";
// std::cout << std::endl;
//
// for (const char *name : Channel::_names())
// std::cout << name << " ";
// std::cout << std::endl;
// for (const char *name : Channel::_names())
// std::cout << name << " ";
// std::cout << std::endl;
return 0;
}

View File

@ -1,4 +1,4 @@
// This file was generated automatically
// This file was generated automatically.
// Safe switch
//
@ -21,7 +21,8 @@ int main()
}
// If you miss a case or add a redundant one, your compiler should be able to
// give you a warning &mdash; try it!
// give you a warning - try it!
std::cout << n << std::endl;
return 0;

View File

@ -1,4 +1,4 @@
// This file was generated automatically
// This file was generated automatically.
// Scope and safety
//
@ -16,10 +16,10 @@
ENUM(Channel, int, Red = 1, Green, Blue)
// you don't get names such as Red in the global namespace. Instead, you get
// Channel, and Red is accessible as Channel::Red. This is no big deal in
// $cxx11, which has enum class. In $cxx98, however, this typically requires
// effort. Better Enums brings scope uniformly to both variants. So, despite the
// above declaration, you can safely declare
// Channel, and Red is accessible as Channel::Red. This is no big deal in C++11,
// which has enum class. In C++98, however, this typically requires effort.
// Better Enums brings scope uniformly to both variants. So, despite the above
// declaration, you can safely declare
ENUM(Node, char, Red, Black)
@ -31,31 +31,19 @@ int main()
// Implicit conversion
//
// A major complaint in $cxx98 is that enums are implicitly convertible to
// A major complaint in C++98 is that enums are implicitly convertible to
// integers. Unfortunately, that is also true of Better Enums, and I haven't
// found a way to forbid the conversions and still have switch case checking.
//
// Better Enums can be made as safe as enum class in $cxx11, however. If your
// Better Enums can be made as safe as enum class in C++11, however. If your
// compiler supports enum class and you define BETTER_ENUMS_STRICT_CONVERSION
// before including enum.h, the following code will not compile:
//
// Channel channel = Channel::Red;
// int n = channel;
//
// The reason you have to opt into this feature with a macro is because it
// breaks compatibility with the $cxx98 version of Better Enums. Specifically,
// when writing a switch statement, you now have to do
//
// switch (channel) {
// case +Channel::Red: return 13;
// case +Channel::Green: return 37;
// case +Channel::Blue: return 42;
// }
//
// The difference is the explicit promotion with +. And, of course, if you had a
// bunch of code that relies on implicitly converting $cxx98 Better Enums to
// integers, it would break when switching to $cxx11 if strict conversions were
// the default.
// The reason this is not enabled by default is explained in the reference page
// on strict conversions.
//
// Default constructor
//
@ -71,31 +59,15 @@ int main()
// future-proof.
//
// So, if you uncomment this code, the file won't compile:
// Channel channel;
// This may seem very strict, and I may relax it in the future. However, my
// guess is that there are few places where a default constructor is truly
// needed.
//
// 1. If you want to opt in to a notion of default values, you can encode your
// project's policy into $cxx templates with ease, using building blocks
// Better Enums provides. The solution sketched <a
// href="${prefix}demo/SpecialValues.html">here</a> is arguably more
// flexible than any fixed choice Better Enums could impose on you.
// 2. If a Better Enum value is the result of a large sequence of statements,
// you may be able to move those statements into a separate function that
// returns the value, and call it to initialize the Better Enum.
// 3. If you need to reserve memory for a Better Enum before it is created,
// you can do so by declaring a value of type Enum::_integral, as described
// in the <a href="${prefix}tutorial/RepresentationAndAlignment.html">next
// tutorial</a>.
// 4. I may add an ability to extend Better Enums, in which case you could add
// a default constructor on a per-type or global basis and have it do
// anything you want. I'd be glad to hear any feedback about your actual
// usage and needs.
// 5. Finally, Better Enums is under the BSD license so you can fork it and
// change it directly, though of course this can have some administration
// overhead.
// Channel channel;
//
// This may be too strict, and I may relax it in the future. In the meantime,
// the solution sketched in the special values demo can replace default
// constructors for some purposes, and in a more flexible way. I may eventually
// have the default constructor calling a template function like the one in that
// demo.
return 0;
}

View File

@ -1,6 +1,6 @@
// This file was generated automatically
// This file was generated automatically.
// Representation and alignment
// Representation
//
// Let's go over some of the low-level properties of a Better Enum. This time,
// we will declare a more unusual enum than the ones we have seen.
@ -21,24 +21,25 @@ struct Header {
int offset;
};
// Here is what we have.
int main()
{
assert(sizeof(ContentType) == 2);
// As you can see, ContentType behaves just like a short, in fact it simply
// wraps one. This makes it possible to lay out structures in a predictable
// fashion:
// ContentType behaves just like a short, in fact it simply wraps one. This
// makes it possible to lay out structures in a predictable fashion:
Header header = {ContentType::PCM, 0, 0};
assert(sizeof(header) == 8);
assert((size_t)&header.flags - (size_t)&header.type == 2);
// uint16_t is called ContentType's <em>underlying</em> or
// <em>representation</em> type. If you want to know the representation type of
// any enum you have declared, it is available as ::_integral:
// uint16_t is called ContentType's underlying or representation type. If you
// want to know the representation type of any enum you have declared, it is
// available as the member type ::_integral:
ContentType::_integral untrusted_value = 44;
@ -51,17 +52,20 @@ int main()
ContentType::_from_integral(untrusted_value);
std::cout << type._to_string() << std::endl;
// You have probably noticed the initializers on each of the constants in
// ContentType. This allows you to declare sparse enums for compatibility with
// external protocols or previous versions of your software. The initializers
// don't need to be literal integers &mdash; they can be anything that the
// compiler would accept in a normal enum declaration. If there was a macro
// called BIG_FAT_MACRO declared above, we could have written Subtitles =
// don't need to be literal integers - they can be anything that the compiler
// would accept in a normal enum declaration. If there was a macro called
// BIG_FAT_MACRO declared above, we could have written Subtitles =
// BIG_FAT_MACRO. We could also have written Subtitles = CompressedVideo.
// The in-memory representation of an enum value is simply the number it has
// been assigned by the compiler. You should be safe passing enums to functions
// like fread and fwrite, and casting memory blocks known to be safe to struct
// types containg enums. The enums will behave as expected.
return 0;
}

View File

@ -1,15 +1,16 @@
// This file was generated automatically
// This file was generated automatically.
// Compile-time usage
//
// When used with $cxx11, Better Enums are generated entirely during
// compilation. All the data is available for use by your own constexpr
// functions. The examples in <em>this</em> tutorial aren't very useful, but
// read the following tutorials to get an idea of what can be done. Here, you
// will see the basics.
// When used with C++11, Better Enums are generated entirely during compilation.
// All the data is available for use by your own constexpr functions. The
// examples in this tutorial aren't very useful, but look at the demos at the
// bottom of the main page to get an idea of what can be done. Here, you will
// see the basics.
#include <iostream>
// The reason for this is explained below.
#ifndef BETTER_ENUMS_CONSTEXPR_TO_STRING
#define BETTER_ENUMS_CONSTEXPR_TO_STRING
#endif
@ -18,14 +19,17 @@
ENUM(Channel, int, Red = 1, Green = 2, Blue = 3)
constexpr Channel channel = Channel::Green;
constexpr Channel channel = Channel::_from_integral(2);
constexpr int value = channel._to_integral();
constexpr const char *name = channel._to_string();
constexpr Channel parsed = Channel::_from_string("Red");
// All of the above are computed during compilation. You can do apparently
// useless things such as:
// All of the above are computed during compilation. The reason for the macro
// definition at the top of the file is explained on the opt-in features page.
// Basically, it makes _to_string constexpr, but slows down compilation.
//
// You can also do things such as:
constexpr size_t length(const char *s, size_t index = 0)
{

View File

@ -1,12 +1,13 @@
.PHONY : default
default :
make -C ../doc examples
python test.py
.PHONY : platform
platform :
make -C ../doc examples
python test.py
python test.py --all
.PHONY : clean
clean :
rm -rf platform *.obj
.PHONY : default
default : run
@:

View File

@ -1 +1,2 @@
Invalid
Red

View File

@ -0,0 +1 @@
0000000000000010100001

View File

@ -27,23 +27,13 @@ ENUM(APIMethod, int,
WriteOwner, PollOwner, ReadProposal, WriteProposal, PollProposal,
ReadHistory, WriteHistory, PollHistory)
ENUM(Region, int,
EasterEurope, CentralEurope, WesternEurope, Mediterranean, NorthAfrica,
MiddleEast, MiddleEastNorthAfrica, GreaterPersia, Balkans, Scandinavia,
NorhernEurope, BritishIsles, LatinEurope, Iberia, LowCountries, Baltics,
Yugoslavia, Caucasus, VolgaBasin, Urals, CentralAsia, Siberia,
RussianFarEast, EastAsia, SoutheastAsia, Indochina, SouthAsia,
IndianSubcontinent, Desi = IndianSubcontinent, Asia, Australasia, Oceania,
Micronesia, Polynesia, JapaneseIslands, Tibet, ArabianPeninsula,
HornOfAfrica, NearEast, AlSham, EastAfrica, NileBasin, Palestine, Levant,
Anatolia, AegeanSea, GreatSteppe, CongoBasin, SouthAfrica, WestAfrica,
Sahara, WestSahara, NorthwestTerritory, YukonAlaska, Quebec, NewEngland,
DeepSouth)
int main()
{
return 0;
}
ENUM(Lipsum, int,
Lorem, ipsum, dolor, sit, amet, consectetur, adipiscing, elit, Vivamus,
libero, massa, tincidunt, at, ex, nec, porta, malesuada, arcu, Nullam,
lectus, nibh, dictum, eget, convallis, ac, feugiat, felis, Suspendisse,
quis, purus, vel, lacus, cursus, tristique, Donec, augue, tortor, luctus,
a, sed, mattis, in, quam, Cras, vitae, euismod, Cum, sociis, natoque,
penatibus, et, magnis, dis, parturient)
ENUM(ASTNode0, int,
IntegerLiteral, StringLiteral, CharacterLiteral, Variable, UnaryOperation,
@ -224,3 +214,8 @@ ENUM(ASTNode29, int,
BinaryOperation, ApplicationExpression, Abstraction, LetBinding,
CaseExpression, Pattern, Signature, Module, Functor, TypeVariable,
BasicType, ArrowType, VariantTypeConstant)
int main()
{
return 0;
}