From 5cfa84f1b8b1065887a677d50a940c92cfbadf30 Mon Sep 17 00:00:00 2001 From: Anton Bachin Date: Mon, 19 Oct 2020 09:11:42 +0300 Subject: [PATCH] Project site --- ApiReference.html | 343 +++++++++++++++ CompilerSupport.html | 205 +++++++++ Contact.html | 103 +++++ DesignDecisionsFAQ.html | 301 ++++++++++++++ ExtendingLimits.html | 148 +++++++ GeneralUnderlyingTypes.html | 183 ++++++++ OptInFeatures.html | 165 ++++++++ Performance.html | 134 ++++++ better-enums.css | 595 +++++++++++++++++++++++++++ demo/BitSets.html | 154 +++++++ demo/C++17Reflection.html | 156 +++++++ demo/C++17ReflectionProposal.html | 224 ++++++++++ demo/NonIntegralUnderlyingTypes.html | 242 +++++++++++ demo/SemiQuine.html | 225 ++++++++++ demo/SpecialValues.html | 220 ++++++++++ image/tweet.png | Bin 0 -> 8250 bytes image/twsupport.png | Bin 0 -> 10206 bytes index.html | 313 ++++++++++++++ sitemap.xml | 64 +++ tutorial/CompileTimeUsage.html | 148 +++++++ tutorial/Conversions.html | 211 ++++++++++ tutorial/HelloWorld.html | 128 ++++++ tutorial/Iteration.html | 142 +++++++ tutorial/Maps.html | 162 ++++++++ tutorial/Representation.html | 168 ++++++++ tutorial/SafeSwitch.html | 134 ++++++ tutorial/ScopeAndSafety.html | 155 +++++++ tutorial/StreamOperators.html | 131 ++++++ 28 files changed, 5154 insertions(+) create mode 100644 ApiReference.html create mode 100644 CompilerSupport.html create mode 100644 Contact.html create mode 100644 DesignDecisionsFAQ.html create mode 100644 ExtendingLimits.html create mode 100644 GeneralUnderlyingTypes.html create mode 100644 OptInFeatures.html create mode 100644 Performance.html create mode 100644 better-enums.css create mode 100644 demo/BitSets.html create mode 100644 demo/C++17Reflection.html create mode 100644 demo/C++17ReflectionProposal.html create mode 100644 demo/NonIntegralUnderlyingTypes.html create mode 100644 demo/SemiQuine.html create mode 100644 demo/SpecialValues.html create mode 100644 image/tweet.png create mode 100644 image/twsupport.png create mode 100644 index.html create mode 100644 sitemap.xml create mode 100644 tutorial/CompileTimeUsage.html create mode 100644 tutorial/Conversions.html create mode 100644 tutorial/HelloWorld.html create mode 100644 tutorial/Iteration.html create mode 100644 tutorial/Maps.html create mode 100644 tutorial/Representation.html create mode 100644 tutorial/SafeSwitch.html create mode 100644 tutorial/ScopeAndSafety.html create mode 100644 tutorial/StreamOperators.html 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 0000000000000000000000000000000000000000..dbbd61d71534b843c997fb1a7a7887cb3cd14e8f GIT binary patch literal 8250 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 literal 0 HcmV?d00001 diff --git a/image/twsupport.png b/image/twsupport.png new file mode 100644 index 0000000000000000000000000000000000000000..ddc4b1f0651f854089d18df92ff224417f247cd5 GIT binary patch literal 10206 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 + + + + + + +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

+ + +
+ + + +
+
+ +
+
+ Copyright © 2015-2019 Anton Bachin. Released under the BSD 2-clause + license. See + + LICENSE. +
+ This page is part of the documentation for Better Enums 0.11.3. +
+
+ + + + 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.

+ + + + + + +
+
+ +
+
+ Copyright © 2015-2019 Anton Bachin. Released under the BSD 2-clause + license. See + + LICENSE. +
+ This page is part of the documentation for Better Enums 0.11.3. +
+
+ + + + 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;
+}
+ + + + + +
+
+ +
+
+ Copyright © 2015-2019 Anton Bachin. Released under the BSD 2-clause + license. See + + LICENSE. +
+ This page is part of the documentation for Better Enums 0.11.3. +
+
+ + + + 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!

+ + + + + + +
+
+ +
+
+ Copyright © 2015-2019 Anton Bachin. Released under the BSD 2-clause + license. See + + LICENSE. +
+ This page is part of the documentation for Better Enums 0.11.3. +
+
+ + + + 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;
+}
+ + + + + +
+
+ +
+
+ Copyright © 2015-2019 Anton Bachin. Released under the BSD 2-clause + license. See + + LICENSE. +
+ This page is part of the documentation for Better Enums 0.11.3. +
+
+ + + + 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.

+ + + + + + +
+
+ +
+
+ Copyright © 2015-2019 Anton Bachin. Released under the BSD 2-clause + license. See + + LICENSE. +
+ This page is part of the documentation for Better Enums 0.11.3. +
+
+ + + + 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. +
+
+ + + + + + +
+
+ +
+
+ Copyright © 2015-2019 Anton Bachin. Released under the BSD 2-clause + license. See + + LICENSE. +
+ This page is part of the documentation for Better Enums 0.11.3. +
+
+ + + + 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;
+}
+ + + + + +
+
+ +
+
+ Copyright © 2015-2019 Anton Bachin. Released under the BSD 2-clause + license. See + + LICENSE. +
+ This page is part of the documentation for Better Enums 0.11.3. +
+
+ + + + 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;
+}
+ + + + + +
+
+ +
+
+ Copyright © 2015-2019 Anton Bachin. Released under the BSD 2-clause + license. See + + LICENSE. +
+ This page is part of the documentation for Better Enums 0.11.3. +
+
+ + + + 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.

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