commit 5cfa84f1b8b1065887a677d50a940c92cfbadf30 Author: Anton Bachin Date: Mon Oct 19 09:11:42 2020 +0300 Project site diff --git a/ApiReference.html b/ApiReference.html new file mode 100644 index 0000000..798c379 --- /dev/null +++ b/ApiReference.html @@ -0,0 +1,343 @@ + + + + + + + +API reference - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +

API reference

+

Contents

+

Overview

+

The declaration

+
#include <enum.h>
+BETTER_ENUM(Enum, underlying_type, A, B, C, ...)

generates a new class type Enum which is notionally similar to the type +created by this C++11 declaration:

+
enum class Enum : underlying_type {A, B, C, ...};

That is, Enum is a scoped enumerated type with constants Enum::A, Enum::B, +Enum::C, and so on, with memory representation the same as underlying_type. +It is possible to supply initializers for any of the constants:

+
BETTER_ENUM(Enum, underlying_type, A = 1, B = constant_expression, C = A, ...)

The initializers have the same meaning and constraints as in a built-in enum +or enum class declaration.

+
+

The principal differences between the types declared by the BETTER_ENUM macro +and enum class are:

+
    +
  • BETTER_ENUM is available for C++98 +compilers supporting __VA_ARGS__ — +all major compilers — while enum class is restricted to C++11,
  • +
  • the BETTER_ENUM type is implicitly convertible to integral types, though +this can be disabled when +using C++11, and
  • +
  • the BETTER_ENUM type supports a set of reflective operations, detailed in +the rest of this reference.
  • +
+
+

The types produced by the BETTER_ENUM macro are called Better Enums in the +rest of this reference.

+

Better Enums are similar to their underlying type for the purposes of argument +passing. This means that they typically fit into a machine word, and should be +passed by value.

+

All names declared in the scope of a Better Enum are prefixed with an underscore +in order to avoid conflicts with potential constant names.

+

Running example

+

The rest of this reference uses the following declaration as a running example:

