mirror of
https://github.com/aantron/better-enums.git
synced 2025-12-06 16:56:42 +08:00
Updated and improved documentation.
This commit is contained in:
parent
3a316e6f79
commit
b24d155b7b
81
README.md
81
README.md
@ -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 — 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 — 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 — 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`.
|
||||
|
||||
@ -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__` —
|
||||
all major compilers — 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 — 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><Enum>
|
||||
|
||||
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><<em>Enum</em>> <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><<em>Enum</em>>;
|
||||
~~~
|
||||
|
||||
|
||||
|
||||
### 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 — 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 — 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.
|
||||
|
||||
@ -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
15
doc/Contact.md
Normal 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
40
doc/ExtendingLimits.md
Normal 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 — 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 — 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.
|
||||
@ -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
|
||||
— 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 — 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 — 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.
|
||||
@ -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 — you need about a dozen
|
||||
slow enums to get the same penalty as including `iostream` — 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` —
|
||||
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
32
doc/Performance.md
Normal 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 — 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.
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 — 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 — 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 — 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()
|
||||
<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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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 — 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.
|
||||
|
||||
51
doc/docs.py
51
doc/docs.py
@ -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()
|
||||
|
||||
270
doc/index.md
270
doc/index.md
@ -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 <iostream>
|
||||
<em>#include <enum.h></em>
|
||||
declare
|
||||
|
||||
<em>ENUM(Channel, int,
|
||||
Red = 1, Green, Blue)</em>
|
||||
|
||||
int main()
|
||||
{
|
||||
<em>Channel c = Channel::Red</em>;
|
||||
std::cout << <em>c._to_string()</em>;
|
||||
parse
|
||||
print
|
||||
|
||||
<em>for (Channel c : Channel::_values())</em>
|
||||
std::cout << <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> <<em>enum.h</em>>
|
||||
|
||||
<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 <iostream>
|
||||
<em>#include <enum.h></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 << <em>c._to_string()</em>;
|
||||
|
||||
for (size_t i = 0;
|
||||
<em>i < Channel::_size</em>; ++i) {
|
||||
|
||||
c = <em>Channel::_values()[i]</em>;
|
||||
std::cout << <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 — 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
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
## Performance
|
||||
|
||||
This is a placeholder page — 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.
|
||||
11
doc/template/demo.tmpl
vendored
11
doc/template/demo.tmpl
vendored
@ -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>
|
||||
|
||||
2
doc/template/download.tmpl
vendored
2
doc/template/download.tmpl
vendored
@ -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
|
||||
15
doc/template/footer.tmpl
vendored
15
doc/template/footer.tmpl
vendored
@ -1,11 +1,14 @@
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
Copyright © 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 © 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>
|
||||
|
||||
26
doc/template/header.tmpl
vendored
26
doc/template/header.tmpl
vendored
@ -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"> </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">
|
||||
|
||||
3
doc/template/next.tmpl
vendored
3
doc/template/next.tmpl
vendored
@ -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>
|
||||
|
||||
1
doc/template/ref.tmpl
vendored
1
doc/template/ref.tmpl
vendored
@ -1 +0,0 @@
|
||||
master
|
||||
2
doc/template/version.tmpl
vendored
2
doc/template/version.tmpl
vendored
@ -1 +1 @@
|
||||
master
|
||||
0.9.0
|
||||
@ -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("<em>", "<em>", escaped)
|
||||
replaced = re.sub("</em>", "</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("—", "-", 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__":
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -26,3 +26,5 @@ you a warning — try it!
|
||||
std::cout << n << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
%% description = Usage in switch statements.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
2
enum.h
@ -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_
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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 — 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!
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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 — for example, if you simply cast the number "42"
|
||||
// to Channel — 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 — 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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 — try it!
|
||||
// give you a warning - try it!
|
||||
|
||||
|
||||
std::cout << n << std::endl;
|
||||
return 0;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 — 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;
|
||||
}
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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
|
||||
@:
|
||||
|
||||
@ -1 +1,2 @@
|
||||
Invalid
|
||||
Red
|
||||
|
||||
@ -0,0 +1 @@
|
||||
0000000000000010100001
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user