mirror of
https://github.com/aantron/better-enums.git
synced 2025-12-06 16:56:42 +08:00
Updated and improved documentation.
This commit is contained in:
parent
3a316e6f79
commit
b24d155b7b
81
README.md
81
README.md
@ -1,7 +1,9 @@
|
|||||||
# Better Enums
|
# 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 — 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 — 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 — 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`.
|
||||||
|
|||||||
@ -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__` —
|
||||||
|
all major compilers — 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 };
|
|
||||||
|
### Helper functions and types
|
||||||
|
|
||||||
|
The types and functions described here make it possible to use Better Enums with
|
||||||
|
the rest of $cxx in a reasonable fashion, or else they are referenced in the
|
||||||
|
rest of the documentation.
|
||||||
|
|
||||||
|
#### <em>typedef _enumerated</em>
|
||||||
|
|
||||||
|
An internal type used to declare constants. The `ENUM` macro generates something
|
||||||
|
similar to
|
||||||
|
|
||||||
|
~~~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 this type in only one situation. A literal constant
|
The user needs to be aware of `_enumerated` in only one situation. A literal
|
||||||
such as `Enum::A` is an expression of type `Enum::_enumerated`, not `Enum`. It
|
constant such as `Enum::A` is an expression of type `Enum::_enumerated`, not
|
||||||
is not possible to directly call a method on the value, as in
|
`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.
|
`Enum::A._to_string()`. This problem is addressed by operator `+`
|
||||||
|
[below](#Operator+).
|
||||||
|
|
||||||
#### non-member constexpr Enum unary operator +(_enumerated)
|
#### non-member constexpr Enum unary <em>operator +</em>(_enumerated)
|
||||||
|
|
||||||
Forces promotion of `Enum::_enumerated` to `Enum`. Provided to solve the problem
|
Forces promotion of [`Enum::_enumerated`](#Typedef_enumerated) to `Enum`.
|
||||||
described under `_enumerated` above. So, for example, it is necessary to write
|
Provided to solve the problem described [above](#Typedef_enumerated). So:
|
||||||
`(+Enum::A)._to_string()` instead of `Enum::A._to_string`.
|
|
||||||
|
|
||||||
#### constexpr implicit constructor Enum(_enumerated)
|
// Does not compile
|
||||||
|
<em>Enum::A</em>.<em>_to_string</em>()
|
||||||
|
|
||||||
A constructor that performs implicit conversions of `Enum::_enumerated` to
|
// Compiles
|
||||||
`Enum`. This allows code to use a literal constant where `Enum` is expected in
|
(<em>+Enum::A</em>).<em>_to_string</em>()
|
||||||
most situations — those where the compiler can do an implicit conversion.
|
|
||||||
For example, if there is a function `void output(Enum)`, it can be called as
|
|
||||||
`output(Enum::A)`, without having to write `output(+Enum::A)`. This constructor
|
|
||||||
is also used for the typical initialization
|
|
||||||
|
|
||||||
Enum value = Enum::A;
|
#### 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><Enum>
|
||||||
|
|
||||||
|
An optional `Enum` value. These are returned by the various `_nothrow`
|
||||||
|
functions, such as [`_from_string_nothrow`](#_from_string_nothrow). This type is
|
||||||
|
meant to represent the possibility of failure. For example, suppose you have:
|
||||||
|
|
||||||
|
<em>better_enums::optional</em><<em>Enum</em>> <em>maybe</em> = <em>_from_string_nothrow</em>(<em>"A"</em>);
|
||||||
|
|
||||||
|
An optional value such as `maybe` is convertible to `bool`. If it converts to
|
||||||
|
`true`, it holds a valid `Enum` value. Otherwise, if it converts to `false`, the
|
||||||
|
operation that produced the optional value failed. So, you can continue with
|
||||||
|
|
||||||
|
<em>if</em> (<em>maybe</em>) {
|
||||||
|
// The string conversion succeeded
|
||||||
|
do_something(<em>*maybe</em>);
|
||||||
|
}
|
||||||
|
<em>else</em> {
|
||||||
|
// The string conversion failed
|
||||||
|
}
|
||||||
|
|
||||||
|
As you can see, `*maybe` evaluates to the `Enum` value, in this case `Enum::A`.
|
||||||
|
|
||||||
|
The rest of this reference refers to this type as simply `optional`, as if you
|
||||||
|
had entered
|
||||||
|
|
||||||
|
~~~comment
|
||||||
|
using <em>optional</em> = <em>better_enums::optional</em><<em>Enum</em>>;
|
||||||
|
~~~
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Value count and iteration
|
||||||
|
|
||||||
|
The types and members described here have to do with the sequence of constants
|
||||||
|
declared, i.e. `A`, `B`, `C` in the [running example](#RunningExample).
|
||||||
|
|
||||||
|
#### static constexpr size_t <em>_size</em>
|
||||||
|
|
||||||
The number of constants declared. `Enum::_size == 3`.
|
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 — adding `1` is a `constexpr` alternative.
|
`operator++` are not `constexpr` due to their nature — adding `1` is a
|
||||||
|
`constexpr` alternative. You typically don't have to refer to this type
|
||||||
|
directly.
|
||||||
|
|
||||||
#### static constexpr? _name_iterable _names()
|
#### static constexpr? _name_iterable <em>_names</em>()
|
||||||
|
|
||||||
Access to the collection of declared constant names. For example:
|
Access to the sequence of declared constant names. For example:
|
||||||
|
|
||||||
for (size_t index = 0; index < Enum::_names().size(); ++index)
|
<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.
|
||||||
|
|||||||
@ -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
15
doc/Contact.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
## Contact
|
||||||
|
|
||||||
|
- Send me an email: [antonbachin@yahoo.com](mailto:antonbachin@yahoo.com).
|
||||||
|
- Visit the [GitHub]($repo) project to open an issue or get a development
|
||||||
|
version.
|
||||||
|
|
||||||
|
I also watch the `enums` tag on Stack Overflow.
|
||||||
|
|
||||||
|
I'm happy to hear any feedback. If you have any trouble using the library or
|
||||||
|
parsing the documentation, please don't hesitate to let me know.
|
||||||
|
|
||||||
|
And, if you find this library helpful, give it a star on GitHub to let me know
|
||||||
|
you use it. It will help keep me encouraged :)
|
||||||
|
|
||||||
|
%% description = Contact information for bugs, issues, support, and feedback.
|
||||||
40
doc/ExtendingLimits.md
Normal file
40
doc/ExtendingLimits.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
## Extending limits
|
||||||
|
|
||||||
|
The `ENUM` macro makes heavy use of the preprocessor, and some of the internal
|
||||||
|
macros have size limits. There are two: on the number of constants you can
|
||||||
|
declare, and on the maximum length of a constant name under very specific
|
||||||
|
conditions. If you run into either one, you can extend the limit by following
|
||||||
|
the instructions on this page.
|
||||||
|
|
||||||
|
The second limit, on the maximum length of a constant name, applies only when
|
||||||
|
you are compiling an enum in
|
||||||
|
["full" `constexpr`](${prefix}OptInFeatures.html#CompileTimeNameTrimming) mode
|
||||||
|
*and* the constant has an initializer. Otherwise, your constants can have names
|
||||||
|
of arbitrary length.
|
||||||
|
|
||||||
|
The default limits are 64 constants in an enum and 23 characters for initialized
|
||||||
|
constants of full-`constexpr` enums. To extend:
|
||||||
|
|
||||||
|
1. Pick your desired limits. I will use 512 constants and 63 characters as an
|
||||||
|
example. Add 1 to the number of characters to account for the null
|
||||||
|
terminator — our numbers are now 512 and 64.
|
||||||
|
2. Get `make_macros.py` from your copy of the full Better Enums distribution
|
||||||
|
or from <a href="https://raw.githubusercontent.com/aantron/better-enums/$version/script/make_macros.py" download>GitHub</a>.
|
||||||
|
3. You will run this script to generate a header file containing some
|
||||||
|
replacement macros for `enum.h` to use. Pick a name for this file and a
|
||||||
|
location somewhere in your include path. I will assume that this file is
|
||||||
|
`common/enum_macros.h` in your project.
|
||||||
|
4. Run `python make_macros.py 512 64 > common/enum_macros.h`.
|
||||||
|
5. Define `BETTER_ENUMS_MACRO_FILE <common/enum_macros.h>` before including
|
||||||
|
`enum.h`. This is typically done by supplying extra flags to the compiler
|
||||||
|
on the command line:
|
||||||
|
|
||||||
|
- For g++ and clang++, `-DBETTER_ENUMS_MACRO_FILE='<common/enum_macros.h>'`
|
||||||
|
- For VC++, `\DBETTER_ENUMS_MACRO_FILE='<common/enum_macros.h>'`
|
||||||
|
|
||||||
|
6. Enjoy the looser limits. Just watch out — increasing the second
|
||||||
|
number can really slow down compilation of full-`constexpr` enums.
|
||||||
|
7. You don't need `make_macros.py` anymore. It's not part of your build
|
||||||
|
process and you can delete it.
|
||||||
|
|
||||||
|
%% description = How to extend limits imposed by internal macros.
|
||||||
@ -1,41 +0,0 @@
|
|||||||
## Extending macro limits
|
|
||||||
|
|
||||||
The `ENUM` macro makes heavy use of the preprocessor. The preprocessor can't
|
|
||||||
perform intelligent operations on sequences of tokens with arbitrary lengths
|
|
||||||
— the operations must be bounded. The result is that there are two size
|
|
||||||
limits: on the number of constants that a Better Enum can have, and on the
|
|
||||||
maximum length of a constant name under certain conditions. If you run into
|
|
||||||
either of these limits, follow the steps on this page to increase them.
|
|
||||||
|
|
||||||
The conditions for the constant name length limit rarely apply. They are:
|
|
||||||
|
|
||||||
- you are compiling an enum with compile-time string trimming, a.k.a. a
|
|
||||||
"slow enum", which is only enabled when you do [this](), and
|
|
||||||
- you have a constant with an initializer.
|
|
||||||
|
|
||||||
Constants without initializers can always have arbitrarily-long names.
|
|
||||||
|
|
||||||
The default limits are 64 constants and 23 characters for names of slow enum
|
|
||||||
constants that have initializers. To extend these limits:
|
|
||||||
|
|
||||||
1. Pick your desired limits. I will use 512 constants and 63 characters as an
|
|
||||||
example. Add 1 to the number of characters to account for the null
|
|
||||||
terminator — our numbers are now 512 and 64.
|
|
||||||
2. Get `make_macros.py` from your copy of the Better Enums distribution or
|
|
||||||
from [GitHub]().
|
|
||||||
3. You will run this script to generate a header file containing some
|
|
||||||
replacement macros for `enum.h` to use. Pick a name for this file and a
|
|
||||||
location somewhere in your include path. I will assume that this file is
|
|
||||||
`common/enum_macros.h`.
|
|
||||||
4. Run `python make_macros.py 512 64 > common/enum_macros.h`.
|
|
||||||
5. Define `BETTER_ENUMS_MACRO_FILE <common/enum_macros.h>` before including
|
|
||||||
`enum.h`. This is typically done by supplying extra flags to the compiler
|
|
||||||
on the command line:
|
|
||||||
|
|
||||||
- For g++ and clang++, `-DBETTER_ENUMS_MACRO_FILE='<common/enum_macros.h>'`
|
|
||||||
- For VC++, `\DBETTER_ENUMS_MACRO_FILE='<common/enum_macros.h>'`
|
|
||||||
|
|
||||||
6. Enjoy the looser limits. Just watch out — increasing the second
|
|
||||||
number can really slow down compilation of compile-time trimming enums.
|
|
||||||
7. You don't need `make_macros.py` anymore. It's not part of your build
|
|
||||||
process and you can delete it.
|
|
||||||
@ -1,34 +1,41 @@
|
|||||||
## Opt-in features
|
## 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 — you need about a dozen
|
||||||
|
slow enums to get the same penalty as including `iostream` — but it is
|
||||||
|
a steep penalty nonetheless. I don't think most people need this feature most of
|
||||||
|
the time, so it's too high a price to pay. If I improve compilation times to the
|
||||||
|
point where compile-time name trimming can be the default, I will simply
|
||||||
|
redefine `SLOW_ENUM` as `ENUM` and deprecate it, so your code will still work.
|
||||||
|
|
||||||
### Strict conversions
|
### 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` —
|
|
||||||
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
32
doc/Performance.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
## Performance
|
||||||
|
|
||||||
|
A basic performance test is run on
|
||||||
|
[every compiler tested](${prefix}CompilerSupport.html#TestedConfigurations). It
|
||||||
|
doesn't try to be very accurate — it just stress-tests the compiler once
|
||||||
|
to get a rough idea of how long it takes to compile Better Enums.
|
||||||
|
|
||||||
|
The files compared in the test are as follows:
|
||||||
|
|
||||||
|
- [One file]($repo/blob/$version/test/performance/4-declare_enums.cc) includes
|
||||||
|
`enum.h` and declares 647 constants across 36 Better Enums.
|
||||||
|
- The [other file]($repo/blob/$version/test/performance/5-iostream.cc) *only*
|
||||||
|
includes `iostream` and does nothing with it.
|
||||||
|
|
||||||
|
The argument is that if compiling a bunch of Better Enums is faster, or about as
|
||||||
|
fast as, including a single standard header such as `iostream`, then Better
|
||||||
|
Enums is fast enough for general use.
|
||||||
|
|
||||||
|
Results are given for select compilers and
|
||||||
|
[configurations](${prefix}CompilerSupport.html#CompileTimeReflectionConfigurations)
|
||||||
|
as ratios of how long it took to compile the Better Enums file to how long it
|
||||||
|
took to compile the `iostream` file. The less the ratio, the better. Ratios less
|
||||||
|
than 1 mean the enums compiled faster, and ratios greater than 1 mean `iostream`
|
||||||
|
compiled faster.
|
||||||
|
|
||||||
|
- clang 3.6, fast `constexpr`: 0.66
|
||||||
|
- clang 3.6, full `constexpr`: 2.25
|
||||||
|
- gcc 5.1, fast `constexpr`: 1.58
|
||||||
|
- gcc 5.1, full `constexpr`: 4.23
|
||||||
|
- VC2015RC, $cxx98: 1.18
|
||||||
|
|
||||||
|
%% description = Compilation performance testing results.
|
||||||
@ -6,7 +6,7 @@ body {
|
|||||||
font-weight: 300;
|
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;
|
||||||
@ -277,3 +280,148 @@ div.back {
|
|||||||
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;
|
||||||
|
}
|
||||||
|
|||||||
@ -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 — 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 — 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 — as you probably should!
|
if you try to call `invalid_impl<>()` on them — as you probably should!
|
||||||
|
|
||||||
### Default
|
### Default values
|
||||||
|
|
||||||
To encode the policy on default values, we need to do a compile-time check that
|
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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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 — 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.
|
|
||||||
|
|||||||
49
doc/docs.py
49
doc/docs.py
@ -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)
|
||||||
|
|
||||||
@ -68,7 +70,11 @@ def compose_page(relative_path, definitions):
|
|||||||
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()
|
||||||
|
|||||||
252
doc/index.md
252
doc/index.md
@ -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 <iostream>
|
declare
|
||||||
<em>#include <enum.h></em>
|
|
||||||
|
|
||||||
<em>ENUM(Channel, int,
|
|
||||||
Red = 1, Green, Blue)</em>
|
|
||||||
|
|
||||||
int main()
|
parse
|
||||||
{
|
print
|
||||||
<em>Channel c = Channel::Red</em>;
|
|
||||||
std::cout << <em>c._to_string()</em>;
|
|
||||||
|
|
||||||
<em>for (Channel c : Channel::_values())</em>
|
|
||||||
std::cout << <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> <<em>enum.h</em>>
|
||||||
|
|
||||||
|
<em>ENUM</em>(<em>Channel</em>, <em>int</em>, <em>Red</em> = <em>1</em>, <em>Green</em>, <em>Blue</em>)
|
||||||
|
|
||||||
|
|
||||||
|
Channel c = <em>Channel::_from_string("Red")</em>;
|
||||||
|
const char *s = <em>c._to_string()</em>;
|
||||||
|
|
||||||
|
|
||||||
|
size_t n = <em>Channel::_size</em>;
|
||||||
|
<em>for</em> (<em>Channel c</em> : <em>Channel::_values()</em>)
|
||||||
|
run_some_function(<em>c</em>);
|
||||||
|
|
||||||
|
|
||||||
|
<em>switch</em> (<em>c</em>) {
|
||||||
|
<em>case Channel::Red</em>: // ...
|
||||||
|
<em>case Channel::Green</em>: // ...
|
||||||
|
<em>case Channel::Blue</em>: // ...
|
||||||
}
|
}
|
||||||
|
|
||||||
<em>constexpr</em> Channel c =
|
|
||||||
<em>Channel::_from_string("Blue")</em>;</pre>
|
Channel c = <em>Channel::_from_integral(3)</em>;
|
||||||
|
|
||||||
|
|
||||||
|
<em>constexpr</em> Channel c = <em>Channel::_from_string("Blue")</em>;</pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="pane right">
|
|
||||||
<h3>$cxx98</h3>
|
|
||||||
<pre>#include <iostream>
|
|
||||||
<em>#include <enum.h></em>
|
|
||||||
|
|
||||||
<em>ENUM(Channel, int,
|
### What do you get?
|
||||||
Red = 1, Green, Blue)</em>
|
|
||||||
|
|
||||||
int main()
|
<ul class="blurbs">
|
||||||
{
|
<li class="even">
|
||||||
<em>Channel c = Channel::Red</em>;
|
<strong>Uniform interface for $cxx98 and $cxx11</strong>
|
||||||
std::cout << <em>c._to_string()</em>;
|
<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>
|
||||||
|
|
||||||
for (size_t i = 0;
|
<li class="even">
|
||||||
<em>i < Channel::_size</em>; ++i) {
|
<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>
|
||||||
|
|
||||||
c = <em>Channel::_values()[i]</em>;
|
<li class="even">
|
||||||
std::cout << <em>c._to_string()</em>;
|
<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>
|
||||||
|
|
||||||
<em>switch (c)</em> {
|
<li class="even">
|
||||||
<em>case Channel::Red</em>:
|
<strong>No build-time generator needed</strong>
|
||||||
return <em>c._to_integral()</em>;
|
<em>
|
||||||
<em>case Channel::Green</em>: return 15;
|
Just include <code>enum.h</code>. It's a metaprogram executed by your
|
||||||
<em>case Channel::Blue</em>: return 42;
|
compiler.
|
||||||
}
|
</em>
|
||||||
}</pre>
|
</li>
|
||||||
</div>
|
<li>
|
||||||
|
<strong>Fast compilation</strong>
|
||||||
|
<em>
|
||||||
|
Much less impact on build time than even just including
|
||||||
|
<code>iostream</code>.
|
||||||
|
</em>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="even">
|
||||||
|
<strong>No external dependencies</strong>
|
||||||
|
<em>
|
||||||
|
Uses standard $cxx and supported on major compilers. Installation is
|
||||||
|
simple — just download <code>enum.h</code>.
|
||||||
|
</em>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Free and open source</strong>
|
||||||
|
<em>
|
||||||
|
Released under the BSD license for use in any project, free or commercial.
|
||||||
|
</em>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<div class="hack"></div>
|
<div class="hack"></div>
|
||||||
</div>
|
|
||||||
|
|
||||||
To install, simply <a $download>download enum.h</a> and add it to your project.
|
### It's what built-in enums ought to do.
|
||||||
|
|
||||||
That's all. The library is header-only and has no dependencies. It is published
|
The library notionally <em>extends</em> $cxx, adding oft-needed features.
|
||||||
under the BSD license, so you can do pretty much anything you want with it.
|
|
||||||
|
|
||||||
---
|
<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>
|
||||||
|
|
||||||
Better Enums is under active development and will always be supported. Follow
|
<div class="hack"></div>
|
||||||
the project on [GitHub]($repo) for updates.
|
|
||||||
|
|
||||||
---
|
### Resources
|
||||||
|
|
||||||
<div class="panes">
|
<ul class="blurbs resources">
|
||||||
<div class="pane left">
|
<li class="even">
|
||||||
<h3>Tutorials</h3>
|
<a id="Tutorial"></a>
|
||||||
|
<strong>Tutorial</strong>
|
||||||
<ol>
|
<ol>
|
||||||
$tutorial_toc
|
$tutorial_toc
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<div class="pane right">
|
<li class="even">
|
||||||
<h3>Advanced demos</h3>
|
<a id="CompileTimeDemos"></a>
|
||||||
|
<strong>Compile-time demos</strong>
|
||||||
<ul>
|
<ul>
|
||||||
$demo_toc
|
$demo_toc
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panes">
|
|
||||||
<div class="pane left">
|
|
||||||
<h3>Reference</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="${prefix}ApiReference.html">API reference</a></li>
|
|
||||||
<li><a href="${prefix}OptInFeatures.html">Opt-in features</a></li>
|
|
||||||
<li>
|
|
||||||
<a href="${prefix}ExtendingMacroLimits.html">Extending macro limits</a>
|
|
||||||
</li>
|
</li>
|
||||||
<li><a href="${prefix}CompilerSupport.html">Compiler support</a></li>
|
|
||||||
<li><a href="${prefix}performance.html">Performance</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="hack"></div>
|
<div class="hack"></div>
|
||||||
|
|
||||||
<!-- Contact -->
|
|
||||||
|
|
||||||
<!-- omfg -->
|
|
||||||
|
|
||||||
<!-- Development blurb -->
|
|
||||||
|
|
||||||
<!-- Prompts and such -->
|
|
||||||
|
|
||||||
%% 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
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
## Performance
|
|
||||||
|
|
||||||
This is a placeholder page — the only testing done so far shows that
|
|
||||||
compiling a large (30+) enums on clang is faster than merely including
|
|
||||||
`iostream`. More conclusive information coming today or in a few days! For the
|
|
||||||
clang test, compilation of all those Better Enums takes about 60% of the time
|
|
||||||
that processing `iostream` does.
|
|
||||||
11
doc/template/demo.tmpl
vendored
11
doc/template/demo.tmpl
vendored
@ -1,6 +1,13 @@
|
|||||||
<p>
|
<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>
|
||||||
|
|||||||
2
doc/template/download.tmpl
vendored
2
doc/template/download.tmpl
vendored
@ -1,2 +1,2 @@
|
|||||||
href="https://raw.githubusercontent.com/aantron/better-enums/$ref/enum.h"
|
href="https://raw.githubusercontent.com/aantron/better-enums/$version/enum.h"
|
||||||
download
|
download
|
||||||
5
doc/template/footer.tmpl
vendored
5
doc/template/footer.tmpl
vendored
@ -1,11 +1,14 @@
|
|||||||
</main>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
|
<div class="container">
|
||||||
Copyright © 2015 Anton Bachin. Released under the BSD 2-clause license.
|
Copyright © 2015 Anton Bachin. Released under the BSD 2-clause license.
|
||||||
See <a href="https://github.com/aantron/better-enums/blob/master/LICENSE">
|
See <a href="https://github.com/aantron/better-enums/blob/master/LICENSE">
|
||||||
LICENSE</a>.
|
LICENSE</a>.
|
||||||
<br />
|
<br />
|
||||||
This page is part of the documentation for Better Enums $version.
|
This page is part of the documentation for Better Enums $version.
|
||||||
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
20
doc/template/header.tmpl
vendored
20
doc/template/header.tmpl
vendored
@ -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"> </div>
|
<div class="spacer"> </div>
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
|
<div class="container">
|
||||||
<div class="back">{}</div>
|
<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">
|
||||||
|
|||||||
3
doc/template/next.tmpl
vendored
3
doc/template/next.tmpl
vendored
@ -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>
|
||||||
|
|||||||
1
doc/template/ref.tmpl
vendored
1
doc/template/ref.tmpl
vendored
@ -1 +0,0 @@
|
|||||||
master
|
|
||||||
2
doc/template/version.tmpl
vendored
2
doc/template/version.tmpl
vendored
@ -1 +1 @@
|
|||||||
master
|
0.9.0
|
||||||
@ -9,7 +9,7 @@ import mistune
|
|||||||
import re
|
import 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("<em>", "<em>", escaped)
|
replaced = re.sub("<em>", "<em>", escaped)
|
||||||
replaced = re.sub("</em>", "</em>", replaced)
|
replaced = re.sub("</em>", "</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("—", "-", 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__":
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -45,3 +45,5 @@ If you are using $cxx11, you can have much nicer syntax:
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%% description = Iterating over all Better Enums constants.
|
||||||
|
|||||||
@ -26,3 +26,5 @@ you a warning — try it!
|
|||||||
std::cout << n << std::endl;
|
std::cout << n << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%% description = Usage in switch statements.
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
This tutorial shows some of the safety features of Better Enums: scope, how to
|
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.
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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
2
enum.h
@ -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_
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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 — 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!
|
||||||
|
|||||||
@ -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");
|
||||||
|
|||||||
@ -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.
|
|
||||||
|
|||||||
@ -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 — 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 — 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 — 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;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// This file was generated automatically
|
// This file was generated automatically.
|
||||||
|
|
||||||
// Iteration
|
// Iteration
|
||||||
//
|
//
|
||||||
@ -28,7 +28,8 @@ 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() << " ";
|
||||||
@ -38,5 +39,6 @@ int main()
|
|||||||
// std::cout << name << " ";
|
// std::cout << name << " ";
|
||||||
// std::cout << std::endl;
|
// std::cout << std::endl;
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 — try it!
|
// give you a warning - try it!
|
||||||
|
|
||||||
|
|
||||||
std::cout << n << std::endl;
|
std::cout << n << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 — 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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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
|
|
||||||
@:
|
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
|
Invalid
|
||||||
Red
|
Red
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
0000000000000010100001
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user