+
BETTER_ENUM(Enum, 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 C++ in a reasonable fashion, or else they are referenced in the +rest of the documentation.

+

typedef _enumerated

+

An internal type used to declare constants. The BETTER_ENUM macro generates +something similar to

+
struct Enum {
+    enum _enumerated : int {A, B, C};
+    // ...
+};

The user needs to be aware of _enumerated in only one situation. A literal +constant such as Enum::A is an expression of type Enum::_enumerated, not +Enum. It is not possible to directly call a method on the constant, as in +Enum::A._to_string(). This problem is addressed by operator + +below.

+

non-member constexpr Enum unary operator +(_enumerated)

+

Forces promotion of Enum::_enumerated to Enum. +Provided to solve the problem described above. So:

+
// Does not compile
+Enum::A._to_string()
+
+// Compiles
+(+Enum::A)._to_string()

constexpr implicit constructor Enum(_enumerated)

+

A constructor that performs implicit conversions of +Enum::_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 do_something(Enum value);
+
+do_something(+Enum::A);  // Not necessary
+do_something(Enum::A);   // Implicit conversion available
+
+Enum value = Enum::A;    // Implicit conversion

The other constructors of Enum are the implicitly-generated copy and move +constructors. There is no default constructor. If you have comments on what a +default constructor should do, please let me know.

+

non-member struct better_enums::optional<Enum>

+

An optional Enum value. These are returned by the various _nothrow +functions, such as _from_string_nothrow. This type is +meant to represent the possibility of failure. For example, suppose you have:

+
better_enums::optional<Enum>    maybe = _from_string_nothrow("A");

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

+
if (maybe) {
+    // The string conversion succeeded
+    do_something(*maybe);
+}
+else {
+    // 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

+
using optional = better_enums::optional<Enum>;

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.

+

static constexpr size_t _size()

+

The number of constants declared. Enum::_size() == 3.

+

static constexpr const size_t _size_constant

+

Same as _size, but a constant instead of a function. This is +provided for use in C++98 constant expressions.

+

typedef _value_iterable

+

Type of object that permits iteration over the constants. Has at least +constexpr begin(), end(), and size() methods, and constexpr +operator[]. Iteration visits each declared constant, even if multiple +constants have the same value, and visits them in order of declaration. See +usage examples under _values.

+

typedef _value_iterator

+

Random-access iterator type for _value_iterable. Most +operations, including dereferencing, are constexpr. The exceptions are +mutating operators such as operator++. In constexpr code, that can be +replaced with addition of 1. You typically don't have to refer to this type +directly.

+

static constexpr _value_iterable _values()

+

constexpr access to the sequence of declared constants. For example:

+
for (size_t index = 0; index < Enum::_values().size(); ++index)
+    do_something(Enum::_values()[index]);

or, using iterators:

+
for (Enum::_value_iterator iterator = Enum::_values().begin();
+     iterator != Enum::_values().end(); ++iterator) {
+
+    do_something(*iterator);
+}

or, in C++11:

+
for (Enum value : Enum::_values())
+    do_something(value);

String conversion and iteration

+

member constexpr? const char* _to_string() const

+

Returns the string representation a Better Enum value. For example:

+
Enum    value = Enum::A;
+value._to_string();     // Same as "A".

If two or more constants have the same numeric value, it is undefined which name +_to_string will choose, but it will choose one of them.

+

If value is not equal to the representation of any declared constant, for +example if it was obtained using an unchecked conversion such as

+
Enum    value = Enum::_from_integral_unchecked(0xbadc0de);

then the behavior of value._to_string is undefined.

+

Running time is linear in the number of declared constants.

+

This method is not constexpr by default. Read +here for information +about making it constexpr.

+

static constexpr Enum _from_string(const char*)

+

If the given string is the exact name of a declared constant, returns the +constant. Otherwise, throws std::runtime_error. Running time is linear in the +number of declared constants multiplied by the length of the longest constant.

+

static constexpr optional<Enum> _from_string_nothrow(const char*)

+

Same as _from_string, but does not throw an exception on +failure. Returns an optional value instead.

+

static constexpr Enum _from_string_nocase(const char*)

+

Same as _from_string, but comparison is up to case, in the +usual sense in the Latin-1 encoding.

+

static constexpr optional<Enum> _from_string_nocase_nothrow(const char*)

+

Is to _from_string_nocase as +_from_string_nothrow is to +_from_string.

+

static constexpr bool _is_valid(const char*)

+

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.

+

static constexpr bool _is_valid_nocase(const char*)

+

The same as _is_valid, but comparison is done up to +case as in _from_string_nocase.

+

static constexpr const char* _name()

+

Evaluates to the name of the Better Enum type. Enum::_name() is the same +string as "Enum".

+

typedef _name_iterable

+

Type of object that permits iteration over names of declared constants. Has at +least constexpr begin(), end(), and size() methods. operator[] is also +available, but is constexpr if and only if _to_string is +constexpr. Iteration visits constants in order of declaration. See usage +example under _names.

+

typedef _name_iterator

+

Random-access iterator type for _name_iterable. Most operations are +constexpr, but dereferencing is constexpr if and only if +_to_string is constexpr. Mutating operators such as +operator++ are not constexpr due to their nature — adding 1 is a +constexpr alternative. You typically don't have to refer to this type +directly.

+

static constexpr? _name_iterable _names()

+

Access to the sequence of declared constant names. For example:

+
for (size_t index = 0; index < Enum::_names().size(); ++index)
+    std::cout << Enum::_names()[index] << std::endl;

or, using iterators:

+
for (Enum::_name_iterator iterator = Enum::_names().begin();
+     iterator != Enum::_names().end(); ++iterator) {
+
+    std::cout << *iterator << std::endl;
+}

or, in C++11:

+
for (const char *name : Enum::_names())
+    std::cout << name << std::endl;

constexpr if and only if _to_string is constexpr.

+

Integer conversion

+

Better Enums are already represented as integers at run time. Values of the +running example 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.

+

typedef _integral

+

The underlying or representation type of the Better Enum. For example, +Enum::_integral is the same type as int. Each Better Enum has the same size +and alignment requirement as its representation type.

+

member constexpr _integral _to_integral() const

+

No-op conversion of a Better Enum to a value of its representation type. For +example,

+
(+Enum::C)._to_integral() == 2

Note that Better Enums are already implicitly convertible to their underlying +integral types by default. +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 _from_integral(_integral)

+

Checked conversion of an integer to a Better Enum value. The check runs in time +linear in the number of declared constants, but the conversion itself is a +no-op. Throws std::runtime_error if the given integer is not the numeric value +of one of the declared constants.

+
Enum::_from_integral(2);    // Enum::C
+Enum::_from_integral(42);   // std::runtime_error

static constexpr optional<Enum> _from_integral_nothrow(_integral)

+

Checked conversion as _from_integral, but does not throw an +exception on failure. Returns an optional value +instead.

+

static constexpr Enum _from_integral_unchecked(_integral)

+

No-op unchecked conversion of an integer to a Better Enum value. If the given +integer is not the numeric value of one of the declared constants, the behavior +of all subsequent operations on the Better Enum value is undefined.

+

This is the direct inverse of _to_integral. Here are no-op +round trips between int and Enum:

+
Enum::_from_integral_unchecked(value._to_integral());
+Enum::_from_integral_unchecked(integer)._to_integral();

You should not use this function on untrusted input, however.

+

static constexpr bool _is_valid(_integral)

+

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.

+

Index lookup

+

member constexpr std::size_t _to_index() const

+

Returns the index of a Better Enum value within its enum declaration. The index +is determined from the value only; if two constants in the declaration have the +same value, this function may return the index of either constant.

+

If the value does not correspond to any constant in the declaration (for +example, if it was obtained using an unchecked conversion or a cast), then the +behavior of value._to_index is undefined.

+

static constexpr Enum _from_index(size_t)

+

Returns the value of the constant with the given index. Throws +std::runtime_error if not given the index of one of the constants.

+

static constexpr Enum _from_index_unchecked(size_t)

+

Returns the value of the constant with the given index. If not given one of the +constants in the declaration of the enum, the returned value is undefined.

+

static constexpr optional<Enum> _from_index_nothrow(size_t)

+

Returns the value of the constant with the given index.

+

Stream operators

+

non-member std::ostream& operator <<(std::ostream&, const Enum&)

+

Formats the given enum to the given stream in the same way as +_to_string.

+

non-member std::istream& operator >>(std::istream&, Enum&)

+

Reads from the given stream and attempts to parse an enum value in the same way +as _from_string. In case of failure, sets the stream's +failbit.

+

Hashing

+

macro BETTER_ENUMS_DECLARE_STD_HASH(Enum)

+

Use this outside namespace scope to declare a specialization of std::hash for +the type Enum. For example:

+
// This declaration might be inside a namespace.
+BETTER_ENUM(Channel, int, Red, Green, Blue)
+
+// Later, outside the namespace:
+BETTER_ENUMS_DECLARE_STD_HASH(Channel)
+ +
+
+ + + + + + diff --git a/CompilerSupport.html b/CompilerSupport.html new file mode 100644 index 0000000..5fb4913 --- /dev/null +++ b/CompilerSupport.html @@ -0,0 +1,205 @@ + + + + + + + +Compiler support - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +

Compiler support

+

Better Enums aims to support all major compilers. It is known to work on:

+
    +
  • clang 3.3 to 3.9
  • +
  • gcc 4.3 to 5.3
  • +
  • Visual C++ 2008 to 2015.
  • +
+

The library can be used with any compiler that supports either C++11, or C++98 +with the __VA_ARGS__ extension. This includes every version of gcc and clang I +have ever heard of, and Visual C++ down to 2005.

+

To ensure that nothing is broken, every release of Better Enums is +tested 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.

+

Compile-time reflection configurations

+

Read this section if:

+
    +
  • you want to use Better Enums reflection at compile time, and need to know +exactly what features are supported on your compiler, or
  • +
  • Better Enums is choosing the wrong configuration automatically and you need +to force a different choice.
  • +
+

All features of Better Enums are always available for run-time use. However, for +compile-time use, Better Enums has two main configurations: C++98 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 "full" (slow) +constexpr mode. The three modes can be ranked, with each next mode including +all the features of the preceding ones:

+
    +
  • C++98
  • +
  • fast constexpr
  • +
  • full constexpr
  • +
+

Only _size is supported at compile time in C++98 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 +C++98 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 C++98 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 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 C++98 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. I will fix it and you won't have to +define these macros anymore.

+

Tested configurations

+
vc2015 /EHsc
+vc2015 /EHsc /DBETTER_ENUMS_STRICT_CONVERSION
+vc2013 /EHsc
+vc2013 /EHsc /DBETTER_ENUMS_STRICT_CONVERSION
+vc2012 /EHsc
+vc2010 /EHsc
+vc2008 /EHsc
+clang++39 -std=c++11
+clang++39 -std=c++11 -DBETTER_ENUMS_STRICT_CONVERSION
+clang++39 -std=c++11 -DBETTER_ENUMS_CONSTEXPR_TO_STRING
+clang++39 -std=c++98
+clang++38 -std=c++11
+clang++38 -std=c++11 -DBETTER_ENUMS_STRICT_CONVERSION
+clang++38 -std=c++11 -DBETTER_ENUMS_CONSTEXPR_TO_STRING
+clang++38 -std=c++98
+clang++37 -std=c++11
+clang++37 -std=c++11 -DBETTER_ENUMS_STRICT_CONVERSION
+clang++37 -std=c++11 -DBETTER_ENUMS_CONSTEXPR_TO_STRING
+clang++37 -std=c++98
+clang++36 -std=c++11
+clang++36 -std=c++11 -DBETTER_ENUMS_STRICT_CONVERSION
+clang++36 -std=c++11 -DBETTER_ENUMS_CONSTEXPR_TO_STRING
+clang++36 -std=c++98
+clang++35 -std=c++11
+clang++35 -std=c++11 -DBETTER_ENUMS_STRICT_CONVERSION
+clang++35 -std=c++11 -DBETTER_ENUMS_CONSTEXPR_TO_STRING
+clang++35 -std=c++98
+clang++34 -std=c++11
+clang++34 -std=c++11 -DBETTER_ENUMS_STRICT_CONVERSION
+clang++34 -std=c++11 -DBETTER_ENUMS_CONSTEXPR_TO_STRING
+clang++34 -std=c++98
+clang++33 -std=c++98
+g++53 -std=c++11
+g++53 -std=c++11 -DBETTER_ENUMS_STRICT_CONVERSION
+g++53 -std=c++11 -DBETTER_ENUMS_CONSTEXPR_TO_STRING
+g++53 -std=c++98
+g++49 -std=c++11
+g++49 -std=c++11 -DBETTER_ENUMS_STRICT_CONVERSION
+g++49 -std=c++11 -DBETTER_ENUMS_CONSTEXPR_TO_STRING
+g++49 -std=c++98
+g++48 -std=c++11
+g++48 -std=c++11 -DBETTER_ENUMS_STRICT_CONVERSION
+g++48 -std=c++11 -DBETTER_ENUMS_CONSTEXPR_TO_STRING
+g++48 -std=c++98
+g++47 -std=c++11
+g++47 -std=c++11 -DBETTER_ENUMS_STRICT_CONVERSION
+g++47 -std=c++11 -DBETTER_ENUMS_CONSTEXPR_TO_STRING
+g++47 -std=c++98
+g++46 -std=c++98
+g++45 -std=c++98
+g++44 -std=c++98
+g++43 -std=c++98
+ +
+
+ + + + + + diff --git a/Contact.html b/Contact.html new file mode 100644 index 0000000..e5e7233 --- /dev/null +++ b/Contact.html @@ -0,0 +1,103 @@ + + + + + + + +Contact - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +

Contact

+ +

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. I'd also be +interested to hear about your use case, if you are willing to share :)

+

And, if you find this library helpful, give it a star on GitHub to let me know +you use it :)

+ + +
+
+ + + + + + diff --git a/DesignDecisionsFAQ.html b/DesignDecisionsFAQ.html new file mode 100644 index 0000000..b53226f --- /dev/null +++ b/DesignDecisionsFAQ.html @@ -0,0 +1,301 @@ + + + + + + + +Design decisions FAQ - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +

Design decisions FAQ

+

Better Enums pushes at the edges of what is possible in standard C++, and I've had to +make some difficult decisions as a result. You can imagine the set of +potential reflective enum implementations as a space, with axes such as "concise +syntax," "uniform interface," "compilation speed," "run-time performance," and +so on. As is typical in engineering, the constraints are such that as you move +to extremes along one axis, you have to retreat along others — for +example, some desirable aspects of concise syntax conflict with having a uniform +interface, which is nonetheless good for teachability, and compile-time +performance is, in some ways, at odds with run-time performance.

+

So, there are many variations possible on Better Enums, and, in fact, I have tried and +maintained a few. This page describes how I chose the one that is published. +The choices are debatable, but I am attempting to record the debate. Hopefully, +this will either convince you that I have made a good choice, or, if you +disagree, you will have a good starting point for discussion, or even for +implementing an alternative.

+

I am always looking for new arguments and approaches. If you have an idea, +comment, criticism, please do let me know.

+

Contents

+

Why do enum members have underscores?

+

Enum members such as _to_string occupy the same scope as the names of +constants declared by the user. I chose to prefix members with underscores to +lessen the chances of collision. For example, take _valid, and suppose it was +valid instead. That would make this enum impossible:

+
BETTER_ENUM(Status, char, valid, invalid)

because the constant Status::valid would clash with the member +Status::valid.

+

Of course, users could try to declare constants with underscores as well, but I +find it easier to ask users not to do that, rather than ask them to worry about +a potentially growing set of reserved names, which they wouldn't fully know even +for a single version of Better Enums, without frequently looking at the API +documentation.

+

Alternatives to this involve separating the namespaces occupied by members and +constants. I don't think increasing nesting is an option, since nobody wants to +write Status::values::valid or Status::operations::valid. I don't think +moving constants out of Status is a good idea, since scoped constants is a +feature of Better Enums, which is especially important for C++98 usage.

+

This leaves the possibility of moving the operations performed by the members +into traits types, i.e. something like traits<Status>::valid. That is an +interesting option, and it has its own section. I have tried it, but +the verbosity increase is much greater than the benefit of dropping underscores, +so I chose not to do it.

+

Why does Better Enums use a macro at all?

+

Better Enums needs to turn the names of declared constants into strings, and I +don't believe there is any way to do this in standard C++ except by using the +preprocessor's macro parameter stringization operator (#). So, at the top +level, Better Enums has to provide a macro. I am, however, trying to keep the +user-facing macros to a minimum; in particular there is only one.

+

I think that's the best that is possible. Furthermore, apart from the macro +itself, the declaration looks very similar to a C++11 enum declaration, with +an underlying type, comma-separated constant list, and the same support for +initializers as built-in enums. So, I am trying to keep even this one macro out +of the user's way. I wouldn't accept any change that involved turning the +declaration into a preprocessor sequence or tuple, i.e. something like

+
BETTER_ENUM(Channel, int, (Red)(Green)((Blue)(5)))

even if it promised extra capabilities.

+

Better Enums uses additional macros internally, for two main purposes: to do the +actual work of stringizing the declared constants and assembling them into +arrays, and to configure itself by detecting which compiler it is running on.

+

I am not a fan of gratuitous macros, but in these cases they are unavoidable, +and, indeed, I am grateful for the stringization operator.

+

+

Why is it not possible to declare a Better Enum inside a class?

+

This is due to an interaction between linkage and constexpr.

+
    +
  1. Better Enums is a header-only library that declares arrays with static +storage, such as the array of constant names for each declared enum. Such +arrays can be declared in namespace scope, in class scope, or in function +scope, but they also need to be defined somewhere.

    +

    If BETTER_ENUM is to be usable in both namespace and class scope, it +already can't assume that it can declare arrays in namespace scope, since if +BETTER_ENUM is used in a class, its entire expansion will be enclosed in +the declaration of that class.

    +

    That leaves class scope and function scope. If the arrays are declared in +class scope, there needs to be a separate definition of them in some +translation unit. This is too burdensome for the user, because the separate +definition would involve repetition of the constants in the macro parameter +list, creating exactly the type of maintenance problem that Better Enums is +trying to eliminate.

    +

    Function scope is the only viable option remaining after considering linkage +constraints. Each array can be wrapped in a static function, which has a +static local variable, which is initialized with the array. The functions can +be called to get references to the arrays.

    +

    However....

    +
  2. +
  3. These arrays need to be accessible to constexpr code in C++11, and +constexpr functions are not allowed to have static local variables.

    +
  4. +
+

Ironically, this seems like one place where C++98 is more "flexible," but only +for the reason that compile-time usage of Better Enums is not supported in +C++98.

+

+

Should Better Enums provide enum "objects" or traits types?

+

A Better Enum value is an "object," whose memory representation is the same as +its underlying type. For example,

+
BETTER_ENUM(Channel, int, Red, Green, Blue)

expands to something like

+
struct Channel {
+    enum _enumerated : int { Red, Green, Blue };
+    int _value;
+    // Strings array, _to_string, _from_string, etc.
+};

+

There is an alternative interpretation, in which the Better Enums library +generates enum traits instead, i.e. the generated arrays and members sit +alongside built-in enums instead of wrapping them:

+
BETTER_ENUM(Channel, int, Red, Green, Blue)

generates

+
enum class Channel : int { Red, Green, Blue };
+
+template <>
+struct ::better_enums::traits<Channel> {
+    using _enumerated = Channel;
+    // Strings array, to_string, from_string, etc.
+};

+

There are a number of advantages to the traits approach.

+
    +
  • The underscore prefixes can be dropped from member names, since they no longer +share a namespace with user-declared constants.
  • +
  • The interface, at first glance, becomes more uniform, since now every member +is a static member of the traits type. Without traits, _to_string is a +non-static member, while _from_string is a static member.
  • +
  • Channel is one of the language's own enum types, instead of some mystery +type provided by Better Enums. This may make it easier to understand. It also +eliminates the problem with different syntaxes for switch statements +described here.
  • +
+

However, it also introduces some difficulties.

+
    +
  • The syntax is more verbose, since everything becomes a member of the traits +type. For example, instead of Channel::_from_string(), you get +better_enums::traits<Channel>::from_string(). The underscore may be +unpleasant, but so far I have preferred the underscore to boilerplate.
  • +
  • The uniform interface ends up getting wrapped behind functions anyway, for the +sake of type inference. For example, the "naked" to_string function is +called as better_enums::traits<Channel>::to_string(channel), which is +redundant, because the compiler could infer the type parameter Channel if it +was the parameter of the function instead of the traits type. So, the obvious +thing is to define such a wrapper function, which can then be called as +better_enums::to_string(channel). No such function can be easily defined for +from_string and other from_* functions, however, because the type +parameters can't be inferred from arguments. So, the signatures of to_* and +from_* functions again effectively diverge, negating this advantage of +traits. The closest I can get with wrappers is +better_enums::from_string<Channel>, which has the same signature only in the +formal sense, i.e. modulo the difference in type inference.

    +

    I actually think there is a way to infer the type parameter from the return +type, similar to how it is done here, but that will not be suitable +for all contexts, and the user may be surprised by ambiguous resolution error +messages when it is not.

    +
  • +
  • Scoped constants are lost for C++98 unless Better Enums again wraps them in a +generated type, though it will be more lightweight than a full Better Enum of +the non-traits approach.
  • +
  • Traits types must be specialized in either the same namespace scope they are +declared in, or in an enclosing scope. This makes it impossible to declare an +enum and specialize traits for it in a user's custom namespace.
  • +
+

Despite the disadvantages listed just above, I consider the traits approach +interesting — it's a close call. There is an +out-of-date branch containing a traits version of Better Enums. +You can see some of the usage in its samples directory. I may +update it from time to time, especially if there is interest.

+

Why does Better Enums use linear scans for lookup?

+

It seems that Better Enums needs to use the same algorithms at compile time as +at run time, because I have not found a way (and doubt there is one) to +determine, during the execution of a constexpr function, whether it is +executing at compile time or at run time. So, whatever data structures I use to +accelerate lookup, I have to generate them at compile time, to be available as +early as possible.

+

I tried to generate various data structures at compile time, but so far, +generation has been too slow. The fastest algorithm so far, a compile-time merge +sort based on template parameter packs, took over 100ms to run on the constant +set of a large enum. I would have to run three of these per enum — for the +constants, for the names, and for the names with case-insensitive comparison. +This results in a 300ms slowdown per enum, which is not acceptable, given that +on my system the same compiler takes 370ms to process iostream, and less than +10ms to process an enum without acceleration data structures. Declaring five +large enums with accelerated lookup would take 1.5 seconds of compilation time. +This doesn't scale to large projects with many translation units.

+

I am continuing to look for faster algorithms or better approaches, so faster +lookup may be coming to Better Enums in the future.

+

So far, I have tried Boost.MPL sort, Eric Niebler's Meta sort, my own selection +sort based on constexpr, and an insertion and merge sort based on parameter +packs. I cannot use (Boost?).Hana sort, because that requires C++14. I am also +considering various hash table-like data structures, and providing two sets of +interfaces for compile-time and run-time usage, which is something I would +really rather not have to do. The latter option would be worth considering, +however, if I measured a significant improvement in running time from better +data structures — something I haven't gotten to yet because there doesn't +seem to be a data structure to measure that is not disqualified by the speed of +generation.

+

Why not use indices for the representation?

+

Representing Better Enum values by their indices in the declaration list seems +like a tempting solution for the problem of having multiple constants with the +same numeric value. It also speeds up some operations. For example, if a Better +Enum is simply an index, then getting its string representation is simply +indexing an array, rather than some kind of data structure lookup.

+
// Representations 0, 1, 2, 3 instead of 1, 2, 3, 1.
+BETTER_ENUM(Kind, int, A = 1, B, C, D = A)

Choosing this approach has serious drawbacks.

+
    +
  • The data structure lookup has simply been moved to another place. It now takes +time to convert from a literal Kind::D to a Better Enum.
  • +
  • It is still impossible to disambiguate between the literals Kind::D and +Kind::A (1 and 1). Only the Better Enums objects of type Kind that +represent D and A are different from each other (0 and 3). This is not +only a technical problem, but is also quite unintuitive.
  • +
  • Treating a Better Enum represented by an index as untyped memory produces +surprising results. This makes Better Enums much less useful with functions +such as fwrite. Worse, Better Enums become sensitive to declaration order +even when initializers are given explicitly. Using indices for the +representation makes it difficult to maintain compatibility with external +protocols and file formats.
  • +
+ + +
+
+ + + + + + diff --git a/ExtendingLimits.html b/ExtendingLimits.html new file mode 100644 index 0000000..21cf976 --- /dev/null +++ b/ExtendingLimits.html @@ -0,0 +1,148 @@ + + + + + + + +Extending limits - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +

Extending limits

+

The BETTER_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 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. +
  3. Get make_macros.py from your copy of the full Better Enums distribution +or from GitHub.
  4. +
  5. 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.
  6. +
  7. Run python make_macros.py 512 64 > common/enum_macros.h.
  8. +
  9. 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>'
    • +
    • With CMake, you may need something like +add_definitions(-DBETTER_ENUMS_MACRO_FILE="${CMAKE_SOURCE_DIR}/src/enum-macros.h")
    • +
    +

    You can also create a new header file that defines this macro, and then +includes enum.h. Then, include your new file everywhere where you would +otherwise include enum.h:

    +
    #pragma once
    +
    +#define BETTER_ENUMS_MACRO_FILE <common/enum_macros.h>
    +#include <enum.h>
  10. +
  11. Enjoy the looser limits. Just watch out — increasing the second +number can really slow down compilation of full-constexpr enums.

    +
  12. +
  13. You don't need make_macros.py anymore. It's not part of your build +process and you can delete it.
  14. +
+
+

I am paying attention to feedback, so if more than a few users say that the +default limit of 64 constants is too low, I will increase it to simplify +everyone's command line. The current choice of 64 is basically an arbitrary +guess, loosely informed by the following two facts about macro parameter limits:

+
    +
  • The default limit in Boost.Preprocessor is 64. Though Better Enums does not +use Boost, I took this number as a guideline.
  • +
  • The next power of two, 128, is more than the number Visual C++ supports +(127).
  • +
+ + +
+
+ + + + + + diff --git a/GeneralUnderlyingTypes.html b/GeneralUnderlyingTypes.html new file mode 100644 index 0000000..4047276 --- /dev/null +++ b/GeneralUnderlyingTypes.html @@ -0,0 +1,183 @@ + + + + + + + +General underlying types - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.10.1

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ + + +
+
+ + +

General underlying types

+

The underlying type of a Better Enum doesn't have to be an integral type. It can +be any literal type T, as long as you provide a constexpr two-way mapping +between T and an integral type of your choosing. It also works in C++98, +though, of course, T doesn't have to be literal and the mapping doesn't have +to be constexpr — everything will be done by Better Enums at run time.

+

Doing this enables the following usage:

+
// The type. A color triplet.
+struct html_color {
+    uint8_t     r, g, b;
+
+    constexpr html_color(uint8_t _r, uint8_t g, uint8_t b) :
+        r(_r), g(_g), b(_b) { }
+};
+
+
+
+// The enum.
+ENUM(Color, html_color,
+     darksalmon = 0xc47451, purplemimosa = 0x9e7bff, slimegreen = 0xbce954)
+
+
+
+// The usage.
+Color   c = Color::darksalmon;
+
+std::cout << "Red component: " << c->r << std::endl;
+
+switch (c) {
+    case Color::darksalmon:   // ...
+    case Color::purplemimosa: // ...
+    case Color::slimegreen:   // ...
+}

As you can see, you can have an enumerated set of any literal type, and safely +use the values in switch, with the compiler checking exhaustiveness. You can +also access the type's members using the enum->underlying_member syntax.

+

You do have to supply the mapping to an integral type, however. One option is:

+
// The mapping. It just stuffs bits.
+template <>
+struct ::better_enums::underlying_traits<html_color> {
+    using integral_representation = unsigned int;
+
+    constexpr static html_color from_integral(unsigned int i)
+        { return html_color(i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff); }
+
+    constexpr static unsigned int to_integral(html_color c)
+        { return (unsigned int)c.r << 16 | (unsigned int)c.g << 8 | c.b; }
+};

Using constructors in initializers

+

The declaration above used only numeric initializers. It is possible to use the +type's own constructors, provided the type has a constexpr conversion to your +chosen integral type:

+
// The type.
+struct html_color {
+    uint8_t     r, g, b;
+
+    constexpr html_color(uint8_t _r, uint8_t g, uint8_t b) :
+        r(_r), g(_g), b(_b) { }
+
+    // This is new:
+    constexpr operator unsigned int() const
+        { return (unsigned int)r << 16 | (unsigned int)g << 8 | b; }
+};
+
+// The enum.
+ENUM(Color, html_color,
+     darksalmon = 0xc47451, purplemimosa = 0x9e7bff, slimegreen = 0xbce954,
+     celeste = html_color(0x50, 0xeb, 0xec))

This is not possible at all in C++98, however.

+

Letting the compiler enumerate your literal type

+

You don't have to use initializers. For example, as long as your example type +file_descriptor knows how to deal with the values, you can have the compiler +generate them in sequence:

+
ENUM(FD, file_descriptor, STDIN, STDOUT, STDERR, SomePipeYourDaemonHas, ...)

SAMPLE

+

You can see the code "in action" in the test case. Be aware that it's +not very "nice," because it uses conditional compilation to run under multiple +compilers. I haven't written a clean sample or documentation yet simply because +this feature is in a very early stage of development.

+

Discussion

+

This feature is still semi-experimental, though I expect it to remain stable, +except perhaps that I will make it possible to infer the type +integral_representation.

+

Any opinions are welcome.

+
    +
  • The main reason Better Enums needs you to supply and explicit mapping is +because it can't just get the "bits" of objects of underlying type in +constexpr code. Both reinterpret_cast and union abuse seem to be forbidden +in constexpr functions.
  • +
  • There is currently no way to have two different integral representaitons for +the same underlying type in different enums. I don't think that's a major use +case at this point, however.
  • +
+ + +
+
+ + + + + + diff --git a/OptInFeatures.html b/OptInFeatures.html new file mode 100644 index 0000000..ba2577b --- /dev/null +++ b/OptInFeatures.html @@ -0,0 +1,165 @@ + + + + + + + +Opt-in features - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +

Opt-in features

+

Better Enums has two opt-in features. They are both "good," but they either hurt +compilation time or break compatibility with C++98, so they are disabled by +default. Read this page if you want to enable them.

+

Contents

+

Default constructors

+

Better Enums generate with inaccessible (private or deleted) default +constructors. This is meant to help control where in your program Better Enums +values are introduced, and thus ensure that only valid, properly initialized +enum values are ever created.

+

If you want Better Enums to have a default constructor, you can do something +like the following:

+
#define BETTER_ENUMS_DEFAULT_CONSTRUCTOR(Enum) \
+  public:                                      \
+    Enum() = default;
+
+#include <enum.h>

You can put this in its own header file, and include that header file instead of +including enum.h directly.

+

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 BETTER_ENUM and deprecate it, so your code will still +work.

+

Strict conversions

+

This disables implicit conversions to underlying integral types. At the moment, +you can only enable this globally for all enums, by defining +BETTER_ENUMS_STRICT_CONVERSION before including enum.h.

+

The reason Better Enums have implicit conversions to integral types in the first +place is that in C++98, Better Enums have to be implicitly convertible to their +member _enumerated types to +be usable in switch statements. It is +possible to avoid this in C++11 and convert to enum class types instead, but +at the cost of breaking interface compatibility with C++98.

+
    +
  • The "weaker" incompatibility is that you could write a bunch of C++98 code +that relies on implicit integer conversions, and then try to switch to +C++11. The code would then fail to compile. An example where implicit +conversions are an "advantage" is when using Better Enums as arguments to +the methods of std::bitset. I could have ignored this problem by declaring +usage of implicit integer conversions unsupported, but in light of the next +issue, I decided not to do that.
  • +
  • The "stronger" incompatibility is a difference in how switch cases must be +written. The syntaxes for the two variants, implicitly-converting and +strict, are mutually exclusive. They differ by a + character.
  • +
+

Here they are:

+
// Default variant
+switch (channel) {
+    case Channel::Red:   break;
+    case Channel::Green: break;
+    case Channel::Blue:  break;
+}
+
+// Strict variant
+switch (channel) {
+    case +Channel::Red:   break;
+    case +Channel::Green: break;
+    case +Channel::Blue:  break;
+}

Injecting tokens

+

You can define BETTER_ENUMS_CLASS_ATTRIBUTE before declaring a Better Enum, to +inject tokens into the class declaration. For example, in

+
#define BETTER_ENUMS_CLASS_ATTRIBUTE __attribute__(foo)
+BETTER_ENUM(Channel, int, Red, Green, Blue)

The resulting enum declaration will begin with

+
class __attribute__(foo) Channel {
+ +
+
+ + + + + + diff --git a/Performance.html b/Performance.html new file mode 100644 index 0000000..989f1e6 --- /dev/null +++ b/Performance.html @@ -0,0 +1,134 @@ + + + + + + + +Performance - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +

Performance

+

A basic performance test is run on +every compiler tested. 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 includes +enum.h and declares 647 constants across 36 Better Enums.
  • +
  • The other file 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 +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, C++98: 1.18
  • +
+

The time to merely include enum.h vary widely by compiler, with clang being +by far the fastest. The ratios to iostream are given below.

+
    +
  • clang 3.6: 0.15
  • +
  • gcc 5.1: 0.77
  • +
  • VC2015RC: 0.82
  • +
+

On my test machines, clang processed the file in 40ms, gcc took 230ms, and +VC2015 took 820ms. The first two are comparable to each other, but VC2015 runs +on a different machine.

+
+

In general, I am very sensitive to performance. Better Enums was originally +developed in the context of a commercial project where slow running times and +slow compilation times were unacceptable. I am continuing to develop it in this +spirit.

+ + +
+
+ + + + + + diff --git a/better-enums.css b/better-enums.css new file mode 100644 index 0000000..f1aff08 --- /dev/null +++ b/better-enums.css @@ -0,0 +1,595 @@ +body { + margin: 0; + color: #333; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 18px; + font-weight: 300; +} + +pre, code, samp, h4, .contents ul { + font-family: + Consolas, "Liberation Mono", Menlo, "Courier New", Courier, monospace; +} + +h1, .splash-text .self, nav .self { + font-family: Georgia, Times, "Times New Roman", serif; +} + +pre { + background-color: #477093; + padding: 1.5em 20px; + border-radius: 5px; + overflow: auto; + color: rgba(255, 255, 255, 0.6); + font-size: 78%; + max-width: 84ex; + margin-left: 1em; +} + +pre em { + font-style: normal; + text-shadow: 0 -1px grey; + color: white; +} + +pre.comment { + background-color: #EEF4F9; + color: #888; + border: 1px dashed #477093; +} + +pre.comment em { + color: #333; + text-shadow: none; +} + +code, samp { + background-color: #EEF4F9; + padding: 1px 3px; + border-radius: 3px; + font-size: 78%; +} + +.container { + margin-left: 150px; + margin-right: 150px; +} + +.main .container > * { + max-width: 41em; +} + +.index .main .container > * { + max-width: none; +} + +.main .container > .contents { + max-width: none; +} + +@media (max-width: 1400px) { + .container { + margin-left: auto; + margin-right: auto; + width: 1100px; + } +} + +@media (max-width: 1120px) { + .container { + margin-left: 10px; + margin-right: 10px; + width: auto; + } +} + +nav { + position: fixed; + width: 100%; + z-index: 1; + + border-bottom: 1px solid #68a; + color: white; + opacity: 0.95; +} + +nav, .spacer { + padding: 0.75em 0; + font-size: 14px; + background-color: #222; +} + +nav > * > span { + float: right; + opacity: 0.9; + margin-right: 1em; +} + +nav .self { + font-size: 16px; + padding-right: 0.5em; +} + +nav a { + margin-right: 2em; +} + +nav a.first { + font-weight: 600; + font-size: 16px; +} + +@media (max-width: 560px) { + nav { + position: initial; + } + + .spacer { + display: none; + } +} + +header { + background-color: #4C6F8C; + background: linear-gradient(#395E7E, #4A79A0); + color: white; + padding: 50px 0; +} + +h1 { + margin: 0; + font-size: 60px; + font-weight: normal; + text-shadow: -2px 2px rgba(0, 0, 0, 0.3); +} + +header section { + float: left; +} + +header section.buttons, header section.notes { + float: right; + margin-top: 1.75em; + margin-left: 20px; +} + +header section.notes { + max-width: 20em; + font-size: 75%; + margin-right: 10px; + opacity: 0.7; +} + +header section.notes p { + margin: 0.35em 0.35em; +} + +header section.notes code { + background-color: transparent; +} + +header h2 { + margin: 0; + font-size: 24px; + font-weight: 100; + position: relative; + left: 3px; +} + +header h3 { + margin: 0; + font-size: 14px; + font-weight: 300; + position: relative; + left: 4px; + opacity: 0.5; +} + +.buttons a { + display: block; + background-color: rgba(255, 255, 255, 0.2) !important; + width: 10em; + text-align: center; + margin-bottom: 0.5em; + padding: 0.25em 0; + border-radius: 4px; +} + +.buttons a:hover { + background-color: white !important; + color: #395E7E; +} + +@media (max-width: 1000px) { + header .notes { + display: none; + } + + header .buttons { + margin-right: 2em; + } +} + +@media (max-width: 660px) { + header .buttons { + display: none; + } +} + +h2 { + margin-top: 2em; + font-size: 36px; + font-weight: 300; +} + +hr { + border: none; + height: 40px; +} + +footer { + font-size: 14px; + margin-top: 100px; + margin-bottom: 20px; + opacity: 0.5; +} + +a { + text-decoration: none; + color: white; + /*background-color: red;*/ +} + +a[href=""] { + color: white !important; + /*background-color: red !important;*/ +} + +a:hover { + text-decoration: underline; +} + +header a:hover { + text-decoration: none; +} + +.main { + margin-top: 50px; +} + +.main a[href], footer a[href] { + background-color: #edd; + color: #844; + letter-spacing: -0.5px; + padding: 0 2px; + border-radius: 3px; +} + +header a[href], nav a[href] { + color: inherit; + background-color: transparent; + letter-spacing: inherit; +} + +a[href] code { + background-color: transparent; +} + +span.cpp, span.cc { + font-size: 90%; +} + +span.eleven { + font-size: 85%; +} + +span#note:target { + background-color: yellow; +} + +.pane { + float: left; + width: 49%; +} + +.hack { + clear: both; +} + +.pane.left > * { + margin-right: 10px; +} + +.pane.right > * { + margin-left: 10px; +} + +.main h3 { + font-size: 30px; + margin-top: 2em; + color: black; +} + +h3 { + margin-top: 0; + margin-bottom: 0; + font-weight: inherit; +} + +h3.contents { + font-size: 22px; +} + +.pane pre { + font-size: 14px; + padding-top: 20px; + padding-bottom: 20px; + line-height: 1.15; +} + +header { + overflow: hidden; +} + +header .container { + position: relative; +} + +div.back { + position: absolute; + bottom: -0.35em; + left: -40px; + font-size: 288px; + font-weight: bold; + letter-spacing: 20px; + opacity: 0.1; + text-shadow: -20px 20px #444; + white-space: nowrap; +} + +.panes { + clear: both; + margin-top: 2em; + margin-bottom: 2em; + overflow: auto; +} + +.tutorial-footer { + margin-top: 4em; +} + +.tutorial-footer .next { + font-weight: 100; + font-size: 24px; +} + +.tutorial-footer .next a[href] { + font-weight: 300; +} + +li { + margin-top: 5px; +} + +.blurbs { + padding-left: 0; + list-style-type: none; + margin-top: 30px; +} + +.blurbs > li { + float: left; + min-height: 5em; +} + +@media (min-width: 1076px) { + .blurbs > li { + width: 28%; + margin-right: 4%; + } + + .blurbs > li.zero-mod-three { + clear: both; + margin-left: 2%; + } +} + +@media (max-width: 1075px) { + .blurbs > li { + width: 45%; + margin-right: 4%; + } + + .blurbs > li.zero-mod-two { + clear: both; + margin-left: 2%; + } +} + +@media (max-width: 620px) { + .blurbs > li { + float: none; + width: 100%; + min-height: 3em; + } + + .blurbs > li.zero-mod-two { + margin-left: 0; + margin-right: 0; + } +} + +.blurbs strong { + font-weight: inherit; + font-size: 110%; +} + +.blurbs em { + display: block; + margin-bottom: 1em; + font-size: 80%; + font-style: normal; +} + +.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 { + padding-top: 1em; + font-size: 150%; + font-weight: normal; + color: #555; +} + +.splash-text em { + border-bottom: 1px solid #888; +} + +.splash-text .self { + color: #777; + letter-spacing: 0; +} + +.splash { + float: right; + text-align: center; + white-space: nowrap; +} + +.splash pre { + display: inline-block; +} + +.splash pre.left { + text-align: right; + color: black; + font-weight: bold; + background-color: transparent; + padding-right: 0; +} + +.splash pre.right { + text-align: left; + background-color: black; +} + +@media (max-width: 1000px) { + .splash { + float: none; + margin-left: -10%; + } +} + +@media (max-width: 700px) { + .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; + font-size: 125%; +} + +h4 em { + color: #555; + font-style: normal; + font-weight: bold; +} + +.api ul.contents { + -webkit-columns: 300px 3; + -moz-columns: 300px 3; + columns: 300px 3; +} + +.api ul.contents > li { + -webkit-column-break-inside: avoid; + page-break-inside: avoid; + break-inside: avoid; +} + +.main h3 { + margin-top: 4em; + clear: both; +} + +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; +} + +.buttons-bar { + background-color: #f4f4f4; + padding-top: 0.5em; + padding-bottom: 0.5em; + height: 20px; +} + +.buttons-bar a img { + margin-right: 25px; +} + +.buttons-bar iframe.gh-button { + width: 95px; +} + +.buttons-bar .tweet-share, +.buttons-bar .gh-button { + display: none; +} + +.index .buttons-bar .tweet-share, +.index .buttons-bar .gh-button { + display: initial; +} + +.buttons-bar iframe.gh-watch { + width: 103px; +} + +.external { + font-size: 75%; +} diff --git a/demo/BitSets.html b/demo/BitSets.html new file mode 100644 index 0000000..a77181a --- /dev/null +++ b/demo/BitSets.html @@ -0,0 +1,154 @@ + + + + + + + +Bit sets - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +

+ This is an example of code you can write on top of Better Enums. It's a valid + program — you can download it and try it out. The + program is also part of the test suite. +

+ +

Bit sets

+

If you want to use std::bitset or a similar library to have enums be keys into +a bit set, you need to know the number of bits at compile time. You can easily +automate this with Better Enums, even when constants are not declared in +increasing order.

+
+

We simply need to find the maximum value of any given enum type.

+
#include <bitset>
+#include <iostream>
+#include <enum.h>
+
+template <typename Enum>
+constexpr Enum max_loop(Enum accumulator, size_t index)
+{
+    return
+        index >= Enum::_size() ? accumulator :
+        Enum::_values()[index] > accumulator ?
+            max_loop<Enum>(Enum::_values()[index], index + 1) :
+            max_loop<Enum>(accumulator, index + 1);
+}
+
+template <typename Enum>
+constexpr Enum max()
+{
+    return max_loop<Enum>(Enum::_values()[0], 1);
+}

And use that to declare a bit set template:

+
template <typename Enum>
+using EnumSet = std::bitset<max<Enum>()._to_integral() + 1>;

Now, we can have bit sets that are wide enough to hold whatever range we +declared. We just declare enums, and the numeric values of their constants will +be bit indices. The rest is straightforward.

+
BETTER_ENUM(EFLAGS, int,
+            Carry, Parity = 2, Adjust = 4, Zero, Sign, Trap, Interrupt, Direction,
+            Overflow, NestedTask = 14, Resume = 16, V8086, AlignmentCheck,
+            CPUIDPresent = 21)
+
+int main()
+{
+    EnumSet<EFLAGS>     eflags = 1 << EFLAGS::Carry | 1 << EFLAGS::Zero;
+
+    if (eflags.test(EFLAGS::Carry))
+        eflags.set(EFLAGS::Trap);
+
+    std::cout << eflags << std::endl;
+
+    return 0;
+}

+

If we want bit sets of fixed known width instead, we can use the code above to +check that we haven't declared any bit indices out of range:

+
static_assert(max<EFLAGS>()._to_integral() < 32,
+              "some bit indices are out of range");
+ + + + +
+
+ + + + + + diff --git a/demo/C++17Reflection.html b/demo/C++17Reflection.html new file mode 100644 index 0000000..bc5265e --- /dev/null +++ b/demo/C++17Reflection.html @@ -0,0 +1,156 @@ + + + + + + + +C++17 reflection - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.10.1

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ + + +
+
+ + +

+ This page is an advanced demo showing the kind of code you can write on top of + Better Enums. It's a valid program — you can + download it and try it out. The program runs as part of + the automated test suite. +

+ +

C++17 reflection

+

Better Enums can be used to approximately implement the enums portion of the +C++17 reflection proposal N4428 in C++11. The implementation is +approximate in the following senses:

+
    +
  • It only applies to Better Enums, not built-in enums.
  • +
  • enum_traits<E>::enumerators::get<I>::identifier is a non-constexpr +function rather than a constexpr variable. I could make it a constexpr +variable as in the proposal, but that requires +compile-time name trimming to be enabled for the Better Enum +on which get is used. Since that's an opt-in feature, I can't guarantee it. +I preferred not to write feature-detection code, in order to keep the +implementation simple.
  • +
  • The return type of identifier is const char* instead of the proposed +std::string_literal, because I don't have an implementation of the latter +available. I'm also ignoring the requirements on encoding, and just taking +whatever the preprocessor provides.
  • +
+

With that out of the way, we can look at a simple example.

+
+

The implementation is defined in extra/better-enums/n4428.h. Let's +assume that extra/ has been added as a directory to search for include files.

+
#include <iostream>
+#include <enum.h>
+#include <better-enums/n4428.h>

+

Let's declare an enum:

+
ENUM(Channel, char, Red = 1, Green, Blue)

N4428 proposes three constexpr traits, of which we have two implemented +exactly — that is, as constexpr:

+
constexpr std::size_t   size =
+    std::enum_traits<Channel>::enumerators::size;
+
+constexpr Channel       value_0 =
+    std::enum_traits<Channel>::enumerators::get<0>::value;
+constexpr Channel       value_1 =
+    std::enum_traits<Channel>::enumerators::get<1>::value;

Let's check the results:

+
static_assert(size == 3, "");
+
+static_assert(value_0 == +Channel::Red, "");
+static_assert(value_1 == +Channel::Green, "");

Finally, we can try using identifier:

+
int main()
+{
+    std::cout
+        << std::enum_traits<Channel>::enumerators::get<2>::identifier()
+        << std::endl;
+
+    return 0;
+}

That prints Blue, as you would expect.

+ + + + + +
+
+ + + + + + diff --git a/demo/C++17ReflectionProposal.html b/demo/C++17ReflectionProposal.html new file mode 100644 index 0000000..d545b7c --- /dev/null +++ b/demo/C++17ReflectionProposal.html @@ -0,0 +1,224 @@ + + + + + + + +C++17 reflection proposal - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +

+ This is an example of code you can write on top of Better Enums. It's a valid + program — you can download it and try it out. The + program is also part of the test suite. +

+ +

C++17 reflection proposal

+

You can try this demo live online.

+

Better Enums can be used to implement the enums portion of the +C++17 reflection proposal N4428 in C++11. N4428 proposes the +following traits interface:

+
namespace std {
+
+template <typename E>
+struct enum_traits {
+    struct enumerators {
+        constexpr static size_t         size;
+
+        template <size_t I>
+        struct get {
+            constexpr string_literal    identifier;
+            constexpr static E          value;
+        };
+    };
+};
+
+}

So, the basic usage would be:

+
enum class Foo {A, B, C};
+
+constexpr size_t            size =
+    std::enum_traits<Foo>::enumerators::size;
+
+constexpr Foo               value_0 =
+    std::enum_traits<Foo>::enumerators::get<0>::value;
+
+constexpr string_literal    name_1 =
+    std::enum_traits<Foo>::enumerators::get<1>::identifier;

Resulting in the values 3, Foo::A, and "B", respectively.

+
+

The interface is implemented in the optional header file +extra/better-enums/n4428.h. There is a necessary difference: the +interface is only available for enums declared through the BETTER_ENUM macro. +This is because the macro is what generates the information necessary for +reflection.

+

Demo

+

So, with that out of the way, we can do a little test. Let's assume that +extra/ has been added as a directory to search for include files.

+
#ifndef BETTER_ENUMS_CONSTEXPR_TO_STRING
+#define BETTER_ENUMS_CONSTEXPR_TO_STRING
+#endif
+
+#include <iostream>
+#include <enum.h>
+#include <better-enums/n4428.h>

+

Let's declare an enum:

+
BETTER_ENUM(Channel, char, Red = 1, Green, Blue)

...and try N4428:

+
constexpr std::size_t   size =
+    std::enum_traits<Channel>::enumerators::size;
+
+constexpr Channel       value_0 =
+    std::enum_traits<Channel>::enumerators::get<0>::value;
+
+constexpr Channel       value_1 =
+    std::enum_traits<Channel>::enumerators::get<1>::value;
+
+constexpr const char    *identifier_2 =
+    std::enum_traits<Channel>::enumerators::get<2>::identifier;

...and check the results:

+
static_assert(size == 3, "");
+
+static_assert(value_0 == +Channel::Red, "");
+static_assert(value_1 == +Channel::Green, "");
+
+int main()
+{
+    std::cout << identifier_2 << std::endl;
+    return 0;
+}

That prints Blue, as you would expect.

+

Quirk

+

The reason for the #define in the code above is that there is one quirk: +the interface above is available only for Better Enums for which +compile-time name trimming is enabled — those declared when +BETTER_ENUMS_CONSTEXPR_TO_STRING was defined, or declared with the SLOW_ENUM +variant of BETTER_ENUM. As mentioned on the linked page, the reason +compile-time name trimming is not the default is that, while still pretty fast, +it is four times slower than program-startup-time name trimming. The latter is +the default.

+

Despite the above, a variation on the interface is available for enums without +compile-time name trimming:

+
namespace std {
+
+template <typename E>
+struct enum_traits {
+    struct enumerators {
+        constexpr static size_t         size;
+
+        template <size_t I>
+        struct get {
+            constexpr const char        *identifier;
+            constexpr static E          value;
+        };
+
+        // For enums without compile-time name trimming.
+        template <size_t I>
+        struct get_alt {
+            static const char* identifier();
+            constexpr static E          value;
+        };
+    };
+};
+
+}

As you can see, the difference is that identifier is a non-constexpr +function, and you have to access it through get_alt<I>.

+
// Without compile-time name trimming.
+BETTER_ENUM(Depth, int, HighColor, TrueColor)
+
+int main()
+{
+    std::cout
+        << std::enum_traits<Depth>::enumerators::get_alt<1>::identifier()
+        << std::endl;
+
+    return 0;
+}

The future

+

N4428 is the fourth in a series of revisions: N3815, N4027, +N4113, N4428. If there are more revisions that change the proposal for +enums, I will try to implement those as well.

+ + + + + +
+
+ + + + + + diff --git a/demo/NonIntegralUnderlyingTypes.html b/demo/NonIntegralUnderlyingTypes.html new file mode 100644 index 0000000..012299e --- /dev/null +++ b/demo/NonIntegralUnderlyingTypes.html @@ -0,0 +1,242 @@ + + + + + + + +Non-integral underlying types - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.10.1

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ + + +
+
+ + +

+ This page is an advanced demo showing the kind of code you can write on top of + Better Enums. It's a valid program — you can + download it and try it out. The program runs as part of + the automated test suite. +

+ +

Non-integral underlying types

+

The underlying type of a Better Enum doesn't have to be an integral type. It can +be any literal type T, as long as you provide a constexpr two-way mapping +between T and an integral type of your choosing. This also works in C++98 +— though then, of course, T doesn't have to be literal and the mapping +doesn't have to be constexpr. In C++98, everything involving T will simply +be done by Better Enums at run time.

+

This feature is semi-experimental. I am considering relaxing the requirements on +T so that it doesn't have to be literal. I can use a reinterpret_cast to +make a mapping automatically. This will make non-integral underlying types +easier to use, but will also prevent usage at compile time, which unfortunately +has structural consequences for the implementation of Better Enums, and +additional semantic consequences for usage, even at run time.

+

In the meantime, here's how to have a non-integral underlying type in the +current version.

+
#include <iostream>
+#include <enum.h>
+typedef unsigned char uint8_t;      // <cstdint> not in C++98.
+
+
+
+// The underlying type. A color triplet.
+struct html_color {
+    uint8_t     r, g, b;
+
+    constexpr html_color(uint8_t _r, uint8_t _g, uint8_t _b) :
+        r(_r), g(_g), b(_b) { }
+};
+
+// The mapping. It just stuffs bits to get the same effect as
+// reinterpret_cast, except reinterpret_cast is not available in constexpr
+// functions, so we have to write the bit manipulations out. On modern
+// C++11 compilers, you don't have to enter the better_enums namespace like
+// this - you can just do
+// struct ::better_enums::integral_mapping<html_color> { ...
+namespace better_enums {
+
+template <>
+struct integral_mapping<html_color> {
+    using integral_representation = unsigned int;
+
+    constexpr static html_color from_integral(unsigned int i)
+        { return html_color(i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff); }
+
+    constexpr static unsigned int to_integral(html_color c)
+        { return (unsigned int)c.r << 16 | (unsigned int)c.g << 8 | c.b; }
+};
+
+}
+
+
+
+// The enum itself.
+ENUM(Color, html_color,
+     darksalmon = 0xc47451, purplemimosa = 0x9e7bff, slimegreen = 0xbce954)

Now, we can do:

+
int main()
+{
+    Color   color = Color::darksalmon;
+
+    std::cout << std::hex;
+    std::cout << "Red component: "   << (int)color->r << std::endl;
+    std::cout << "Green component: " << (int)color->g << std::endl;
+    std::cout << "Blue component: "  << (int)color->b << std::endl;
+
+    std::cout << color._to_string() << std::endl;
+
+    switch (color) {
+        case Color::darksalmon:   return 0;
+        case Color::purplemimosa: return 1;
+        case Color::slimegreen:   return 2;
+    }
+
+    return 0;
+}

This prints each component, the name of the color ("darksalmon"), and then +exits from the switch with status 0.

+

Constructors in initializers

+

The above declaration used only numbers in initializers, but it is actually +possible to use constructors of html_color. We have to add a constexpr +converting operator directly to html_color, however:

+
struct better_html_color {
+    uint8_t     r, g, b;
+
+    constexpr better_html_color(uint8_t _r, uint8_t _g, uint8_t _b) :
+        r(_r), g(_g), b(_b) { }
+
+    // This is new:
+    constexpr operator unsigned int() const
+        { return (unsigned int)r << 16 | (unsigned int)g << 8 | b; }
+};
+
+namespace better_enums {
+
+template <>
+struct integral_mapping<better_html_color> {
+    using integral_representation = unsigned int;
+
+    constexpr static better_html_color from_integral(unsigned int i)
+    {
+        return better_html_color(i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff);
+    }
+
+    constexpr static unsigned int to_integral(better_html_color c)
+        { return (unsigned int)c.r << 16 | (unsigned int)c.g << 8 | c.b; }
+};
+
+}

This allows:

+
ENUM(BetterColor, better_html_color,
+     darksalmon = 0xc47451, purplemimosa = 0x9e7bff, slimegreen = 0xbce954,
+     celeste = better_html_color(0x50, 0xeb, 0xec))

If you can't edit your literal type to add this converting operator, or don't +want to for type safety reasons, you can achieve a similar effect by declaring +an intermediate type U that html_color can convert to, that can convert to +the integral type. Then, cast your constructor call to U. The type U is for +declarations only.

+

Constructors in initializers require C++11. Also, g++ doesn't support this +before 5.1.

+

Letting the compiler enumerate your type

+

Of course, as long as the values are valid, you can let the compiler enumerate +your type as in a regular enum, by omitting initializers:

+
ENUM(FD, file_descriptor, STDIN, STDOUT, STDERR, SomePipeYourDaemonHas, ...)

Here, FD::STDIN maps to the integral representation 0, STDOUT to 1, and so +on.

+

Discussion

+

This feature is still semi-experimental, though I expect it to remain stable, +except perhaps that I will make it possible to infer the type +integral_representation.

+

Any opinions are welcome.

+
    +
  • The main reason Better Enums needs you to supply and explicit mapping is +because it can't just get the "bits" of objects of underlying type in +constexpr code. Both reinterpret_cast and union abuse seem to be forbidden +in constexpr functions.
  • +
  • There is currently no way to have two different integral representaitons for +the same underlying type in different enums. I don't think that's a major use +case at this point, however.
  • +
+ + + + + +
+
+ + + + + + diff --git a/demo/SemiQuine.html b/demo/SemiQuine.html new file mode 100644 index 0000000..d5ec75f --- /dev/null +++ b/demo/SemiQuine.html @@ -0,0 +1,225 @@ + + + + + + + +Semi-quine - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +

+ This is an example of code you can write on top of Better Enums. It's a valid + program — you can download it and try it out. The + program is also part of the test suite. +

+ +

Semi-quine

+

Let's make a Better Enum assemble its own definition in memory. It won't be +literally as defined, since we will lose the exact initializer expressions, but +we will be able to preserve the numeric values. We will reserve the memory +buffer for the definition at compile time.

+

Ok, so it's not really a quine, because we won't be writing all the code needed +to generate the definition to the buffer as well. And, there are better ways to +dump the definition than shown here. You could simply define a macro that +expands to an BETTER_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 :)

+

Contents

+
+
#include <cassert>
+#include <cstdio>
+#include <iostream>

+

First, we will need +full compile-time reflection, +since we will be calling _to_string. Let's make sure it's enabled by defining +BETTER_ENUMS_CONSTEXPR_TO_STRING before including enum.h:

+
#ifndef BETTER_ENUMS_CONSTEXPR_TO_STRING
+#define BETTER_ENUMS_CONSTEXPR_TO_STRING
+#endif
+
+#include <enum.h>

Now, let's declare some enums to dump later:

+
BETTER_ENUM(Channel, int, Red, Green, Blue)
+BETTER_ENUM(Depth, int, TrueColor = 1, HighColor = 0)

Computing the size of the buffer

+

First, we need to be able to get the length of each declaration above. We will +assume that the underlying type is always int, and that the spacing convention +is followed as above.

+

First, let's get the lengths of basic components:

+
// Returns the length of the string representation of the number n
+constexpr size_t value_length(int n, int bound = 10, size_t digits = 1)
+{
+    return
+        n < bound ? digits : value_length(n, bound * 10, digits + 1);
+}
+
+// Returns the length of s
+constexpr size_t string_length(const char *s, size_t index = 0)
+{
+    return s[index] == '\0' ? index : string_length(s, index + 1);
+}

Now, the length of the constant declaration. Here is where we lose information +about initializers. We are going to format the constant declarations like this:

+
Red = 0, Green = 1, Blue = 2
+TrueColor = 1, HighColor = 0

This is because Better Enums doesn't provide a way to know what the exact +initializer was or whether there even was one — just the numeric value of +each constant. If we were trying to be clever, we could avoid formatting +initializers for sequential values, but I won't go through this exercise here.

+
// Returns the length of the constants portion of the declaration of Enum,
+// as described above.
+template <typename Enum>
+constexpr size_t constants_length(size_t index = 0, size_t accumulator = 0)
+{
+    return
+        index >= Enum::_size() ? accumulator :
+        constants_length<Enum>(
+            index + 1, accumulator
+                        + string_length(", ")
+                        + string_length(Enum::_names()[index])
+                        + string_length(" = ")
+                        + value_length(
+                            Enum::_values()[index]._to_integral()));
+}

Finally, we can combine these to get the length of the formatted declaration of +the whole enum:

+
// Returns the length of the whole declaration of Enum, assuming the
+// underlying type is int, and the constants are initialized as assumed by
+// constants_length() above.
+template <typename Enum>
+constexpr size_t declaration_length()
+{
+    return
+        string_length("BETTER_ENUM(")
+        + string_length(Enum::_name())
+        + string_length(", int")
+        + constants_length<Enum>()
+        + string_length(")");
+}

Formatting the enums

+

Now, we can declare the buffers. The memory will be reserved at load time by the +binary's loader. The extra one byte in each buffer is for the null terminator.

+
char    channel_definition[declaration_length<Channel>() + 1];
+char    depth_definition[declaration_length<Depth>() + 1];

Let's also create the formatting function. This is executed at run time, but we +will be giving it pointers to our statically-allocated buffers. It will format +the enum declaration and then return the number of bytes it wrote to the buffer, +so that we can do a sanity check on it.

+
template <typename Enum>
+size_t format(char *buffer)
+{
+    size_t  offset = 0;
+
+    offset += std::sprintf(buffer, "BETTER_ENUM(%s, int", Enum::_name());
+
+    for (Enum value : Enum::_values()) {
+        offset +=
+            std::sprintf(buffer + offset,
+                         ", %s = %i",
+                         value._to_string(), value._to_integral());
+    }
+
+    offset += std::sprintf(buffer + offset, ")");
+
+    return offset;
+}

Checking our work

+

Now, we can write and run this code.

+
int main()
+{
+    size_t  channel_length = format<Channel>(channel_definition);
+    assert(channel_length + 1 == sizeof(channel_definition));
+
+    size_t  depth_length = format<Depth>(depth_definition);
+    assert(depth_length + 1 == sizeof(depth_definition));
+
+    std::cout << channel_definition << std::endl;
+    std::cout << depth_definition << std::endl;
+
+    return 0;
+}

It prints:

+
BETTER_ENUM(Channel, int, Red = 0, Green = 1, Blue = 2)
+BETTER_ENUM(Depth, int, TrueColor = 1, HighColor = 0)
+ + + + +
+
+ + + + + + diff --git a/demo/SpecialValues.html b/demo/SpecialValues.html new file mode 100644 index 0000000..f089914 --- /dev/null +++ b/demo/SpecialValues.html @@ -0,0 +1,220 @@ + + + + + + + +Special values - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +

+ This is an example of code you can write on top of Better Enums. It's a valid + program — you can download it and try it out. The + program is also part of the test suite. +

+ +

Special values

+

Suppose your project has a convention where each enum has special invalid and +default values — for example, Enum::Invalid is invalid, and the +first valid constant is default. With Better Enums, you can get the compiler +to enforce the convention. At the end of this demo, we will have defined +functions and templates that allow us to write:

+
Channel     channel = default_;
+Channel     channel = invalid;
+
+void do_something(Channel channel);
+
+do_something(default_);
+do_something(invalid);

The compiler will compute default and invalid values automatically, but the +programmer will also be able to override the choice. Obviously, the syntax above +is very legible and maintainable — the intent is clear and your code base +will respond automatically to changes in enum definitions.

+

Contents

+

Invalid values

+

Let's start by defining the invalid values.

+
#include <iostream>
+#include <stdexcept>
+#include <enum.h>

Perhaps the convention is that the invalid value is usually called Invalid, +but not for all enums. We will encode that using a template function. The +unspecialized version will encode the default policy:

+
template <typename Enum>
+constexpr Enum invalid_impl() { return Enum::Invalid; }

A macro allows us to override the invalid value by specializing the template:

+
#define OVERRIDE_INVALID(Enum, Value)                 \
+template<>                                            \
+constexpr Enum invalid_impl<Enum>() { return Enum::Value; }

Now, we can declare enums like these:

+
BETTER_ENUM(Channel, int, Red, Green, Blue, Invalid)
+// Invalid is the invalid value by default
+
+BETTER_ENUM(Compression, int, Undefined, None, Huffman)
+OVERRIDE_INVALID(Compression, Undefined)

and use them:

+
static_assert(invalid_impl<Channel>() == +Channel::Invalid, "");
+static_assert(invalid_impl<Compression>() == +Compression::Undefined, "");

This even supports enums that don't have an invalid value at all. As long as +they don't have a constant called Invalid, you will get a compile-time error +if you try to call invalid_impl<>() on them — as you probably should!

+

Default values

+

Perhaps here the convention is the first value that is not invalid is default, +unless, again, overridden by the programmer. This can be encoded using only a +slightly more complex template function for the general case:

+
template <typename Enum>
+constexpr Enum default_impl()
+{
+    return
+        Enum::_size() < 2 ?
+            throw std::logic_error("enum has no valid constants") :
+        Enum::_values()[0] == invalid_impl<Enum>() ?
+            Enum::_values()[1] :
+            Enum::_values()[0];
+}

The above code gives us the first value if it is not invalid, otherwise the +second value.

+

The companion macro for overriding the choice of default value is almost the +same as it was for invalid. The difference is that we do an extra sanity check +to make sure the programmer doesn't declare the invalid value to be the default. +If the sanity check fails, we produce a nice error message. Again, we are +assuming that this is dictated by policy.

+
#define OVERRIDE_DEFAULT(Enum, Value)                  \
+static_assert(Enum::Value != Enum::Invalid,            \
+              #Enum ": default cannot equal invalid"); \
+template<>                                             \
+constexpr Enum default_impl<Enum>() { return Enum::Value; }

And, as before, the usage:

+
static_assert(default_impl<Channel>() == +Channel::Red, "");
+static_assert(default_impl<Compression>() == +Compression::None, "");

And, if you do

+
BETTER_ENUM(Answer, int, Yes, No, Invalid)
+// OVERRIDE_DEFAULT(Answer, Invalid)

you will get a helpful compile-time error saying +Answer: default cannot equal invalid.

+

Making the syntax nicer

+

At this point, our policy is encoded by the ugly-looking functions +invalid_impl and default_impl. We want a nicer syntax. The main reason we +don't just use these functions directly is that the compiler wouldn't infer +their template arguments from the context. For example, we would have to write +things like

+
Channel     channel = invalid_impl<Channel>();

which is unfortunate, because it results in repetition.

+

In this section, we introduce two global objects called invalid and default_ +that will implicitly convert to any Better Enum type, and provide the invalid +or default value, respectively, when they do so. They will act as new +"keywords".

+
struct invalid_t {
+    template <typename To>
+    constexpr operator To() const { return invalid_impl<To>(); }
+};
+
+struct default_t {
+    template <typename To>
+    constexpr operator To() const { return default_impl<To>(); }
+};
+
+constexpr invalid_t     invalid{};
+constexpr default_t     default_{};

As you can see, both of these provide the families of implicit conversions that +we need. Now, we can test:

+
static_assert(+Channel::Invalid == invalid, "");
+static_assert(+Compression::Undefined == invalid, "");
+
+static_assert(+Channel::Red == default_, "");
+static_assert(+Compression::None == default_, "");

Finally, we can have nice code such as this:

+
void dump(Channel channel)
+{
+    std::cout << channel._to_string() << std::endl;
+}
+
+int main()
+{
+    dump(invalid);
+
+    Channel     channel = default_;
+    dump(channel);
+
+    return 0;
+}

+

There are many possible variations of these policies, but I think most of them +can be encoded in a reasonable fashion using the tools Better Enums provides. +Enjoy!

+ + + + + +
+
+ + + + + + diff --git a/image/tweet.png b/image/tweet.png new file mode 100644 index 0000000..dbbd61d Binary files /dev/null and b/image/tweet.png differ diff --git a/image/twsupport.png b/image/twsupport.png new file mode 100644 index 0000000..ddc4b1f Binary files /dev/null and b/image/twsupport.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..6a008dd --- /dev/null +++ b/index.html @@ -0,0 +1,313 @@ + + + + + + + +Better Enums - Clean reflective enums for C++ + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +
+
enable
+
+declare
+
+
+parse
+format
+
+
+count
+iterate
+
+
+
+
+switch
+
+
+
+
+
+
+safe cast
+
+
+during
+compilation
+
+
#include <enum.h>
+
+BETTER_ENUM(Channel, int, Red = 1, Green, Blue)
+
+
+Channel     c = Channel::_from_string("Red");
+const char  *s = c._to_string();
+
+
+size_t      n = Channel::_size();
+for (Channel c : Channel::_values()) {
+    run_some_function(c);
+}
+
+
+switch (c) {
+    case Channel::Red:    // ...
+    case Channel::Green:  // ...
+    case Channel::Blue:   // ...
+}
+
+
+Channel     c = Channel::_from_integral(3);
+
+
+constexpr Channel c =
+    Channel::_from_string("Blue");
+
+ +

+ Better Enums is a single, lightweight header file that makes your compiler generate + reflective enum types. +

+ +

That means you can easily convert enums to and from strings, +validate them, and loop over them. In C++11, you can do it all at +compile time.

+

It's what built-in enums ought to support. Better Enums simply adds the missing +features. And, it is based on the best known techniques, thoroughly tested, +fast, portable, and documented exhaustively.

+

To use it, just include enum.h and begin the +tutorial!

+
+ +

Highlights

+
    +
  • + Unobtrusive syntax + + No ugly macros. Use initializers as with built-in enums. + Internal members have underscores to avoid clashing with your constant + names. + +
  • + +
  • + No external dependencies + + Uses only standard C++. Installation is simple — just download + enum.h. There are no objects or libraries to link with. + +
  • + +
  • + No generator program needed + + Just include enum.h. It's a metaprogram executed by your + compiler. + +
  • + +
  • + Plays nice with switch + + Use a Better Enum like a built-in enum, and still have the + compiler do case checking. + +
  • + +
  • + Don't repeat yourself + + No more unmaintanable maps or switch statements for + converting enums to strings. + +
  • + +
  • + Non-contiguous sequences + + Iteration and counting are much easier to maintain than with an extra + Count constant and assuming a dense range. + +
  • + +
  • + Fast compilation + + Much less impact on build time than even just including + iostream. enum.h is only slightly more than 1000 + lines long. + +
  • + +
  • + Compile-time reflection + + Have the compiler do additional enum processing using your own + templates or constexpr functions. + +
  • + +
  • + Uniform interface for C++98, C++11 + + Scoped, sized, reflective enums for C++98, and an easy upgrade + path. + +
  • + +
  • + Stream operators + + Write enum names directly to std::cout or use + boost::lexical_cast. + +
  • + +
  • + Free and open source + + Released under the BSD license for use in any project, free or commercial. + +
  • +
+ +
+ +

Documentation

+ + +
+ + + +
+
+ + + + + + diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..2ad657a --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,64 @@ + + + + http://aantron.github.io/better-enums/tutorial/HelloWorld.html + + + http://aantron.github.io/better-enums/tutorial/Conversions.html + + + http://aantron.github.io/better-enums/tutorial/Iteration.html + + + http://aantron.github.io/better-enums/tutorial/SafeSwitch.html + + + http://aantron.github.io/better-enums/tutorial/Maps.html + + + http://aantron.github.io/better-enums/tutorial/StreamOperators.html + + + http://aantron.github.io/better-enums/tutorial/ScopeAndSafety.html + + + http://aantron.github.io/better-enums/tutorial/Representation.html + + + http://aantron.github.io/better-enums/tutorial/CompileTimeUsage.html + + + http://aantron.github.io/better-enums/demo/SpecialValues.html + + + http://aantron.github.io/better-enums/demo/BitSets.html + + + http://aantron.github.io/better-enums/demo/SemiQuine.html + + + http://aantron.github.io/better-enums/demo/C++17ReflectionProposal.html + + + http://aantron.github.io/better-enums/ApiReference.html + + + http://aantron.github.io/better-enums/CompilerSupport.html + + + http://aantron.github.io/better-enums/DesignDecisionsFAQ.html + + + http://aantron.github.io/better-enums/ExtendingLimits.html + + + http://aantron.github.io/better-enums + 1.0 + + + http://aantron.github.io/better-enums/OptInFeatures.html + + + http://aantron.github.io/better-enums/Performance.html + + diff --git a/tutorial/CompileTimeUsage.html b/tutorial/CompileTimeUsage.html new file mode 100644 index 0000000..50d86ee --- /dev/null +++ b/tutorial/CompileTimeUsage.html @@ -0,0 +1,148 @@ + + + + + + + +Compile-time usage - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +

+ Welcome to the Better Enums tutorials! The code in this tutorial forms a + valid program, which you can download and play with. The + program runs as part of the automated test suite. +

+ +

Compile-time usage

+

When used with C++11, Better Enums are generated entirely during compilation. +All the data is available for use by your own constexpr functions. The +examples in this tutorial aren't very useful, but look at the +demos at the bottom of the main page to +get an idea of what can be done. Here, you will see the basics.

+
#include <iostream>
+
+// The reason for this is explained below.
+#ifndef BETTER_ENUMS_CONSTEXPR_TO_STRING
+#define BETTER_ENUMS_CONSTEXPR_TO_STRING
+#endif
+
+#include <enum.h>
+
+BETTER_ENUM(Channel, int, Red = 1, Green = 2, Blue = 3)
+
+constexpr Channel      channel = Channel::_from_integral(2);
+constexpr int          value = channel._to_integral();
+
+constexpr const char   *name = channel._to_string();
+constexpr Channel      parsed = Channel::_from_string("Red");

All of the above are computed during compilation. The reason for the macro +definition at the top of the file is explained on the +opt-in features page. +Basically, it makes _to_string constexpr, but slows down compilation.

+

You can also do things such as:

+
constexpr size_t length(const char *s, size_t index = 0)
+{
+    return s[index] == '\0' ? index : length(s, index + 1);
+}
+
+constexpr size_t    length_of_name_of_second_constant =
+    length(Channel::_names()[1]);
+
+int main()
+{
+    std::cout << length_of_name_of_second_constant << std::endl;
+
+    return 0;
+}

Which prints "5", the length of "Green". That 5 was also computed during +compilation.

+ + + + + + +
+
+ + + + + + diff --git a/tutorial/Conversions.html b/tutorial/Conversions.html new file mode 100644 index 0000000..07bc903 --- /dev/null +++ b/tutorial/Conversions.html @@ -0,0 +1,211 @@ + + + + + + + +Conversions - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +

+ Welcome to the Better Enums tutorials! The code in this tutorial forms a + valid program, which you can download and play with. The + program runs as part of the automated test suite. +

+ +

Conversions

+

Let's begin by including enum.h and declaring our enum:

+
#include <cassert>
+#include <iostream>
+
+#include <enum.h>
+
+BETTER_ENUM(Channel, int, Cyan = 1, Magenta, Yellow, Black)

We now have an int-sized enum with four constants.

+

There are three groups of conversion functions: for strings, case-insensitive +strings, and integers. They all follow the same pattern, so I'll explain the +string functions in detail, and the rest can be understood by analogy.

+

Contents

+

Strings

+

There are three functions:

+
    +
  1. ._to_string
  2. +
  3. ::_from_string
  4. +
  5. ::_from_string_nothrow
  6. +
+
int main()
+{
+    Channel     channel = Channel::Cyan;
+    std::cout << channel._to_string() << " ";

As you'd expect, the code above prints "Cyan".

+

If channel is invalid — for example, if you simply cast the number "42" +to Channel — then the result of to_string is undefined.

+
+
    channel = Channel::_from_string("Magenta");
+    std::cout << channel._to_string() << " ";

This is also straightforward. If you pass a string which is not the name of a +declared value, _from_string throws std::runtime_error.

+
+

If you don't want an exception, there is _from_string_nothrow:

+
    better_enums::optional<Channel> maybe_channel =
+        Channel::_from_string_nothrow("Yellow");
+
+    if (!maybe_channel)
+        std::cout << "error";
+    else
+        std::cout << maybe_channel->_to_string() << " ";

This returns an optional value, in the style of +boost::optional +or the proposed +std::optional.

+

What that means for the above code is:

+
    +
  • if the conversion succeeds, maybe_channel converts to true and +*maybe_channel is the converted value of type Channel,
  • +
  • if the conversion fails, maybe_channel converts to false.
  • +
+

In C++11, you can use auto to avoid writing out the optional type:

+
    auto        maybe_channel = Channel::_from_string_nothrow("Yellow");
+    if (!maybe_channel)
+        std::cout << "error";
+    else
+        std::cout << maybe_channel->_to_string() << " ";

Case-insensitive strings

+

The "_nocase" string conversions follow the same pattern, except for the lack +of a "to_string_nocase".

+
    +
  1. ::_from_string_nocase
  2. +
  3. ::_from_string_nocase_nothrow
  4. +
+
    channel = Channel::_from_string_nocase("cYaN");
+    std::cout << channel._to_string() << " ";
+
+    maybe_channel = Channel::_from_string_nocase_nothrow("rEeD");
+    assert(!maybe_channel);

Integers

+

And, it is similar with the representation type int:

+
    +
  1. ._to_integral
  2. +
  3. ::_from_integral
  4. +
  5. ::_from_integral_nothrow
  6. +
  7. ::_from_integral_unchecked
  8. +
+
    channel = Channel::Cyan;
+    std::cout << channel._to_integral() << " ";
+
+    channel = Channel::_from_integral(2);
+    std::cout << channel._to_string() << " ";
+
+    maybe_channel = Channel::_from_integral_nothrow(0);
+    assert(!maybe_channel);

That prints "1 Magenta".

+

_from_integral_unchecked is a no-op unchecked cast of integers to enums, so +use it carefully.

+
    channel = Channel::_from_integral_unchecked(0);
+    // Invalid - better not to try converting it to string!

Validity checking

+

For completeness, Better Enums also provides three validity checking functions, +one for each of the groups of conversions — string, case-insensitive +string, and integer:

+
    assert(Channel::_is_valid(3));
+    assert(Channel::_is_valid("Magenta"));
+    assert(Channel::_is_valid_nocase("cYaN"));

+

Almost done.

+

There is one unfortunate wrinkle. You cannot convert a literal constant such as +Channel::Cyan directly to, for example, a string. You have to prefix it with ++:

+
    std::cout << (+Channel::Cyan)._to_string();

This is due to some type gymnastics in the implementation of Better Enums. The +reference has a +full explanation.

+
+
    std::cout << std::endl;
+    return 0;
+}
+ + + + + +
+
+ + + + + + diff --git a/tutorial/HelloWorld.html b/tutorial/HelloWorld.html new file mode 100644 index 0000000..04ce36d --- /dev/null +++ b/tutorial/HelloWorld.html @@ -0,0 +1,128 @@ + + + + + + + +Hello, World! - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +

+ Welcome to the Better Enums tutorials! The code in this tutorial forms a + valid program, which you can download and play with. The + program runs as part of the automated test suite. +

+ +

Hello, World!

+

Download enum.h, then compile this program:

+
#include <iostream>
+#include "enum.h"
+
+BETTER_ENUM(Word, int, Hello, World)
+
+int main()
+{
+    std::cout << (+Word::Hello)._to_string() << ", "
+              << (+Word::World)._to_string() << "!"
+              << std::endl;
+
+    return 0;
+}

Run it, and you should see the output "Hello, World!"

+

Congratulations, you have just created your first Better Enum!

+ + + + + + +
+
+ + + + + + diff --git a/tutorial/Iteration.html b/tutorial/Iteration.html new file mode 100644 index 0000000..157d84c --- /dev/null +++ b/tutorial/Iteration.html @@ -0,0 +1,142 @@ + + + + + + + +Iteration - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +

+ Welcome to the Better Enums tutorials! The code in this tutorial forms a + valid program, which you can download and play with. The + program runs as part of the automated test suite. +

+ +

Iteration

+

Better Enums makes it easy to iterate over the values you have declared. For +example, this:

+
#include <iostream>
+#include <enum.h>
+
+BETTER_ENUM(Channel, int, Red, Green = 2, Blue)
+
+int main()
+{
+
+    for (size_t index = 0; index < Channel::_size(); ++index) {
+        Channel     channel = Channel::_values()[index];
+        std::cout << channel._to_integral() << " ";
+    }
+    std::cout << std::endl;

will print "0 2 3". And this:

+
    for (size_t index = 0; index < Channel::_size(); ++index) {
+        const char  *name = Channel::_names()[index];
+        std::cout << name << " ";
+    }
+    std::cout << std::endl;

will print "Red Green Blue".

+
+

If you are using C++11, you can have much nicer syntax:

+
    for (Channel channel : Channel::_values())
+        std::cout << channel._to_integral() << " ";
+    std::cout << std::endl;
+
+    for (const char *name : Channel::_names())
+        std::cout << name << " ";
+    std::cout << std::endl;

+
    return 0;
+}
+ + + + + +
+
+ + + + + + diff --git a/tutorial/Maps.html b/tutorial/Maps.html new file mode 100644 index 0000000..9c28469 --- /dev/null +++ b/tutorial/Maps.html @@ -0,0 +1,162 @@ + + + + + + + +Maps - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +

+ Welcome to the Better Enums tutorials! The code in this tutorial forms a + valid program, which you can download and play with. The + program runs as part of the automated test suite. +

+ +

Maps

+

It is possible to create constexpr bidirectional maps between Better Enums and +any type. This is currently an experimental feature. Feedback is very much +wanted, but please don't build any mission-critical code on top of this :)

+

The way it works is you give Better Enums a function — say, +const char* describe(Channel). The library enumerates it to make a map.

+

The reason for using a function is that a switch statement is, I believe, the +only place where a compiler will check for exhaustiveness. If you forget to +create a case for one of the enum's constants, the compiler can let you know. +Obviously, a switch statement is not data, and needs to be inside a function. +It can only be inside a constexpr function in C++14, so this feature is most +natural in C++14. When you pass the function to Better Enums, the library can +build up a lookup data structure at compile time.

+

Actually, right now, Better Enums doesn't quite do that — it enumerates +the function every time you want to convert to an enum (but not from an +enum). It simply does a linear scan every time. This is because I haven't yet +found a data structure whose compile-time generation is fast enough for +practical use.

+
+
#include <iostream>
+#include <enum.h>
+
+BETTER_ENUM(Channel, int, Red, Green, Blue)

We will create a map from this function:

+
constexpr const char* describe(Channel channel)
+{
+    switch(channel) {
+        case Channel::Red:   return "the red channel";
+        case Channel::Green: return "the green channel";
+        case Channel::Blue:  return "the blue channel";
+    }
+
+    return "needed for gcc 5";
+}

Here is the map. The actual type is better_enums::map<Channel, const char*>.

+
constexpr auto descriptions = better_enums::make_map(describe);

And the usage:

+
int main()
+{
+    std::cout << descriptions[Channel::Red] << std::endl;
+
+    std::cout << descriptions.from_enum(Channel::Red) << std::endl;
+    std::cout << descriptions.to_enum("the green channel") << std::endl;
+
+    auto not_a_literal = std::string("the blue channel");
+    std::cout << descriptions.to_enum(not_a_literal.c_str()) << std::endl;
+
+    return 0;
+}

+

make_map above produces a value of type better_enums::map<E, T>. The full +signature of the template better_enums::map is

+
template <typename Enum, typename T, typename Compare = map_compare<T>>

Compare has to be a class with a static member function +bool less(const T&, const T&). The default implementation +better_enums::map_compare simply applies operator <, except when T is +const char* or const wchar_t*. In that case, it does lexicographic comparison.

+ + + + + + +
+
+ + + + + + diff --git a/tutorial/Representation.html b/tutorial/Representation.html new file mode 100644 index 0000000..4f48b23 --- /dev/null +++ b/tutorial/Representation.html @@ -0,0 +1,168 @@ + + + + + + + +Representation - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +

+ Welcome to the Better Enums tutorials! The code in this tutorial forms a + valid program, which you can download and play with. The + program runs as part of the automated test suite. +

+ +

Representation

+

Let's go over some of the low-level properties of a Better Enum. This time, we +will declare a more unusual enum than the ones we have seen.

+
#include <cassert>
+#include <iostream>
+#include <enum.h>
+
+BETTER_ENUM(ContentType, short,
+     CompressedVideo = 5, PCM = 8, Subtitles = 17, Comment = 44)

This is for a hypothetical multimedia container file format. Perhaps the files +have sections, and each one has a header:

+
struct Header {
+    ContentType     type;
+    short           flags;
+    int             offset;
+};

+

Here is what we have.

+
int main()
+{
+    assert(sizeof(ContentType) == 2);

ContentType behaves just like a short1, in fact it simply wraps one. This +makes it possible to lay out structures in a predictable fashion:

+
    Header      header = {ContentType::PCM, 0, 0};
+
+    assert(sizeof(header) == 8);
+    assert((size_t)&header.flags - (size_t)&header.type == 2);

+

uint16_t is called ContentType's underlying or representation type. If +you want to know the representation type of any enum you have declared, it is +available as the member type ::_integral:

+
    ContentType::_integral  untrusted_value = 44;

Use this if you want a sized field to receive untrusted data, but aren't willing +to call it ContentType yet because you have not validated it. Your validator +will likely call ::_from_integral_nothrow, perform any other validation your +application requires, and then return ContentType.

+
    ContentType             type =
+        ContentType::_from_integral(untrusted_value);
+    std::cout << type._to_string() << std::endl;

+

You have probably noticed the initializers on each of the constants in +ContentType. This allows you to declare sparse enums for compatibility with +external protocols or previous versions of your software. The initializers don't +need to be literal integers — they can be anything that the compiler would +accept in a normal enum declaration. If there was a macro called +BIG_FAT_MACRO declared above, we could have written +Subtitles = BIG_FAT_MACRO. We could also have written +Subtitles = CompressedVideo.

+
+

The in-memory representation of an enum value is simply the number it has been +assigned by the compiler. You should be safe passing enums to functions like +fread and fwrite, and casting memory blocks known to be safe to struct +types containg enums. The enums will behave as expected.

+
+
    return 0;
+}
+
+
  1. It should properly be a uint16_t, and the rest of the header fields +should also be explicitly sized. However, this code is trying to be +compatible with C++98, where those names aren't available in a portable +manner.

  2. +
+
+ + + + + + +
+
+ + + + + + diff --git a/tutorial/SafeSwitch.html b/tutorial/SafeSwitch.html new file mode 100644 index 0000000..a362a67 --- /dev/null +++ b/tutorial/SafeSwitch.html @@ -0,0 +1,134 @@ + + + + + + + +Safe switch - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +

+ Welcome to the Better Enums tutorials! The code in this tutorial forms a + valid program, which you can download and play with. The + program runs as part of the automated test suite. +

+ +

Safe switch

+

A Better Enum can be used directly in a switch statement:

+
#include <iostream>
+#include <enum.h>
+
+BETTER_ENUM(Channel, int, Red, Green, Blue)
+
+int main()
+{
+    Channel     channel = Channel::Green;
+    int         n;
+
+    switch (channel) {
+        case Channel::Red:   n = 13; break;
+        case Channel::Green: n = 37; break;
+        case Channel::Blue:  n = 42; break;
+    }

If you miss a case or add a redundant one, your compiler should be able to give +you a warning — try it!

+

Note that on msvc, you may need to enable warning C4062.

+
+
    std::cout << n << std::endl;
+    return 0;
+}
+ + + + + +
+
+ + + + + + diff --git a/tutorial/ScopeAndSafety.html b/tutorial/ScopeAndSafety.html new file mode 100644 index 0000000..7c8f7fd --- /dev/null +++ b/tutorial/ScopeAndSafety.html @@ -0,0 +1,155 @@ + + + + + + + +Scope and safety - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +

+ Welcome to the Better Enums tutorials! The code in this tutorial forms a + valid program, which you can download and play with. The + program runs as part of the automated test suite. +

+ +

Scope and safety

+

This tutorial shows some of the safety features of Better Enums: scope, how to +control conversions, and the lack of a default constructor.

+

On balance, Better Enums are in one way less type-safe than enum class, and in +another way more type-safe. The first difference in safety is the presence of +implicit conversion to integral types. The second difference is the lack of a +default constructor. Both of these can be toggled, so you can make Better Enums +strictly safer than enum class, or just as safe.

+

Contents

+

Scope

+

You have probably noticed by now that Better Enums are scoped: when you declare

+
#include <cassert>
+#include <enum.h>
+
+BETTER_ENUM(Channel, int, Red = 1, Green, Blue)

you don't get names such as Red in the global namespace. Instead, you get +Channel, and Red is accessible as Channel::Red. This is no big deal in +C++11, which has enum class. In C++98, however, this typically requires +effort. Better Enums brings scope uniformly to both variants. So, despite the +above declaration, you can safely declare

+
BETTER_ENUM(Node, char, Red, Black)

and everything will work as expected.

+
int main()
+{
+    assert((+Channel::Red)._to_integral() != (+Node::Red)._to_integral());

Implicit conversion

+

A major complaint in C++98 is that enums are implicitly convertible to +integers. Unfortunately, that is also true of Better Enums, and I haven't found +a way to forbid the conversions and still have switch case checking.

+

Better Enums can be made as safe as enum class in C++11, however. If your +compiler supports enum class and you define +BETTER_ENUMS_STRICT_CONVERSION before including enum.h, the following code +will not compile:

+
    Channel     channel = Channel::Red;
+    int         n = channel;

The reason this is not enabled by default is explained in the reference page on +strict conversions.

+

You can conveniently define the macro on your compiler's command line, or by +creating a little header file that defines it, and then includes +enum.h. You can then include this new header file in your project +everywhere where you would have included enum.h.

+

Default constructor

+

Better Enums generate without a default constructor. The purpose is to support +the convention where if a Better Enum exists, then it has a valid value. So, if +you uncomment this code, the program won't compile:

+
    Channel      channel;

If this is too strict for your project, you can relax it as described +here.

+
+
    return 0;
+}
+ + + + + +
+
+ + + + + + diff --git a/tutorial/StreamOperators.html b/tutorial/StreamOperators.html new file mode 100644 index 0000000..2463c2b --- /dev/null +++ b/tutorial/StreamOperators.html @@ -0,0 +1,131 @@ + + + + + + + +Stream operators - Better Enums + + + + + + + + + + + + + + + + +
 
+ +
+
+
+

Better Enums

+

Reflective compile-time enums for C++

+

Open-source under the BSD license

+
+ +
+

Version 0.11.3

+

To install, just add enum.h to your project.

+

+ Visit the GitHub repo for issues, feedback, and the latest development. +

+
+ +
+ Download enum.h + GitHub +
+
+
+ +
+
+ + +

+ Welcome to the Better Enums tutorials! The code in this tutorial forms a + valid program, which you can download and play with. The + program runs as part of the automated test suite. +

+ +

Stream operators

+

These work almost as you'd expect. First, make sure you include iostream +before enum.h in any translation unit in which you intend to use the +operators:

+
#include <iostream>
+#include <enum.h>
+
+BETTER_ENUM(Channel, int, Red, Green, Blue)
+
+int main()
+{
+    std::cout << +Channel::Red << std::endl;
+    return 0;
+}

The thing to watch for is the +: without it, Channel::Red is a value of type +Channel::_enumerated, a C++98 enum type, so writing that to cout will +output an integer. +Channel::Red, however, is a value of type Channel, and +writing that instead will output the string "Red".

+

Input is also supported:

+
Channel     channel = Channel::Blue;
+std::cin >> channel;    // Expects input such as "Green".

+

Only char streams are supported for the time being.

+ + + + + + +
+
+ + + + + +