Updated and improved documentation.

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

View File

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

View File

@ -1,272 +1,370 @@
## API reference ## API reference
Table of contents $internal_toc
### Overview ### Overview
The following declaration The declaration
#include <enum.h> #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 generates a new class type `Enum` which is notionally similar to the type
this $cxx11 declaration: 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`. `Enum::C`, and so on, with memory representation the same as `underlying_type`.
It is possible to supply initializers for any of the constants: 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` The initializers have the same meaning and constraints as in a built-in `enum`
declaration. or `enum class` declaration.
---
The principal differences between the types declared by the `ENUM` macro and The principal differences between the types declared by the `ENUM` macro and
`enum class` are: `enum class` are:
- `ENUM` is available for $cxx98 for compilers supporting `__VA_ARGS__`, - `ENUM` is available for $cxx98
[compilers](${prefix}CompilerSupport.html) supporting `__VA_ARGS__` &mdash;
all major compilers &mdash; while `enum class` is restricted to $cxx11,
- the `ENUM` type is implicitly convertible to integral types, though this can - 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 - the `ENUM` type supports a set of reflective operations, detailed in the
rest of this reference. 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 Better Enums are similar to their underlying type for the purposes of argument
equivalent to their underlying type. This means that Better Enums are typically passing. This means that they typically fit into a machine word, and should be
integers that fit into a register or a stack word, and should be passed by passed by value.
value.
All names declared in the scope of a Better Enum are prefixed with an underscore All names declared in the scope of a Better Enum are prefixed with an underscore
in order to avoid conflicts with potential constant names. in order to avoid conflicts with potential constant names.
### 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 <em>ENUM</em>(<em>Enum</em>, <em>int</em>, <em>A</em>, <em>B</em>, <em>C</em>)
to
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 The types and functions described here make it possible to use Better Enums with
described under `_enumerated` above. So, for example, it is necessary to write the rest of $cxx in a reasonable fashion, or else they are referenced in the
`(+Enum::A)._to_string()` instead of `Enum::A._to_string`. rest of the documentation.
#### constexpr implicit constructor Enum(_enumerated) #### <em>typedef _enumerated</em>
A constructor that performs implicit conversions of `Enum::_enumerated` to An internal type used to declare constants. The `ENUM` macro generates something
`Enum`. This allows code to use a literal constant where `Enum` is expected in similar to
most situations &mdash; those where the compiler can do an implicit conversion.
For example, if there is a function `void output(Enum)`, it can be called as
`output(Enum::A)`, without having to write `output(+Enum::A)`. This constructor
is also used for the typical initialization
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 The other constructors of `Enum` are the implicitly-generated copy and move
constructors. There is no default constructor. constructors. There is no default constructor. If you have comments on what a
default constructor should do, please [let me know](${prefix}Contact.html).
#### static constexpr size_t _size #### non-member <em>struct better_enums::optional</em>&lt;Enum&gt;
An optional `Enum` value. These are returned by the various `_nothrow`
functions, such as [`_from_string_nothrow`](#_from_string_nothrow). This type is
meant to represent the possibility of failure. For example, suppose you have:
<em>better_enums::optional</em>&lt;<em>Enum</em>&gt; <em>maybe</em> = <em>_from_string_nothrow</em>(<em>"A"</em>);
An optional value such as `maybe` is convertible to `bool`. If it converts to
`true`, it holds a valid `Enum` value. Otherwise, if it converts to `false`, the
operation that produced the optional value failed. So, you can continue with
<em>if</em> (<em>maybe</em>) {
// The string conversion succeeded
do_something(<em>*maybe</em>);
}
<em>else</em> {
// The string conversion failed
}
As you can see, `*maybe` evaluates to the `Enum` value, in this case `Enum::A`.
The rest of this reference refers to this type as simply `optional`, as if you
had entered
~~~comment
using <em>optional</em> = <em>better_enums::optional</em>&lt;<em>Enum</em>&gt;;
~~~
### Value count and iteration
The types and members described here have to do with the sequence of constants
declared, i.e. `A`, `B`, `C` in the [running example](#RunningExample).
#### static constexpr size_t <em>_size</em>
The number of constants declared. `Enum::_size == 3`. 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 Type of object that permits iteration over the constants. Has at least
`constexpr` `begin()`, `end()`, and `size()` methods, and `operator[]`. `constexpr` `begin()`, `end()`, and `size()` methods, and `constexpr`
Iteration visits each declared constant, even if multiple constants have the `operator[]`. Iteration visits each *declared* constant, even if multiple
same value, and visits them in order of declaration. See usage examples under constants have the same value, and visits them in order of declaration. See
`_values`. usage examples under [`_values`](#_values).
#### typedef _value_iterator #### <em>typedef _value_iterator</em>
Random-access iterator type for `_value_iterable`. Most operations, including Random-access iterator type for [`_value_iterable`](#_value_iterable). Most
dereferencing, are `constexpr`. The exceptions are mutating operators such as operations, including dereferencing, are `constexpr`. The exceptions are
`operator++`. In `constexpr` code, that can be replaced with addition of `1`. 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) <em>for</em> (size_t index = 0; <em>index</em> < <em>Enum::_values().size()</em>; ++index)
output(Enum::_values()[index]); do_something(<em>Enum::_values()[index]</em>);
or, using iterators: or, using iterators:
for (Enum::_value_iterator iterator = Enum::_values().begin(); <em>for</em> (Enum::_value_iterator iterator = <em>Enum::_values().begin()</em>;
iterator != Enum::_values().end(); ++iterator) { iterator != <em>Enum::_values().end()</em>; ++iterator) {
output(*iterator); do_something(<em>*iterator</em>);
} }
or, in $cxx11: or, in $cxx11:
for (Enum value : Enum::_values()) <em>for</em> (Enum value : <em>Enum::_values()</em>)
output(value); do_something(<em>value</em>);
#### 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.
### 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 Returns the string representation a Better Enum value. For example:
called. For example, if `value == Enum::A`, `value._to_string()` is the same
string as `"A"`.
If two constants have the same numeric representation, and `value` is equal to Enum value = <em>Enum::A</em>;
one of those constants, then it has that numeric representation, and it is value.<em>_to_string</em>(); // Same as "A".
undefined which constant's name `_to_string` will choose.
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 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. 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 This method is not `constexpr` by default. Read
slowdown during compilation. [See here]() for how to make it `constexpr` for [here](${prefix}OptInFeatures.html#CompileTimeNameTrimming) for information
some or all Better Enums. 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. If the given string is the exact name of a declared constant, returns the
Otherwise, throws `std::runtime_error`. Running time is linear in the number of constant. Otherwise, throws `std::runtime_error`. Running time is linear in the
declared constants multiplied by the length of the longest constant. 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 Same as [`_from_string`](#_from_string), but does not throw an exception on
an [optional]() value instead. 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 Same as [`_from_string`](#_from_string), but comparison is up to case, in the
the Latin-1 encoding. 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 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 The same as [`_is_valid`](#_is_validconstChar*), but comparison is done up to
`_from_string_nocase`. 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 Evaluates to the name of the Better Enum type. `Enum::_name()` is the same
string as `"Enum"`. string as `"Enum"`.
#### typedef _name_iterable #### typedef <em>_name_iterable</em>
Type of object that permits iteration over names of declared constants. Has at Type of object that permits iteration over names of declared constants. Has at
least `constexpr` `begin()`, `end()`, and `size()` methods. `operator[]` is also least `constexpr` `begin()`, `end()`, and `size()` methods. `operator[]` is also
available, but is `constexpr` if and only if `_to_string` is `constexpr`. available, but is `constexpr` if and only if [`_to_string`](#_to_string) is
Iteration visits constants in order of declaration. See usage example under `constexpr`. Iteration visits constants in order of declaration. See usage
`_names`. example under [`_names`](#_names).
#### typedef _name_iterator #### typedef <em>_name_iterator</em>
Random-access iterator type for `_name_iterable`. Most operations are Random-access iterator type for `_name_iterable`. Most operations are
`constexpr`, but dereferencing is `constexpr` if and only if `_to_string` is `constexpr`, but dereferencing is `constexpr` if and only if
`constexpr`. Mutating operators such as `operator++` are not `constexpr` due to [`_to_string`](#_to_string) is `constexpr`. Mutating operators such as
their nature &mdash; adding `1` is a `constexpr` alternative. `operator++` are not `constexpr` due to their nature &mdash; adding `1` is a
`constexpr` alternative. You typically don't have to refer to this type
directly.
#### static constexpr? _name_iterable _names() #### static constexpr? _name_iterable <em>_names</em>()
Access to the collection of declared constant names. For example: Access to the sequence of declared constant names. For example:
for (size_t index = 0; index < Enum::_names().size(); ++index) <em>for</em> (size_t index = 0; <em>index</em> < <em>Enum::_names().size()</em>; ++index)
std::cout << Enum::_names()[index] << std::endl; std::cout << <em>Enum::_names()[index]</em> << std::endl;
or, using iterators: or, using iterators:
for (Enum::_name_iterator iterator = Enum::_names().begin(); <em>for</em> (Enum::_name_iterator iterator = <em>Enum::_names().begin()</em>;
iterator != Enum::_names().end(); ++iterator) { iterator != <em>Enum::_names().end()</em>; ++iterator) {
std::cout << *iterator << std::endl; std::cout << <em>*iterator</em> << std::endl;
} }
or, in $cxx11: or, in $cxx11:
for (const char *name : Enum::_names()) <em>for</em> (const char *name : <em>Enum::_names()</em>)
std::cout << name << std::endl; 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, 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 `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 and alignment requirement as its representation type.
$cxx98.
#### 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 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 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 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 no-op. Throws `std::runtime_error` if the given integer is not the numeric value
of one of the declared constants. 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 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 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. 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 <em>Enum::_from_integral_unchecked</em>(value.<em>_to_integral</em>());
failure. Returns an [optional]() value instead. <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 #### static constexpr bool <em>_is_valid(_integral)</em>
of the declared constants.
Evaluates to `true` if and only if the given integer is the numeric value of one
of the declared constants. Running time is linear in the number of declared
constants.
%% class = api
%% description = Detailed description of the Better Enums API.

View File

@ -1,23 +1,73 @@
## Compiler support ## Compiler support
Better Enums aims to support as many compilers as is reasonable. It has been Better Enums aims to support all major compilers. It is known to definitely work
tested with clang++ and g++, and Visual C++: on
- clang++ 3.3 to 3.6 - clang 3.3 to 3.6
- g++ 4.3 to 5.1 - gcc 4.3 to 5.1
- Visual C++ 2013 Update 4, Visual C++ 2015 RC - 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 To ensure that nothing is broken, every release of Better Enums is
- $cxx98 with the variadic macro (`__VA_ARGS__`) extension [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 ### Compile-time reflection configurations
automatically with a large number of combinations of compiler and configuration.
The full list is given at the end of this page.
The tests include compiling and running unit tests, all the examples in the Read this section if:
demos and tutorials, and a multiple translation unit linking test.
- 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 ### Tested configurations
@ -70,3 +120,5 @@ g++44 -std=c++98
g++43 -std=c++0x -DBETTER_ENUMS_NO_CONSTEXPR g++43 -std=c++0x -DBETTER_ENUMS_NO_CONSTEXPR
g++43 -std=c++98 g++43 -std=c++98
~~~ ~~~
%% description = Information about compiler support and feature detection.

15
doc/Contact.md Normal file
View File

@ -0,0 +1,15 @@
## Contact
- Send me an email: [antonbachin@yahoo.com](mailto:antonbachin@yahoo.com).
- Visit the [GitHub]($repo) project to open an issue or get a development
version.
I also watch the `enums` tag on Stack Overflow.
I'm happy to hear any feedback. If you have any trouble using the library or
parsing the documentation, please don't hesitate to let me know.
And, if you find this library helpful, give it a star on GitHub to let me know
you use it. It will help keep me encouraged :)
%% description = Contact information for bugs, issues, support, and feedback.

40
doc/ExtendingLimits.md Normal file
View File

@ -0,0 +1,40 @@
## Extending limits
The `ENUM` macro makes heavy use of the preprocessor, and some of the internal
macros have size limits. There are two: on the number of constants you can
declare, and on the maximum length of a constant name under very specific
conditions. If you run into either one, you can extend the limit by following
the instructions on this page.
The second limit, on the maximum length of a constant name, applies only when
you are compiling an enum in
["full" `constexpr`](${prefix}OptInFeatures.html#CompileTimeNameTrimming) mode
*and* the constant has an initializer. Otherwise, your constants can have names
of arbitrary length.
The default limits are 64 constants in an enum and 23 characters for initialized
constants of full-`constexpr` enums. To extend:
1. Pick your desired limits. I will use 512 constants and 63 characters as an
example. Add 1 to the number of characters to account for the null
terminator &mdash; our numbers are now 512 and 64.
2. Get `make_macros.py` from your copy of the full Better Enums distribution
or from <a href="https://raw.githubusercontent.com/aantron/better-enums/$version/script/make_macros.py" download>GitHub</a>.
3. You will run this script to generate a header file containing some
replacement macros for `enum.h` to use. Pick a name for this file and a
location somewhere in your include path. I will assume that this file is
`common/enum_macros.h` in your project.
4. Run `python make_macros.py 512 64 > common/enum_macros.h`.
5. Define `BETTER_ENUMS_MACRO_FILE <common/enum_macros.h>` before including
`enum.h`. This is typically done by supplying extra flags to the compiler
on the command line:
- For g++ and clang++, `-DBETTER_ENUMS_MACRO_FILE='<common/enum_macros.h>'`
- For VC++, `\DBETTER_ENUMS_MACRO_FILE='<common/enum_macros.h>'`
6. Enjoy the looser limits. Just watch out &mdash; increasing the second
number can really slow down compilation of full-`constexpr` enums.
7. You don't need `make_macros.py` anymore. It's not part of your build
process and you can delete it.
%% description = How to extend limits imposed by internal macros.

View File

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

View File

@ -1,34 +1,41 @@
## Opt-in features ## Opt-in features
Better Enums has a few opt-in features that affect how enums are generated. The Better Enums has two opt-in features. They are both "good," but they either hurt
default configuration is suitable for general use, but you might have more compilation time or break compatibility with $cxx98, so they are disabled by
stringent requirements. This page describes the optional features and how to default. Read this page if you want to enable them.
enable them.
$internal_toc
### Compile-time name trimming
This makes `_to_string` and `_names` `constexpr`, i.e. "full" `constexpr` mode.
To enable this for all enums, define `BETTER_ENUMS_CONSTEXPR_TO_STRING` before
including `enum.h`. Typically, you would pass an option to your compiler to do
this.
You can also enable this feature for individual enums instead, by declaring them
using the alternative `SLOW_ENUM` macro.
The feature is disabled because it increases compilation times by a factor of
about 4. Compilation is still relatively fast &mdash; you need about a dozen
slow enums to get the same penalty as including `iostream` &mdash; but it is
a steep penalty nonetheless. I don't think most people need this feature most of
the time, so it's too high a price to pay. If I improve compilation times to the
point where compile-time name trimming can be the default, I will simply
redefine `SLOW_ENUM` as `ENUM` and deprecate it, so your code will still work.
### Strict conversions ### Strict conversions
Each Better Enum is implicitly convertible to its member `enum _enumerated` This disables implicit conversions to underlying integral types. At the moment,
type. This is meant to support usage of Better Enums directly in `switch` you can only enable this globally for all enums, by defining
statements. When you write `BETTER_ENUMS_STRICT_CONVERSION` before including `enum.h`.
switch (channel) { 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
the compiler applies the implicit conversion, and is also able to do case possible to avoid this in $cxx11 and convert to `enum class` types instead, but
exhaustiveness checking. Unfortunately, `_enumerated` is always a regular $cxx98 at the cost of breaking interface compatibility with $cxx98.
enum type, meaning that it has standard conversions to integral types. The end
result is `channel` is implicitly convertible all the way to `int` &mdash;
behavior that is often considered a violation of type safety.
In $cxx11, you can force Better Enums to declare an internal `enum class` type
to use for `switch` statements. Each enum will then be only convertible to its
`enum class`, and won't be implicitly convertible to integers. This is done by
defining `BETTER_ENUMS_STRICT_CONVERSION` before including `enum.h`. You would
typically do this on the compiler's command line.
The reason strict conversions aren't enabled by default in $cxx11 is that doing
so would break compatibility with the $cxx98 interface.
- The "weaker" incompatibility is that you could write a bunch of $cxx98 code - 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 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. issue, I decided not to do that.
- The "stronger" incompatibility is a difference in how `switch` cases must be - The "stronger" incompatibility is a difference in how `switch` cases must be
written. The syntaxes for the two variants, implicitly-converting and 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: Here they are:
// Default variant // Default variant
switch (channel) { <em>switch</em> (<em>channel</em>) {
case Channel::Red: break; case Channel::Red: break;
case Channel::Green: break; case Channel::Green: break;
case Channel::Blue: break; case Channel::Blue: break;
} }
// Strict variant // Strict variant
switch (channel) { <em>switch</em> (<em>channel</em>) {
case +Channel::Red: break; case <em>+</em>Channel::Red: break;
case +Channel::Green: break; case <em>+</em>Channel::Green: break;
case +Channel::Blue: break; case <em>+</em>Channel::Blue: break;
} }
I would be very happy to make conversion to `enum class` the default whenever %% description = Opting into features disabled by default for performance or
`enum class` is available, but how to do so without breaking compatibility is so compatibility reasons.
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.

32
doc/Performance.md Normal file
View File

@ -0,0 +1,32 @@
## Performance
A basic performance test is run on
[every compiler tested](${prefix}CompilerSupport.html#TestedConfigurations). It
doesn't try to be very accurate &mdash; it just stress-tests the compiler once
to get a rough idea of how long it takes to compile Better Enums.
The files compared in the test are as follows:
- [One file]($repo/blob/$version/test/performance/4-declare_enums.cc) includes
`enum.h` and declares 647 constants across 36 Better Enums.
- The [other file]($repo/blob/$version/test/performance/5-iostream.cc) *only*
includes `iostream` and does nothing with it.
The argument is that if compiling a bunch of Better Enums is faster, or about as
fast as, including a single standard header such as `iostream`, then Better
Enums is fast enough for general use.
Results are given for select compilers and
[configurations](${prefix}CompilerSupport.html#CompileTimeReflectionConfigurations)
as ratios of how long it took to compile the Better Enums file to how long it
took to compile the `iostream` file. The less the ratio, the better. Ratios less
than 1 mean the enums compiled faster, and ratios greater than 1 mean `iostream`
compiled faster.
- clang 3.6, fast `constexpr`: 0.66
- clang 3.6, full `constexpr`: 2.25
- gcc 5.1, fast `constexpr`: 1.58
- gcc 5.1, full `constexpr`: 4.23
- VC2015RC, $cxx98: 1.18
%% description = Compilation performance testing results.

View File

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

View File

@ -1,122 +1,165 @@
## Special values ## Special values
Suppose your project has a convention where each enum has special *invalid* and 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 *default* values &mdash; for example, `Enum::Invalid` is *invalid*, and the
time, and then access each enum's special values using syntax like first valid constant is *default*. With Better Enums, you can get the compiler
`Channel c = default_` and `Channel c = invalid`. This can make your code adapt to enforce the convention. At the end of this demo, we will have defined
automatically to changes in enum definitions, as well as make it easier to read functions and templates that allow us to write:
and understand its intent.
--- ~~~comment
<em>Channel channel</em> = <em>default_</em>;
<em>Channel channel</em> = <em>invalid</em>;
void do_something(<em>Channel channel</em>);
do_something(<em>default_</em>);
do_something(<em>invalid</em>);
~~~
The compiler will compute default and invalid values automatically, but the
programmer will also be able to override the choice. Obviously, the syntax above
is very legible and maintainable &mdash; the intent is clear and your code base
will respond automatically to changes in enum definitions.
$internal_toc
### Invalid values
Let's start by defining the invalid values.
#include <iostream> #include <iostream>
#include <stdexcept> #include <stdexcept>
<em>#include <enum.h></em> <em>#include <enum.h></em>
### Invalid 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
Perhaps the invalid value is usually called `Invalid`, but not in all enums. You unspecialized version will encode the default policy:
can encode that using a function template for the common case, and a macro that
creates specializations:
<em>template</em> <<em>typename Enum</em>> <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>) \ <em>#define OVERRIDE_INVALID</em>(<em>Enum</em>, <em>Value</em>) \
template<> \ 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>) 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) 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: and use them:
static_assert(<em>find_invalid</em><<em>Channel</em>>() == <em>+Channel::Invalid</em>, ""); static_assert(<em>invalid_impl</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>Compression</em>>() == +<em>Compression::Undefined</em>, "");
This even supports enums that don't have an invalid value at all. As long as 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 they don't have a constant called `Invalid`, you will get a compile-time error
if you try to call `invalid()` on them &mdash; as you probably should! if you try to call `invalid_impl<>()` on them &mdash; as you probably should!
### Default ### Default values
To encode the policy on default values, we need to do a compile-time check that Perhaps here the convention is the first value that is not invalid is default,
the first value is not invalid. Otherwise, the technique is the same. 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> <em>template</em> <<em>typename Enum</em>>
constexpr Enum find_default() constexpr <em>Enum default_imp</em>l()
{ {
return return
Enum::_size < 2 ? <em>Enum::_size < 2 ?
throw std::logic_error("enum has no valid constants") : 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()[1] :
Enum::_values()[0]; Enum::_values()[0]</em>;
} }
#define OVERRIDE_DEFAULT(Enum, Value) \ The above code gives us the first value if it is not invalid, otherwise the
static_assert(Enum::Value != Enum::Invalid, \ second value.
#Enum ": default cannot equal invalid"); \
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<> \ 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(<em>default_impl</em><<em>Channel</em>>() == +<em>Channel::Red</em>, "");
static_assert(find_default<Compression>() == +Compression::None, ""); static_assert(<em>default_impl</em><<em>Compression</em>>() == +<em>Compression::None</em>, "");
And, if you do And, if you do
ENUM(Answer, int, Yes, No, Invalid) ENUM(<em>Answer</em>, int, Yes, No, <em>Invalid</em>)
// OVERRIDE_DEFAULT(Answer, Invalid) // OVERRIDE_DEFAULT(<em>Answer</em>, <em>Invalid</em>)
you will get a helpful compile-time error saying you will get a helpful compile-time error saying
`Answer: default cannot equal invalid`. `Answer: default cannot equal invalid`.
### Making the syntax nicer ### Making the syntax nicer
For the final touch, we will make the syntax better by introducing new At this point, our policy is encoded by the ugly-looking functions
"keywords" called `default_` and `invalid` in such a way that we cause the `invalid_impl` and `default_impl`. We want a nicer syntax. The main reason we
compiler to do type inference: 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> ~~~comment
struct assert_enum { Channel channel = invalid_impl<Channel>();
using check = typename Enum::_enumerated; ~~~
using type = Enum;
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 { <em>struct default_t</em> {
template <typename To> template <<em>typename To</em>>
constexpr operator To() const { return find_invalid<To>(); } constexpr <em>operator To</em>() const { return <em>default_impl<To>()</em>; }
template <typename To>
constexpr To convert() const { return find_invalid<To>(); }
}; };
struct default_t { constexpr <em>invalid_t invalid</em>{};
template <typename To> constexpr <em>default_t default_</em>{};
constexpr operator To() const { return find_default<To>(); }
};
constexpr invalid_t invalid{}; As you can see, both of these provide the families of implicit conversions that
constexpr default_t default_{}; we need. Now, we can test:
static_assert(+Channel::Invalid == invalid, ""); static_assert(+<em>Channel::Invalid</em> == <em>invalid</em>, "");
static_assert(+Compression::Undefined == invalid, ""); static_assert(+<em>Compression::Undefined</em> == <em>invalid</em>, "");
static_assert(+Channel::Red == default_, ""); static_assert(+<em>Channel::Red</em> == <em>default_</em>, "");
static_assert(+Compression::None == default_, ""); 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() int main()
{ {
Channel channel = default_; dump(<em>invalid</em>);
std::cout << channel._to_string() << std::endl;
<em>Channel channel</em> = <em>default_</em>;
dump(channel);
return 0; 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 There are many possible variations of these policies, but I think most of them
can be encoded in a reasonable fashion using the tools Better Enums provides. can be encoded in a reasonable fashion using the tools Better Enums provides.
Enjoy! Enjoy!
%% description = Encoding project policies for static enforcement using Better
Enums.

View File

@ -1,6 +1,6 @@
## Bit sets ## 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 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 automate this with Better Enums, even when constants are not declared in
increasing order. increasing order.
@ -10,41 +10,60 @@ increasing order.
We simply need to find the maximum value of any given enum type. We simply need to find the maximum value of any given enum type.
#include <bitset> #include <bitset>
#include <enum.h> #include <iostream>
<em>#include <enum.h></em>
template <typename Enum> template <<em>typename Enum</em>>
constexpr Enum max_loop(Enum accumulator, size_t index) constexpr <em>Enum max_loop</em>(Enum accumulator, size_t index)
{ {
return return
index >= Enum::_size ? accumulator : <em>index >= Enum::_size ? accumulator :
Enum::_values()[index] > accumulator ? Enum::_values()[index] > accumulator ?
max_loop<Enum>(Enum::_values()[index], index + 1) : max_loop<Enum>(Enum::_values()[index], index + 1) :
max_loop<Enum>(accumulator, index + 1); max_loop<Enum>(accumulator, index + 1)</em>;
} }
template <typename Enum> template <<em>typename Enum</em>>
constexpr Enum max() 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: And use that to declare a bit set template:
template <typename Enum> template <<em>typename Enum</em>>
using EnumSet = std::bitset<max<Enum>()._to_integral() + 1>; 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 Now, we can have bit sets that are wide enough to hold whatever range we
to keep calling `to_integral` on the enums when passing them to `bitset` declared. We just declare enums, and the numeric values of their constants will
functions. You may want to implement a more enum-friendly bit set type, or be bit indices. The rest is straightforward.
overload unary `operator -`.
ENUM(Channel, int, Red, Green, Blue) ENUM(<em>EFLAGS</em>, int,
ENUM(Depth, int, TrueColor = 1, HighColor = 0) <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() int main()
{ {
EnumSet<Channel> channels; <em>EnumSet</em><<em>EFLAGS</em>> eflags = <em>1 << EFLAGS::Carry</em> | <em>1 << EFLAGS::Zero</em>;
EnumSet<Depth> depths;
if (eflags.test(<em>EFLAGS::Carry</em>))
eflags.set(<em>EFLAGS::Trap</em>);
std::cout << <em>eflags</em> << std::endl;
return 0; return 0;
} }
---
If we want bit sets of fixed known width instead, we can use the code above to
check that we haven't declared any bit indices out of range:
~~~comment
static_assert(max<EFLAGS>()._to_integral() < 32,
"some bit indices are out of range");
~~~
%% description = Finding the maximum value of a Better Enum constant for use in
declaring bit set types.

View File

@ -1,117 +1,173 @@
## Semi-quine ## Semi-quine
Let's make a Better Enum compose its own definition. It won't be literally as Let's make a Better Enum assemble its own definition in memory. It won't be
defined, since we will lose some information about initializers, but we will be literally as defined, since we will lose the exact initializer expressions, but
able to preserve their numeric values. We will reserve the memory buffers at we will be able to preserve the numeric values. We will reserve the memory
compile time. buffer for the definition at compile time.
There are actually better ways to do this than shown here. You could define a Ok, so it's not really a quine, because we won't be writing all the code needed
macro that expands to an `ENUM` declaration and also stringizes it. The point to generate the definition to the buffer as well. And, there are better ways to
here is to show some of the reflective capabilities of Better Enums, so you can dump the definition than shown here. You could simply define a macro that
adapt them for cases where a macro is not sufficient. 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 <cassert>
#include <cstdio> #include <cstdio>
#include <iostream> #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 #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(<em>Channel</em>, int, Red, Green, Blue)
ENUM(Depth, int, TrueColor = 1, HighColor = HIGH_COLOR) 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 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 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> Now, the length of the constant declaration. Here is where we lose information
constexpr size_t constants_length(size_t index = 0, size_t accumulator = 0) about initializers. We are going to format the constant declarations like this:
~~~comment
Red = 0, Green = 1, Blue = 2
TrueColor = 1, HighColor = 0
~~~
This is because Better Enums doesn't provide a way to know what the exact
initializer was or whether there even was one &mdash; just the numeric value of
each constant. If we were trying to be clever, we could avoid formatting
initializers for sequential values, but I won't go through this exercise here.
// Returns the length of the constants portion of the declaration of Enum,
// as described above.
template <<em>typename Enum</em>>
constexpr <em>size_t constants_length</em>(size_t index = 0, size_t accumulator = 0)
{ {
return return
index >= Enum::_size ? accumulator : <em>index >= Enum::_size ? accumulator :
constants_length<Enum>( constants_length<Enum>(
index + 1, accumulator index + 1, accumulator
+ string_length(", ") + string_length(", ")
+ string_length(Enum::_names()[index]) + string_length(Enum::_names()[index])
+ string_length(" = ") + string_length(" = ")
+ value_length( + value_length(
Enum::_values()[index]._to_integral())); Enum::_values()[index]._to_integral()))</em>;
} }
template <typename Enum> Finally, we can combine these to get the length of the formatted declaration of
constexpr size_t declaration_length() 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 return
string_length("ENUM(") <em>string_length("ENUM(")
+ string_length(Enum::_name()) + string_length(Enum::_name())
+ string_length(", int") + string_length(", int")
+ constants_length<Enum>() + 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> Now, we can declare the buffers. The memory will be reserved at load time by the
size_t format(char *buffer) 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; 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 += offset +=
std::sprintf(buffer + offset, std::sprintf(buffer + offset,
", %s = %i", <em>", %s = %i",
value._to_string(), value._to_integral()); 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() 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)); 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)); assert(depth_length + 1 == sizeof(depth_definition));
std::cout << channel_definition << std::endl; std::cout << <em>channel_definition</em> << std::endl;
std::cout << depth_definition << std::endl; std::cout << <em>depth_definition</em> << std::endl;
return 0; return 0;
} }
This outputs: It prints:
~~~comment ~~~comment
ENUM(Channel, int, Red = 0, Green = 1, Blue = 2) ENUM(Channel, int, Red = 0, Green = 1, Blue = 2)
ENUM(Depth, int, TrueColor = 1, HighColor = 0) ENUM(Depth, int, TrueColor = 1, HighColor = 0)
~~~ ~~~
This does have the advantage of not depending on anything else defined in the %% description = Contrived example that shows static memory allocation.
program, which isn't as easy to achieve with stringization.

View File

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

View File

@ -1,125 +1,199 @@
Have you noticed the awkward situation with enums in $cxx? They are missing Better Enums is a single header file that causes your compiler to generate
basic reflective features, such as string conversions. You are forced to put *reflective* enum types. This makes it easy to translate between enums and
them through big `switch` statements and write duplicate enum names. It's a strings, and much more.
maintenance nightmare.
$be is a short header file that gives you rich, reflective enums, with the Here's how to use a Better Enum:
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!
<div class="panes"> <div class="splash">
<div class="pane left"> <pre class="left">enable
<h3>$cxx11</h3>
<pre>#include &lt;iostream&gt; declare
<em>#include &lt;enum.h&gt;</em>
<em>ENUM(Channel, int,
Red = 1, Green, Blue)</em>
int main() parse
{ print
<em>Channel c = Channel::Red</em>;
std::cout &lt;&lt; <em>c._to_string()</em>;
<em>for (Channel c : Channel::_values())</em>
std::cout &lt;&lt; <em>c._to_string()</em>;
<em>switch (c)</em> { count
<em>case Channel::Red</em>: iterate
return <em>c._to_integral()</em>;
<em>case Channel::Green</em>: return 15;
<em>case Channel::Blue</em>: return 42;
} switch
safe cast
at compile time</pre>
<pre class="right"><em>#include</em> &lt;<em>enum.h</em>&gt;
<em>ENUM</em>(<em>Channel</em>, <em>int</em>, <em>Red</em> = <em>1</em>, <em>Green</em>, <em>Blue</em>)
Channel c = <em>Channel::_from_string("Red")</em>;
const char *s = <em>c._to_string()</em>;
size_t n = <em>Channel::_size</em>;
<em>for</em> (<em>Channel c</em> : <em>Channel::_values()</em>)
run_some_function(<em>c</em>);
<em>switch</em> (<em>c</em>) {
<em>case Channel::Red</em>: // ...
<em>case Channel::Green</em>: // ...
<em>case Channel::Blue</em>: // ...
} }
<em>constexpr</em> Channel c =
<em>Channel::_from_string("Blue")</em>;</pre>
</div>
<div class="pane right">
<h3>$cxx98</h3>
<pre>#include &lt;iostream&gt;
<em>#include &lt;enum.h&gt;</em>
<em>ENUM(Channel, int, Channel c = <em>Channel::_from_integral(3)</em>;
Red = 1, Green, Blue)</em>
int main()
{
<em>Channel c = Channel::Red</em>;
std::cout &lt;&lt; <em>c._to_string()</em>;
for (size_t i = 0; <em>constexpr</em> Channel c = <em>Channel::_from_string("Blue")</em>;</pre>
<em>i < Channel::_size</em>; ++i) {
c = <em>Channel::_values()[i]</em>;
std::cout &lt;&lt; <em>c._to_string()</em>;
}
<em>switch (c)</em> {
<em>case Channel::Red</em>:
return <em>c._to_integral()</em>;
<em>case Channel::Green</em>: return 15;
<em>case Channel::Blue</em>: return 42;
}
}</pre>
</div>
<div class="hack"></div>
</div> </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 <ul class="blurbs">
under the BSD license, so you can do pretty much anything you want with it. <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 <li class="even">
the project on [GitHub]($repo) for updates. <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"> <li class="even">
<div class="pane left"> <strong>No external dependencies</strong>
<h3>Tutorials</h3> <em>
<ol> Uses standard $cxx and supported on major compilers. Installation is
$tutorial_toc simple &mdash; just download <code>enum.h</code>.
</ol> </em>
</div> </li>
<li>
<div class="pane right"> <strong>Free and open source</strong>
<h3>Advanced demos</h3> <em>
<ul> Released under the BSD license for use in any project, free or commercial.
$demo_toc </em>
</ul> </li>
</div> </ul>
</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>
<div class="hack"></div> <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++ %% title = Clean reflective enums for C++
%% description = Reflective enums for C++ with clean syntax, in a header-only
library. Can be converted to strings, iterated, counted, and used for
metaprogramming. Free under the BSD license.
%% class = index

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,6 +4,7 @@
</p> </p>
<p class="up"> <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> </p>
</section> </section>

View File

@ -1 +0,0 @@
master

View File

@ -1 +1 @@
master 0.9.0

View File

@ -9,7 +9,7 @@ import mistune
import re import re
import sys 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") epilog = "At least one output file must be specified")
parser.add_argument("--o-html", metavar = "HTML", dest = "html_file", parser.add_argument("--o-html", metavar = "HTML", dest = "html_file",
help = "HTML output file name") help = "HTML output file name")
@ -46,17 +46,44 @@ def pretty_print(text, prefix, start_with_prefix = True):
return result 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): class HtmlRenderer(mistune.Renderer):
def __init__(self): def __init__(self):
super(HtmlRenderer, self).__init__() super(HtmlRenderer, self).__init__()
self._definitions = {} self._definitions = {}
self._table_of_contents = []
self._second_level = None
def header(self, text, level, raw = None): def header(self, text, level, raw = None):
if level == 2: if level == 2:
if "title" not in self._definitions: if "title" not in self._definitions:
self._definitions["title"] = text 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): def paragraph(self, text):
if text.startswith("%%"): if text.startswith("%%"):
@ -76,7 +103,6 @@ class HtmlRenderer(mistune.Renderer):
escaped = mistune.escape(re.sub("\n*$", "", code)) escaped = mistune.escape(re.sub("\n*$", "", code))
replaced = re.sub("&lt;em&gt;", "<em>", escaped) replaced = re.sub("&lt;em&gt;", "<em>", escaped)
replaced = re.sub("&lt;/em&gt;", "</em>", replaced) replaced = re.sub("&lt;/em&gt;", "</em>", replaced)
replaced = re.sub("#(ifn?def|endif).*\n?", "", replaced)
if lang == "comment": if lang == "comment":
start_tag = "<pre class=\"comment\">" start_tag = "<pre class=\"comment\">"
@ -86,6 +112,26 @@ class HtmlRenderer(mistune.Renderer):
return start_tag + replaced + "</pre>" return start_tag + replaced + "</pre>"
def definitions(self): 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 return self._definitions
def to_html(text): def to_html(text):
@ -95,6 +141,13 @@ def to_html(text):
definitions["body"] = html definitions["body"] = html
return definitions return definitions
def clean_text(text):
text = re.sub("<(?P<tag>[^> ]+)[^>]*>(.*?)</(?P=tag)>", "\g<2>", text)
text = re.sub("<(?P<tag>[^> ]+)[^>]*>(.*?)</(?P=tag)>", "\g<2>", text)
text = re.sub("&mdash;", "-", text)
text = re.sub("\$cxx", "C++", text)
return text
class CxxRenderer(mistune.Renderer): class CxxRenderer(mistune.Renderer):
def __init__(self): def __init__(self):
super(CxxRenderer, self).__init__() super(CxxRenderer, self).__init__()
@ -109,18 +162,25 @@ class CxxRenderer(mistune.Renderer):
if text.startswith("%%"): if text.startswith("%%"):
return "" return ""
if text == "$internal_toc":
return ""
self._not_in_list() self._not_in_list()
text = clean_text(text)
return self._join_paragraph() + pretty_print(text, "// ") return self._join_paragraph() + pretty_print(text, "// ")
def codespan(self, text): def codespan(self, text):
return text return text
def link(self, link, title, content):
return content
def list(self, body, ordered = True): def list(self, body, ordered = True):
return self._join_paragraph() + body return self._join_paragraph() + body
def list_item(self, text): def list_item(self, text):
return ("// %i. " % self._number_list_item()) + \ return ("// %i. " % self._number_list_item()) + \
pretty_print(text, "// ", False) pretty_print(clean_text(text), "// ", False)
def block_code(self, code, lang): def block_code(self, code, lang):
self._not_in_list() self._not_in_list()
@ -138,7 +198,7 @@ class CxxRenderer(mistune.Renderer):
def hrule(self): def hrule(self):
self._not_in_list() self._not_in_list()
self._not_paragraph() self._not_paragraph()
return "" return "\n"
def footnote_ref(self, key, index): def footnote_ref(self, key, index):
return "" return ""
@ -185,7 +245,7 @@ def main(md_file, html_file, cxx_file):
renderer = CxxRenderer() renderer = CxxRenderer()
source = mistune.Markdown(renderer = renderer).render(markdown) source = mistune.Markdown(renderer = renderer).render(markdown)
source = re.sub(r"\n*$", "\n", source) 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) open(cxx_file, "w").write(source)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -1,11 +1,11 @@
## Hello, World! ## 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> #include <iostream>
<em>#include "enum.h"</em> <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() 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!" Run it, and you should see the output "Hello, World!"
Congratulations, you have just created your first Better Enum! Congratulations, you have just created your first Better Enum!
%% description = Introductory Better Enums tutorial.

View File

@ -7,7 +7,7 @@ Let's begin by including `enum.h` and declaring our enum:
<em>#include <enum.h></em> <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. 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 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. string functions in detail, and the rest can be understood by analogy.
$internal_toc
### Strings ### Strings
There are three functions: There are three functions:
@ -117,12 +119,6 @@ use it carefully.
channel = <em>Channel::_from_integral_unchecked(0)</em>; channel = <em>Channel::_from_integral_unchecked(0)</em>;
// <em>Invalid</em> - better not to try converting it to string! // <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 ### Validity checking
For completeness, Better Enums also provides three validity checking functions, 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(); std::cout << (<em>+Channel::Cyan</em>)._to_string();
This is due to some type gymnastics in the implementation of Better Enums. The This is due to some type gymnastics in the implementation of Better Enums. The
<a>Reference</a> section has a full explanation. reference has a
[full explanation](${prefix}ApiReference.html#HelperFunctionsAndTypes).
---
This concludes the first tutorial!
--- ---
std::cout << std::endl; std::cout << std::endl;
return 0; return 0;
} }
%% description = Walkthrough of Better Enums conversion functions.

View File

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

View File

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

View File

@ -3,6 +3,8 @@
This tutorial shows some of the safety features of Better Enums: scope, how to This tutorial shows some of the safety features of Better Enums: scope, how to
control conversions, and the lack of a default constructor. control conversions, and the lack of a default constructor.
$internal_toc
### Scope ### Scope
You have probably noticed by now that Better Enums are scoped: when you declare 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> #include <cassert>
<em>#include <enum.h></em> <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 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 `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 effort. Better Enums brings scope uniformly to both variants. So, despite the
above declaration, you can safely declare 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. and everything will work as expected.
@ -42,22 +44,8 @@ will not compile:
int n = channel; int n = channel;
~~~ ~~~
The reason you have to opt into this feature with a macro is because it breaks The reason this is not enabled by default is explained in the reference page on
compatibility with the $cxx98 version of Better Enums. Specifically, when [strict conversions](${prefix}OptInFeatures.html#StrictConversions).
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.
### Default constructor ### 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: 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 This may be too strict, and I may relax it in the future. In the meantime, the
is that there are few places where a default constructor is truly needed. solution sketched in the [special values demo](${prefix}demo/SpecialValues.html)
can replace default constructors for some purposes, and in a more flexible way.
- If you want to opt in to a notion of default values, you can encode your I may eventually have the default constructor calling a template function like
project's policy into $cxx templates with ease, using building blocks Better the one in that demo.
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.
--- ---
return 0;
} }
%% description = Type safety features and limitations.

View File

@ -1,4 +1,4 @@
## Representation and alignment ## Representation
Let's go over some of the low-level properties of a Better Enum. This time, we 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. 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> #include <iostream>
<em>#include <enum.h></em> <em>#include <enum.h></em>
<em>ENUM(ContentType, short, <em>ENUM</em>(<em>ContentType</em>, <em>short</em>,
CompressedVideo = 5, PCM = 8, Subtitles = 17, Comment = 44)</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 This is for a hypothetical multimedia container file format. Perhaps the files
have sections, and each one has a header: have sections, and each one has a header:
@ -25,22 +25,21 @@ Here is what we have.
int main() 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 `ContentType` behaves just like a `short`[^*], in fact it simply wraps one. This
wraps one. This makes it possible to lay out structures in a predictable makes it possible to lay out structures in a predictable fashion:
fashion:
Header header = {ContentType::PCM, 0, 0}; Header <em>header</em> = {ContentType::PCM, 0, 0};
assert(<em>sizeof(header) == 8</em>); assert(<em>sizeof(header)</em> == <em>8</em>);
assert((size_t)&<em>header.flags -</em> (size_t)&<em>header.type == 2</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 `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 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; <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 should also be explicitly sized. However, this code is trying to be
compatible with $cxx98, where those names aren't available in a portable compatible with $cxx98, where those names aren't available in a portable
manner. manner.
%% description = Underlying representation.

View File

@ -2,27 +2,33 @@
When used with $cxx11, Better Enums are generated entirely during compilation. When used with $cxx11, Better Enums are generated entirely during compilation.
All the data is available for use by your own `constexpr` functions. The 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 examples in *this* tutorial aren't very useful, but look at the
to get an idea of what can be done. Here, you will see the basics. [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> #include <iostream>
// The reason for this is explained below.
#ifndef BETTER_ENUMS_CONSTEXPR_TO_STRING #ifndef BETTER_ENUMS_CONSTEXPR_TO_STRING
#define BETTER_ENUMS_CONSTEXPR_TO_STRING #define BETTER_ENUMS_CONSTEXPR_TO_STRING
#endif #endif
<em>#include <enum.h></em> <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> int value = <em>channel._to_integral()</em>;
<em>constexpr</em> const char *name = <em>channel._to_string()</em>; <em>constexpr</em> const char *name = <em>channel._to_string()</em>;
<em>constexpr</em> Channel parsed = <em>Channel::_from_string("Red")</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 All of the above are computed during compilation. The reason for the macro
things such as: 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>) <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 Which prints "5", the length of "Green". That 5 was also computed during
compilation. compilation.
%% description = Introduction to compile-time conversions.

2
enum.h
View File

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

View File

@ -1,8 +1,8 @@
// This file was generated automatically // This file was generated automatically.
// Hello, World! // 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 <iostream>
#include "enum.h" #include "enum.h"

View File

@ -1,73 +1,100 @@
// This file was generated automatically // This file was generated automatically.
// Special values // Special values
// //
// Suppose your project has a convention where each enum has special // Suppose your project has a convention where each enum has special invalid and
// <em>invalid</em> and <em>default</em> values. With Better Enums, you can // default values - for example, Enum::Invalid is invalid, and the first valid
// encode that directly at compile time, and then access each enum's special // constant is default. With Better Enums, you can get the compiler to enforce
// values using syntax like Channel c = default_ and Channel c = invalid. This // the convention. At the end of this demo, we will have defined functions and
// can make your code adapt automatically to changes in enum definitions, as // templates that allow us to write:
// well as make it easier to read and understand its intent. //
// 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 <iostream>
#include <stdexcept> #include <stdexcept>
#include <enum.h> #include <enum.h>
// Invalid // 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
// Perhaps the invalid value is usually called Invalid, but not in all enums. // unspecialized version will encode the default policy:
// You can encode that using a function template for the common case, and a
// macro that creates specializations:
template <typename Enum> 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) \ #define OVERRIDE_INVALID(Enum, Value) \
template<> \ 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) ENUM(Channel, int, Red, Green, Blue, Invalid)
// Invalid is the invalid value by default
ENUM(Compression, int, Undefined, None, Huffman) ENUM(Compression, int, Undefined, None, Huffman)
OVERRIDE_INVALID(Compression, Undefined) OVERRIDE_INVALID(Compression, Undefined)
// and use them: // and use them:
static_assert(find_invalid<Channel>() == +Channel::Invalid, ""); static_assert(invalid_impl<Channel>() == +Channel::Invalid, "");
static_assert(find_invalid<Compression>() == +Compression::Undefined, ""); static_assert(invalid_impl<Compression>() == +Compression::Undefined, "");
// This even supports enums that don't have an invalid value at all. As long as // 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 // they don't have a constant called Invalid, you will get a compile-time error
// if you try to call invalid() on them &mdash; as you probably should! // if you try to call invalid_impl<>() on them - as you probably should!
// //
// Default // Default values
// //
// To encode the policy on default values, we need to do a compile-time check // Perhaps here the convention is the first value that is not invalid is
// that the first value is not invalid. Otherwise, the technique is the same. // 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> template <typename Enum>
constexpr Enum find_default() constexpr Enum default_impl()
{ {
return return
Enum::_size < 2 ? Enum::_size < 2 ?
throw std::logic_error("enum has no valid constants") : 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()[1] :
Enum::_values()[0]; 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) \ #define OVERRIDE_DEFAULT(Enum, Value) \
static_assert(Enum::Value != Enum::Invalid, \ static_assert(Enum::Value != Enum::Invalid, \
#Enum ": default cannot equal invalid"); \ #Enum ": default cannot equal invalid"); \
template<> \ 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(default_impl<Channel>() == +Channel::Red, "");
static_assert(find_default<Compression>() == +Compression::None, ""); static_assert(default_impl<Compression>() == +Compression::None, "");
// And, if you do // And, if you do
@ -79,48 +106,61 @@ ENUM(Answer, int, Yes, No, Invalid)
// //
// Making the syntax nicer // Making the syntax nicer
// //
// For the final touch, we will make the syntax better by introducing new // At this point, our policy is encoded by the ugly-looking functions
// "keywords" called default_ and invalid in such a way that we cause the // invalid_impl and default_impl. We want a nicer syntax. The main reason we
// compiler to do type inference: // 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
template <typename Enum> // write things like
struct assert_enum { //
using check = typename Enum::_enumerated; // Channel channel = invalid_impl<Channel>();
using type = Enum; //
}; // 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 { struct invalid_t {
template <typename To> template <typename To>
constexpr operator To() const { return find_invalid<To>(); } constexpr operator To() const { return invalid_impl<To>(); }
template <typename To>
constexpr To convert() const { return find_invalid<To>(); }
}; };
struct default_t { struct default_t {
template <typename To> template <typename To>
constexpr operator To() const { return find_default<To>(); } constexpr operator To() const { return default_impl<To>(); }
}; };
constexpr invalid_t invalid{}; constexpr invalid_t invalid{};
constexpr default_t default_{}; 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(+Channel::Invalid == invalid, "");
static_assert(+Compression::Undefined == invalid, ""); static_assert(+Compression::Undefined == invalid, "");
static_assert(+Channel::Red == default_, ""); static_assert(+Channel::Red == default_, "");
static_assert(+Compression::None == 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() int main()
{ {
dump(invalid);
Channel channel = default_; Channel channel = default_;
std::cout << channel._to_string() << std::endl; dump(channel);
return 0; return 0;
} }
// There are many possible variations of these policies, but I think most of // 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 // them can be encoded in a reasonable fashion using the tools Better Enums
// provides. Enjoy! // provides. Enjoy!

View File

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

View File

@ -1,53 +1,85 @@
// This file was generated automatically // This file was generated automatically.
// Semi-quine // Semi-quine
// //
// Let's make a Better Enum compose its own definition. It won't be literally as // Let's make a Better Enum assemble its own definition in memory. It won't be
// defined, since we will lose some information about initializers, but we will // literally as defined, since we will lose the exact initializer expressions,
// be able to preserve their numeric values. We will reserve the memory buffers // but we will be able to preserve the numeric values. We will reserve the
// at compile time. // memory buffer for the definition at compile time.
// //
// There are actually better ways to do this than shown here. You could define a // Ok, so it's not really a quine, because we won't be writing all the code
// macro that expands to an ENUM declaration and also stringizes it. The point // needed to generate the definition to the buffer as well. And, there are
// here is to show some of the reflective capabilities of Better Enums, so you // better ways to dump the definition than shown here. You could simply define a
// can adapt them for cases where a macro is not sufficient. // 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 <cassert>
#include <cstdio> #include <cstdio>
#include <iostream> #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 #ifndef BETTER_ENUMS_CONSTEXPR_TO_STRING
#define BETTER_ENUMS_CONSTEXPR_TO_STRING #define BETTER_ENUMS_CONSTEXPR_TO_STRING
#endif #endif
#include <enum.h> #include <enum.h>
#define HIGH_COLOR 0 // Now, let's declare some enums to dump later:
ENUM(Channel, int, Red, Green, Blue) 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) constexpr size_t value_length(int n, int bound = 10, size_t digits = 1)
{ {
return return
n < bound ? digits : value_length(n, bound * 10, digits + 1); 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) constexpr size_t string_length(const char *s, size_t index = 0)
{ {
return s[index] == '\0' ? index : string_length(s, index + 1); 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> template <typename Enum>
constexpr size_t constants_length(size_t index = 0, size_t accumulator = 0) constexpr size_t constants_length(size_t index = 0, size_t accumulator = 0)
{ {
return return
index >= Enum::_size ? accumulator : index >= Enum::_size ? accumulator :
constants_length<Enum>( constants_length<Enum>(
index + 1, accumulator index + 1, accumulator
+ string_length(", ") + string_length(", ")
@ -57,6 +89,12 @@ constexpr size_t constants_length(size_t index = 0, size_t accumulator = 0)
Enum::_values()[index]._to_integral())); 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> template <typename Enum>
constexpr size_t declaration_length() constexpr size_t declaration_length()
{ {
@ -68,12 +106,21 @@ constexpr size_t declaration_length()
+ string_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 channel_definition[declaration_length<Channel>() + 1];
char depth_definition[declaration_length<Depth>() + 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> template <typename Enum>
size_t format(char *buffer) size_t format(char *buffer)
@ -94,6 +141,12 @@ size_t format(char *buffer)
return offset; return offset;
} }
// Checking our work
//
// Now, we can write and run this code.
int main() int main()
{ {
size_t channel_length = format<Channel>(channel_definition); size_t channel_length = format<Channel>(channel_definition);
@ -108,10 +161,7 @@ int main()
return 0; return 0;
} }
// This outputs: // It prints:
// //
// ENUM(Channel, int, Red = 0, Green = 1, Blue = 2) // ENUM(Channel, int, Red = 0, Green = 1, Blue = 2)
// ENUM(Depth, int, TrueColor = 1, HighColor = 0) // ENUM(Depth, int, TrueColor = 1, HighColor = 0)
//
// This does have the advantage of not depending on anything else defined in the
// program, which isn't as easy to achieve with stringization.

View File

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

View File

@ -1,4 +1,4 @@
// This file was generated automatically // This file was generated automatically.
// Iteration // Iteration
// //
@ -28,15 +28,17 @@ int main()
std::cout << std::endl; std::cout << std::endl;
// will print "Red Green Blue". // 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()) // for (Channel channel : Channel::_values())
// std::cout << channel._to_integral() << " "; // std::cout << channel._to_integral() << " ";
// std::cout << std::endl; // std::cout << std::endl;
// //
// for (const char *name : Channel::_names()) // for (const char *name : Channel::_names())
// std::cout << name << " "; // std::cout << name << " ";
// std::cout << std::endl; // std::cout << std::endl;
return 0; return 0;
} }

View File

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

View File

@ -1,4 +1,4 @@
// This file was generated automatically // This file was generated automatically.
// Scope and safety // Scope and safety
// //
@ -16,10 +16,10 @@
ENUM(Channel, int, Red = 1, Green, Blue) ENUM(Channel, int, Red = 1, Green, Blue)
// you don't get names such as Red in the global namespace. Instead, you get // 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 // Channel, and Red is accessible as Channel::Red. This is no big deal in C++11,
// $cxx11, which has enum class. In $cxx98, however, this typically requires // which has enum class. In C++98, however, this typically requires effort.
// effort. Better Enums brings scope uniformly to both variants. So, despite the // Better Enums brings scope uniformly to both variants. So, despite the above
// above declaration, you can safely declare // declaration, you can safely declare
ENUM(Node, char, Red, Black) ENUM(Node, char, Red, Black)
@ -31,31 +31,19 @@ int main()
// Implicit conversion // 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 // 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. // 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 // compiler supports enum class and you define BETTER_ENUMS_STRICT_CONVERSION
// before including enum.h, the following code will not compile: // before including enum.h, the following code will not compile:
// //
// Channel channel = Channel::Red; // Channel channel = Channel::Red;
// int n = channel; // int n = channel;
// //
// The reason you have to opt into this feature with a macro is because it // The reason this is not enabled by default is explained in the reference page
// breaks compatibility with the $cxx98 version of Better Enums. Specifically, // on strict conversions.
// 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.
// //
// Default constructor // Default constructor
// //
@ -71,31 +59,15 @@ int main()
// future-proof. // future-proof.
// //
// So, if you uncomment this code, the file won't compile: // 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 // Channel channel;
// project's policy into $cxx templates with ease, using building blocks //
// Better Enums provides. The solution sketched <a // This may be too strict, and I may relax it in the future. In the meantime,
// href="${prefix}demo/SpecialValues.html">here</a> is arguably more // the solution sketched in the special values demo can replace default
// flexible than any fixed choice Better Enums could impose on you. // constructors for some purposes, and in a more flexible way. I may eventually
// 2. If a Better Enum value is the result of a large sequence of statements, // have the default constructor calling a template function like the one in that
// you may be able to move those statements into a separate function that // demo.
// 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.
return 0;
} }

View File

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

View File

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

View File

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

View File

@ -1 +1,2 @@
Invalid
Red Red

View File

@ -0,0 +1 @@
0000000000000010100001

View File

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