diff --git a/README.md b/README.md index 6c32337..d098b5e 100644 --- a/README.md +++ b/README.md @@ -43,13 +43,13 @@ The interface is the same for C++98 — you just have to use most of it at run time only. This library does provide scoped and sized enums, something not built into C++98. -See the [project page][project] for full documentation and [here][tutorial] for +See the [project page][project] for full documentation and [here][wandbox] for a simple working program. [max]: http://aantron.github.io/better-enums/demo/BitSets.html [enforce]: http://aantron.github.io/better-enums/demo/SpecialValues.html [project]: http://aantron.github.io/better-enums -[tutorial]: http://aantron.github.io/better-enums/tutorial/HelloWorld.html +[wandbox]: http://melpon.org/wandbox/permlink/pdlAAGoxnjqG6FRI ## Installation @@ -67,14 +67,19 @@ enums that are missing from standard C++. compiler as much as [just including `iostream` does][performance]. - Use any initializers and sparse ranges, just like with a built-in enum. - Guaranteed size and alignment — you choose the representation type. +- Stream operators supported. +- Does not use the heap and can be compiled with exceptions disabled, for use in + minimal freestanding environments. +- The underlying type [can be an object type][underlying]. [performance]: http://aantron.github.io/better-enums/Performance.html +[underlying]: http://aantron.github.io/better-enums/demo/NonIntegralUnderlyingTypes.html ## Limitations -The biggest current limitation is that the `ENUM` macro can't be used inside a -class. This seems difficult to remove, but I am looking into it. In the -meantime, there is a workaround with a `typedef` (or `using`): +The biggest limitation is that the `ENUM` macro can't be used inside a class. +This seems [difficult to remove][nested]. There is a workaround with `typedef` +(or `using`): ```cpp ENUM(UniquePrefix_Color, uint8_t, Red, Green, Blue) @@ -89,6 +94,8 @@ triplet::Color color; You can, however, use `ENUM` inside a namespace. +[nested]: http://aantron.github.io/better-enums/DesignDecisionsFAQ.html#NoEnumInsideClass + ## Contact Don't hesitate to contact me about features or bugs: diff --git a/doc/ApiReference.md b/doc/ApiReference.md index f0ef885..d216667 100644 --- a/doc/ApiReference.md +++ b/doc/ApiReference.md @@ -51,6 +51,15 @@ 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. +If you are using [non-integral underlying types][non-integral], you need to be +aware of section of this reference on underlying types. However, if you are +using a regular, integral underlying type, the type `Enum::_underlying` is the +same `Enum::_integral`, and each of the `*_underlying` functions is the same as +the corresponding `*_integral` function, so you can safely ignore that whole +section. + +[non-integral]: ${prefix}demo/NonIntegralUnderlyingTypes.html + ### Running example @@ -150,9 +159,14 @@ using optional = better_enums::optional<Enum>; The types and members described here have to do with the sequence of constants declared, i.e. `A`, `B`, `C` in the [running example](#RunningExample). -#### static constexpr size_t _size +#### static constexpr size_t _size() -The number of constants declared. `Enum::_size == 3`. +The number of constants declared. `Enum::_size() == 3`. + +#### static constexpr const size_t _size_constant + +Same as [`_size`](#_size), but a constant instead of a function. This is +provided for use in $cxx98 constant expressions. #### typedef _value_iterable @@ -255,7 +269,7 @@ case as in [`_from_string_nocase`](#_from_string_nocase). Evaluates to the name of the Better Enum type. `Enum::_name()` is the same string as `"Enum"`. -#### typedef _name_iterable +#### 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 @@ -263,7 +277,7 @@ available, but is `constexpr` if and only if [`_to_string`](#_to_string) is `constexpr`. Iteration visits constants in order of declaration. See usage example under [`_names`](#_names). -#### typedef _name_iterator +#### typedef _name_iterator Random-access iterator type for `_name_iterable`. Most operations are `constexpr`, but dereferencing is `constexpr` if and only if @@ -322,7 +336,7 @@ example, (+Enum::C)._to_integral() == 2 Note that Better Enums are already implicitly convertible to their underlying -integral types [by default](${prefix}OptInFeatures.html#StrictConversion). +integral types [by default](${prefix}OptInFeatures.html#StrictConversions). 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. @@ -365,6 +379,151 @@ constants. +### 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`](#_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`](#_from_string). In case of failure, sets the stream's +`failbit`. + + + +### Non-integral underlying type + +This section is relevant only if you are using an underlying type that is not +an integral type — otherwise, `Enum::_underlying` is the same as +`Enum::_integral`, and all the functions described here are redundant with their +corresponding functions in the [section on integer conversions][integral]. + +That section is written for the simple, but common case where the underlying +type is an integral type, in order to avoid overloading users not using the +feature described here with unnecessary generality. The information in that +section is fully accurate for integral underlying types, but for non-integral +underlying types this section corrects it. + +[integral]: #IntegerConversion + +The rest of this section will assume that your non-integral underlying type is +called `Underlying`. + +In this case, the memory representation of your Better Enum type is the same as +for `Underlying`. In fact, this is always true — the memory representation +is always the same as for the underlying type. It is only a matter of whether +that type is integral or not. + +When `Underlying` is not integral, Better Enums still needs an integral +representation of `Underlying` for use in `switch`. That is the true meaning of +the member type `_integral`. It's just that when `Underlying` *is* integral to +begin with, it is its own integral representation, and the two types collapse. + +To support non-integral underlying types, Better Enums requires a two-way +mapping between `Underlying` and some type `_integral`. In case `Underlying` +*is* integral, however, that mapping is simply the identity function. Otherwise, +you have to supply a mapping as shown [here][non-integral]. + +In short, the underlying type is "first-class," whether it is integral or not, +and the type `_integral` is a helper type. When `Underlying` *is* integral, the +various `*_integral` functions just happen to work with the underlying type, as +a special case. The material in this section is for the general case where +`Underlying` is not integral. + +#### typename _underlying + +`Enum::_underlying` is the same type as `Underlying`. It has to satisfy the +requirements given [here][non-integral]. + +#### non-member specialization struct better_enums::integral_mapping<Underlying> + +You should specialize this template for `Underlying`, as shown in the +[example][non-integral]. The specialization needs the following members: + +- A type `integral_representation`, which gives an integral type that Better + Enums will use to make `Underlying` compatible with `switch` statements, and + to define an ordering on the generated Better Enums type. This type is *not* + the internal representation of the Better Enum — the Better Enum's + memory representation is the same as `Underlying`. +- A function + `constexpr static integral_representation to_integral(const Underlying&)`. +- A function + `constexpr static Underlying from_integral(integral_representation)`. + +In $cxx98, the above functions don't have to be `constexpr`. + +You can avoid specializing this template, but its default implementation puts +additional requirements on `Underlying` in order to be able to define default +versions of `to_integral` and `from_integral`: + +- `Underlying` must have a member type `integral_representation`, with the same + meaning as above. +- `Underlying` must have a conversion + `constexpr operator integral_representation() const`. +- `Underlying` must have a constructor + `constexpr Underlying(integral_representation)`. This constructor can be + explicit. + +Again, in $cxx98, these members don't have to be `constexpr`. + +#### member constexpr _underlying _to_underlying() const + +No-op conversion of a Better Enum to its underlying type. Behaves as +[`_to_integral`](#_to_integral), except that the text concerning implicit +conversions is irrelevant when `_underlying` is not the same as `_integral`. +Implicit conversions, if not disabled, are always to `_integral`. + +#### static constexpr Enum _from_underlying(_underlying) + +Same as [`_from_integral`](#_from_integral), but for the underlying type. In +fact, `from_integral` is a wrapper that first converts the integer to a value of +the underlying type (a no-op when the types are equal), and then calls +`_from_underlying`. + +#### static constexpr optional _from_underlying_nothrow(_underlying) + +Same as [`_from_integral_nothrow`](#_from_integral_nothrow), but for the +underlying type. `_from_integral_nothrow` is a wrapper as described +[above](#_from_underlying). + +#### static constexpr Enum _from_underlying_unchecked(_underlying) + +Same as [`_from_integral_unchecked`](#_from_integral), but for the underlying +type. `_from_integral_unchecked` is a wrapper as described +[above](#_from_underlying). + +#### static constexpr bool _is_valid(_underlying) + +Replaces [`_is_valid(_integral)`](#_is_valid_integral). In fact, *this* function +is the only one defined, but in the case where `_integral` is `_underlying`, +this function's signature is equivalent to +[`_is_valid(_integral)`](#_is_valid_integral). + +#### static constexpr _value_iterable _values — _underlying[]() + +Collection of declared enum values, stored in memory as instances of the +underlying type. + +Replaces [`_values`](#_values), the collection of integral values of declared +constants. In fact, this is the only member defined — in the case where +`_integral` is the same as `_underlying`, the definition in the section on +integer conversions is equivalent to this one. + +#### member constexpr const _underlying& operator *() const + +Returns a reference to the wrapped underlying value. There is also a non-`const` +version. + +#### member constexpr const _underlying* operator ->() const + +Returns a pointer to the wrapped underlying value that is suitable for member +access, if `_underlying` has members. + + + %% class = api %% description = Detailed description of the Better Enums API. diff --git a/doc/CompilerSupport.md b/doc/CompilerSupport.md index 4d0fe6d..6f4c35d 100644 --- a/doc/CompilerSupport.md +++ b/doc/CompilerSupport.md @@ -1,7 +1,6 @@ ## Compiler support -Better Enums aims to support all major compilers. It is known to definitely work -on +Better Enums aims to support all major compilers. It is known to work on: - clang 3.3 to 3.6 - gcc 4.3 to 5.1 @@ -121,4 +120,6 @@ g++43 -std=c++0x -DBETTER_ENUMS_NO_CONSTEXPR g++43 -std=c++98 ~~~ -%% description = Information about compiler support and feature detection. +%% description = +Better Enums compiler support, compatibility, feature detection, and automated +testing. diff --git a/doc/Contact.md b/doc/Contact.md index 043802b..3c626f9 100644 --- a/doc/Contact.md +++ b/doc/Contact.md @@ -8,9 +8,11 @@ I also watch the `enums` tag on Stack Overflow. I'm happy to hear any feedback. If you have any trouble using the library or -parsing the documentation, please don't hesitate to let me know. +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. It will help keep me encouraged :) -%% description = Contact information for bugs, issues, support, and feedback. +%% description = +Contact information for Better Enums bugs, issues, support, and feedback. diff --git a/doc/DesignDecisionsFAQ.md b/doc/DesignDecisionsFAQ.md new file mode 100644 index 0000000..188b423 --- /dev/null +++ b/doc/DesignDecisionsFAQ.md @@ -0,0 +1,250 @@ +## Design decisions FAQ + +$be pushes at the edges of what is possible in standard $cxx, 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 $be, 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][contact]. + +$internal_toc + +### 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: + + 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 $cxx98 usage. + +This leaves the possibility of moving the operations performed by the members +into traits types, i.e. something like `traits::valid`. That is an +interesting option, and it has [its own section][traits]. I have tried it, but +the verbosity increase is much greater than the benefit of dropping underscores, +so I chose not to do it. + +%% description = Better Enums design decisions and tradeoffs. + +### 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 $cxx 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 $cxx11 `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 + + 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 `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 `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. These arrays need to be accessible to `constexpr` code in $cxx11, and + `constexpr` functions are not allowed to have static local variables. + +Ironically, this seems like one place where $cxx98 is more "flexible," but only +for the reason that compile-time usage of Better Enums is not supported in +$cxx98. + + +### 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, + + 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: + + ENUM(Channel, int, Red, Green, Blue) + +generates + + enum class Channel : int { Red, Green, Blue }; + + template <> + struct ::better_enums::traits { + 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][implicit]. + +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::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::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`, 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][infer], but that will not be suitable + for all contexts, and the user may be surprised by ambiguous resolution error + messages when it is not. +- The experimental feature presented [here][underlying] would be questionable in + the traits interpretation. It is still possible to have a non-integral + underlying type with traits, but it would be strange if the traits version of + the macro accepted a non-integral underlying type, and then declared the + actual language enum with an integral underlying type. Even though that is + exactly what the non-traits version does, there the language enum is hidden + inside the generated type, instead of being exposed alongside a traits type. +- Scoped constants are lost for $cxx98 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 are not intuitive for some $cxx users, which would present a + barrier to usage. + +Despite the disadvantages listed just above, I consider the traits approach +interesting — it's a close call. There is an +[out-of-date branch][traits-branch] containing a traits version of Better Enums. +You can see some of the usage in its [samples][traits-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 $cxx14. 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. + +[contact]: ${prefix}Contact.html +[traits]: #Traits +[implicit]: ${prefix}OptInFeatures.html#StrictConversions +[infer]: ${prefix}demo/SpecialValues.html +[underlying]: ${prefix}demo/NonIntegralUnderlyingTypes.html +[traits-branch]: $repo/tree/traits +[traits-samples]: $repo/tree/traits/samples diff --git a/doc/ExtendingLimits.md b/doc/ExtendingLimits.md index d1e8950..a990250 100644 --- a/doc/ExtendingLimits.md +++ b/doc/ExtendingLimits.md @@ -19,7 +19,7 @@ constants of full-`constexpr` enums. To extend: example. Add 1 to the number of characters to account for the null terminator — our numbers are now 512 and 64. 2. Get `make_macros.py` from your copy of the full Better Enums distribution - or from GitHub. + or from GitHub. 3. You will run this script to generate a header file containing some replacement macros for `enum.h` to use. Pick a name for this file and a location somewhere in your include path. I will assume that this file is @@ -31,10 +31,27 @@ constants of full-`constexpr` enums. To extend: - For g++ and clang++, `-DBETTER_ENUMS_MACRO_FILE=''` - For VC++, `\DBETTER_ENUMS_MACRO_FILE=''` + - With CMake, you may need something like + `add_definitions(-DBETTER_ENUMS_MACRO_FILE="$${CMAKE_SOURCE_DIR}/src/enum-macros.h")` 6. Enjoy the looser limits. Just watch out — increasing the second number can really slow down compilation of full-`constexpr` enums. 7. You don't need `make_macros.py` anymore. It's not part of your build process and you can delete it. -%% description = How to extend limits imposed by internal macros. +--- + +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][vc] + (127). + +[vc]: https://msdn.microsoft.com/en-us/library/ft39hh4x.aspx + +%% description = +How to extend limits imposed by internal macros in Better Enums. diff --git a/doc/GeneralUnderlyingTypes.md b/doc/GeneralUnderlyingTypes.md new file mode 100644 index 0000000..f1bf89c --- /dev/null +++ b/doc/GeneralUnderlyingTypes.md @@ -0,0 +1,114 @@ +## 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 $cxx98, +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 { + 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 $cxx98, 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][test]. 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. + +[test]: $repo/blob/master/test/cxxtest/underlying.h + +### 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. + +%% description = "Using Better Enums with non-integral underlying types." diff --git a/doc/OptInFeatures.md b/doc/OptInFeatures.md index 8f24518..9710b78 100644 --- a/doc/OptInFeatures.md +++ b/doc/OptInFeatures.md @@ -64,5 +64,5 @@ Here they are: case +Channel::Blue: break; } -%% description = Opting into features disabled by default for performance or -compatibility reasons. +%% description = Optional Better Enums features, disabled by default for +performance or compatibility reasons. diff --git a/doc/Performance.md b/doc/Performance.md index d827810..e6ad31f 100644 --- a/doc/Performance.md +++ b/doc/Performance.md @@ -7,9 +7,9 @@ to get a rough idea of how long it takes to compile Better Enums. The files compared in the test are as follows: - - [One file]($repo/blob/$version/test/performance/4-declare_enums.cc) includes + - [One file]($repo/blob/$ref/test/performance/4-declare_enums.cc) includes `enum.h` and declares 647 constants across 36 Better Enums. - - The [other file]($repo/blob/$version/test/performance/5-iostream.cc) *only* + - The [other file]($repo/blob/$ref/test/performance/5-iostream.cc) *only* includes `iostream` and does nothing with it. The argument is that if compiling a bunch of Better Enums is faster, or about as @@ -29,4 +29,22 @@ compiled faster. - gcc 5.1, full `constexpr`: 4.23 - VC2015RC, $cxx98: 1.18 -%% description = Compilation performance testing results. +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. + +%% description = Better Enums compilation speed and performance testing results. diff --git a/doc/better-enums.css b/doc/better-enums.css index 721d361..d585746 100644 --- a/doc/better-enums.css +++ b/doc/better-enums.css @@ -11,6 +11,10 @@ pre, code, samp, h4, .contents ul { 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; @@ -18,6 +22,8 @@ pre { overflow: scroll; color: rgba(255, 255, 255, 0.6); font-size: 78%; + max-width: 84ex; + margin-left: 1em; } pre em { @@ -45,21 +51,23 @@ code, samp { } .container { - max-width: 760px; - margin-left: 230px; + margin-left: 150px; + margin-right: 150px; } -@media (max-width: 1220px) { +@media (max-width: 1400px) { .container { margin-left: auto; margin-right: auto; + width: 1100px; } } -@media (max-width: 780px) { +@media (max-width: 1120px) { .container { margin-left: 10px; margin-right: 10px; + width: auto; } } @@ -79,6 +87,17 @@ nav, .spacer { 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; } @@ -109,7 +128,33 @@ header { h1 { margin: 0; font-size: 60px; - font-weight: 100; + 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 { @@ -129,10 +174,41 @@ header h3 { 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: 100; + font-weight: 300; } hr { @@ -142,9 +218,9 @@ hr { footer { font-size: 14px; - margin-top: 150px; + margin-top: 100px; margin-bottom: 20px; - opacity: 0.6; + opacity: 0.5; } a { @@ -215,7 +291,6 @@ span#note:target { .main h3 { font-size: 30px; - font-weight: 100; margin-top: 2em; color: black; } @@ -288,15 +363,45 @@ li { } .blurbs > li { - width: 45%; float: left; min-height: 5em; } -.blurbs > li.even { - clear: both; - margin-left: 3%; - margin-right: 6%; +@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 { @@ -311,19 +416,6 @@ li { font-style: normal; } -@media (max-width: 620px) { - .blurbs > li { - float: none; - width: 100%; - min-height: 3em; - } - - .blurbs > li.even { - margin-left: 0; - margin-right: 0; - } -} - .act strong { font-weight: bold; font-size: 120%; @@ -342,9 +434,25 @@ li { 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; - margin-left: -10%; white-space: nowrap; } @@ -354,12 +462,22 @@ li { .splash pre.left { text-align: right; - color: inherit; + 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) { @@ -385,6 +503,7 @@ h4 { color: #888; padding-top: 1em; white-space: nowrap; + font-size: 125%; } h4 em { @@ -394,9 +513,9 @@ h4 em { } .api ul.contents { - -webkit-columns: 300px 2; - -moz-columns: 300px 2; - columns: 300px 2; + -webkit-columns: 300px 3; + -moz-columns: 300px 3; + columns: 300px 3; } .api ul.contents > li { @@ -407,6 +526,7 @@ h4 em { .main h3 { margin-top: 4em; + clear: both; } h3.contents { diff --git a/doc/demo/101-special-values.md b/doc/demo/101-special-values.md index 169ef1c..9611bd2 100644 --- a/doc/demo/101-special-values.md +++ b/doc/demo/101-special-values.md @@ -170,5 +170,6 @@ There are many possible variations of these policies, but I think most of them can be encoded in a reasonable fashion using the tools Better Enums provides. Enjoy! -%% description = Encoding project policies for static enforcement using Better -Enums. +%% description = An example that uses Better Enums compile-time reflection to +create invalid and default values for each enum, enforced statically by the +compiler, for readability and maintainability. diff --git a/doc/demo/102-any-underlying.md b/doc/demo/102-any-underlying.md new file mode 100644 index 0000000..46f8efc --- /dev/null +++ b/doc/demo/102-any-underlying.md @@ -0,0 +1,161 @@ +## 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 $cxx98 +— though then, of course, `T` doesn't have to be literal and the mapping +doesn't have to be `constexpr`. In $cxx98, everything involving `T` will simply +be done by Better Enums at run time. + +Here's how to do it. + + #include + #include + typedef unsigned char uint8_t; // 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 { ... + namespace better_enums { + + template <> + struct integral_mapping { + 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: + +~~~comment +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 { + 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: + +~~~comment +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 $cxx11. 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: + +~~~comment +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. + + + +%% description = "Using Better Enums with non-integral underlying types." diff --git a/doc/demo/102-bitset.md b/doc/demo/103-bitset.md similarity index 95% rename from doc/demo/102-bitset.md rename to doc/demo/103-bitset.md index 87b5456..357b02f 100644 --- a/doc/demo/102-bitset.md +++ b/doc/demo/103-bitset.md @@ -65,5 +65,5 @@ static_assert(max()._to_integral() < 32, "some bit indices are out of range"); ~~~ -%% description = Finding the maximum value of a Better Enum constant for use in -declaring bit set types. +%% description = Finding the maximum value of a Better Enum for use in declaring +statically-sized bit set types. diff --git a/doc/demo/103-quine.md b/doc/demo/104-quine.md similarity index 97% rename from doc/demo/103-quine.md rename to doc/demo/104-quine.md index 90a474e..ae64482 100644 --- a/doc/demo/103-quine.md +++ b/doc/demo/104-quine.md @@ -170,4 +170,6 @@ ENUM(Channel, int, Red = 0, Green = 1, Blue = 2) ENUM(Depth, int, TrueColor = 1, HighColor = 0) ~~~ -%% description = Contrived example that shows static memory allocation. +%% description = Have a Better Enum print its own definition. Shows how to +compute the amount of memory necessary from the reflective information provided +by a Better Enum. diff --git a/doc/docs.py b/doc/docs.py index dae8fcf..8f015f0 100755 --- a/doc/docs.py +++ b/doc/docs.py @@ -87,11 +87,17 @@ def compose_page(relative_path, definitions): definitions["class"] = "" text = templates["page"] - text = scrub_comments(text) - while '$' in text: - text = apply_template(text, definitions) - text = scrub_comments(text) + while True: + new_text = scrub_comments(text) + new_text = re.sub("\$\$", "$$$$", new_text) + new_text = apply_template(new_text, definitions) + + if new_text == text: + text = apply_template(new_text, definitions) + break + + text = new_text text = "\n\n" + text @@ -143,7 +149,7 @@ def process_threaded(directory): source_file = \ os.path.splitext(os.path.basename(file))[0] + "." + CXX_EXTENSION - source_link = "$repo/blob/$version/example/" + source_file + source_link = "$repo/blob/$ref/example/" + source_file definitions[directory + "_body"] = definitions["body"] definitions["body"] = templates[directory] @@ -183,6 +189,10 @@ def generate_sitemap(): for url in generated: text += " \n" text += " %s\n" % url + + if ".html" not in url: + text += " 1.0\n" + text += " \n" text += "\n" diff --git a/doc/index.md b/doc/index.md index f539ace..9bdb7e8 100644 --- a/doc/index.md +++ b/doc/index.md @@ -1,9 +1,3 @@ -Better Enums is a single header file that causes your compiler to generate -*reflective* enum types. This makes it easy to translate between enums and -strings, and much more. - -Here's how to use a Better Enum: -
enable
 
@@ -11,7 +5,7 @@ declare
 
 
 parse
-print
+format
 
 
 count
@@ -19,6 +13,7 @@ iterate
 
 
 
+
 switch
 
 
@@ -29,22 +24,25 @@ switch
 safe cast
 
 
-at compile time
-
#include <enum.h>
+during
+compilation
+
+
#include <enum.h>
 
-ENUM(Channel, int, Red = 1, Green, Blue)
+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);
+size_t      n = Channel::_size();
+for (Channel c : Channel::_values()) {
+    run_some_function(c);
+}
 
 
-switch (c) {
+switch (c) {
     case Channel::Red:    // ...
     case Channel::Green:  // ...
     case Channel::Blue:   // ...
@@ -54,35 +52,60 @@ size_t      n = Channel::_size;
 Channel     c = Channel::_from_integral(3);
 
 
-constexpr Channel c = Channel::_from_string("Blue");
+constexpr Channel c = + Channel::_from_string("Blue");
-Jump to the [tutorial](${prefix}tutorial/HelloWorld.html) to see a complete -working example. +

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

-### What do you get? +That means you can easily convert enums to and from strings, +validate them, and loop over them. In $cxx11, 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. + +All you have to do to use it is include enum.h. + +Try it live online in +[Wandbox](http://melpon.org/wandbox/permlink/pdlAAGoxnjqG6FRI), or begin the +[tutorial](${prefix}tutorial/HelloWorld.html)! + +
+ +### Features
    -
  • - Uniform interface for $cxx98 and $cxx11 - Scoped, sized, reflective enums for $cxx98. -
  • -
  • - Compile-time reflection +
  • + Unobtrusive syntax - Have the compiler do additional enum processing using your own - templates or constexpr functions. + No ugly macros. Use initializers as with built-in enums. + Internal members have underscores to avoid clashing with your constant + names.
  • -
  • - Non-contiguous sequences +
  • + No external dependencies - Iteration and count much easier to maintain than with an extra "count" - constant and making assumptions. + Uses only standard $cxx. 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 @@ -90,15 +113,7 @@ working example.
  • -
  • - Unobtrusive syntax - - No ugly macros. Use initializers just like with built-in - enums. Generated members have underscores to avoid conflicts - with your constant names. - -
  • -
  • +
  • Don't repeat yourself No more unmaintanable maps or switch statements for @@ -106,29 +121,56 @@ working example.
  • -
  • - No build-time generator needed +
  • + Non-contiguous sequences - Just include enum.h. It's a metaprogram executed by your - compiler. - -
  • -
  • - Fast compilation - - Much less impact on build time than even just including - iostream. + Iteration and counting are much easier to maintain than with an extra + Count constant and assuming a dense range.
  • -
  • - No external dependencies +
  • + Fast compilation - Uses standard $cxx and supported on major compilers. Installation is - simple — just download enum.h. + 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 $cxx98, $cxx11 + + Scoped, sized, reflective enums for $cxx98, and an easy upgrade + path. + +
  • + +
  • + Stream operators + + Write enum names directly to std::cout or use + boost::lexical_cast. + +
  • + +
  • + Non-integral underlying types + + Have sets of named, switch-friendly constants of any literal + type. + +
  • + +
  • Free and open source Released under the BSD license for use in any project, free or commercial. @@ -138,40 +180,18 @@ working example.
    -### It's what built-in enums ought to do. - -The library notionally extends $cxx, adding oft-needed features. - -
      -
    • - Download enum.h - - Current version: $version
      - To install, just add the file to your project. -
      -
    • -
    • - Visit on GitHub - - Follow development, report issues, and let me know if you find this - library useful! - -
    • -
    - -
    - -### Resources +### Documentation
      -
    • +
    • Tutorial
        $tutorial_toc
    • -
    • + +
    • Reference
    • -
    • +
    • - Compile-time demos + Advanced
        $demo_toc
      @@ -195,8 +218,9 @@ The library notionally extends $cxx, adding oft-needed features. %% title = Clean reflective enums for C++ -%% description = Reflective enums for C++ with clean syntax, in a header-only -library. Can be converted to strings, iterated, counted, and used for -metaprogramming. Free under the BSD license. +%% description = Better Enums is a single header C++ library providing +reflective enums with clean syntax. Better Enums can be converted to and from +strings, be iterated, counted, and used at run time or for template and +constexpr metaprogramming. Free and open source under the BSD license. %% class = index diff --git a/doc/template/be.html b/doc/template/be.tmpl similarity index 100% rename from doc/template/be.html rename to doc/template/be.tmpl diff --git a/doc/template/cxx14.tmpl b/doc/template/cxx14.tmpl new file mode 100644 index 0000000..896f251 --- /dev/null +++ b/doc/template/cxx14.tmpl @@ -0,0 +1 @@ +C++14 \ No newline at end of file diff --git a/doc/template/demo.tmpl b/doc/template/demo.tmpl index f87df8a..56220fa 100644 --- a/doc/template/demo.tmpl +++ b/doc/template/demo.tmpl @@ -1,7 +1,8 @@

      - This page is an advanced demo showing the kind of compile-time code you can - write on top of Better Enums. You can download it and - try it out. + 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.

      $demo_body diff --git a/doc/template/download.tmpl b/doc/template/download.tmpl index 76cc260..46e6804 100644 --- a/doc/template/download.tmpl +++ b/doc/template/download.tmpl @@ -1,2 +1,2 @@ -href="https://raw.githubusercontent.com/aantron/better-enums/$version/enum.h" +href="https://raw.githubusercontent.com/aantron/better-enums/$ref/enum.h" download \ No newline at end of file diff --git a/doc/template/header.tmpl b/doc/template/header.tmpl index 1630395..ac19494 100644 --- a/doc/template/header.tmpl +++ b/doc/template/header.tmpl @@ -31,11 +31,25 @@
      -
      {}
      +
      +

      Better Enums

      +

      Reflective compile-time enums for $cxx

      +

      Open-source under the BSD license

      +
      -

      Better Enums

      -

      Reflective compile-time enums for $cxx

      -

      Open-source under the BSD license

      +
      +

      Version $version

      +

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

      +

      + Visit the GitHub repo for issues, feedback, and let me know if you + found this library useful! +

      +
      + +
      + Download enum.h + GitHub +
      diff --git a/doc/template/ref.tmpl b/doc/template/ref.tmpl new file mode 100644 index 0000000..2774f85 --- /dev/null +++ b/doc/template/ref.tmpl @@ -0,0 +1 @@ +0.10.0 \ No newline at end of file diff --git a/doc/template/tutorial.tmpl b/doc/template/tutorial.tmpl index 3c1a07f..0a72c71 100644 --- a/doc/template/tutorial.tmpl +++ b/doc/template/tutorial.tmpl @@ -1,6 +1,7 @@

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

      $tutorial_body diff --git a/doc/template/version.tmpl b/doc/template/version.tmpl index 899f24f..2774f85 100644 --- a/doc/template/version.tmpl +++ b/doc/template/version.tmpl @@ -1 +1 @@ -0.9.0 \ No newline at end of file +0.10.0 \ No newline at end of file diff --git a/doc/tutorial/1-hello-world.md b/doc/tutorial/1-hello-world.md index 0cb72b5..a7b915d 100644 --- a/doc/tutorial/1-hello-world.md +++ b/doc/tutorial/1-hello-world.md @@ -1,6 +1,6 @@ ## Hello, World! -Download enum.h, then build this program with it: +Download enum.h, then compile this program: #include #include "enum.h" @@ -20,4 +20,5 @@ Run it, and you should see the output "Hello, World!" Congratulations, you have just created your first Better Enum! -%% description = Introductory Better Enums tutorial. +%% description = Introductory Better Enums tutorial - a simple, but complete, +Hello World program. diff --git a/doc/tutorial/2-conversions.md b/doc/tutorial/2-conversions.md index 42d4d41..47be581 100644 --- a/doc/tutorial/2-conversions.md +++ b/doc/tutorial/2-conversions.md @@ -149,4 +149,6 @@ reference has a return 0; } -%% description = Walkthrough of Better Enums conversion functions. +%% description = Better Enums conversion functions. Converting to string, from +string, to int, from int, and validation, both case-sensitive and +case-insensitive. Exception-throwing and non-throwing variants presented. diff --git a/doc/tutorial/3-iterate.md b/doc/tutorial/3-iterate.md index 093f9d8..66db78a 100644 --- a/doc/tutorial/3-iterate.md +++ b/doc/tutorial/3-iterate.md @@ -46,4 +46,5 @@ If you are using $cxx11, you can have much nicer syntax: return 0; } -%% description = Iterating over all Better Enums constants. +%% description = Using Better Enums to iterate over all the constants of an +enum, as well as over its names. Also shows the same with C++11 for-each syntax. diff --git a/doc/tutorial/4-switch.md b/doc/tutorial/4-switch.md index 2d0347a..d6808be 100644 --- a/doc/tutorial/4-switch.md +++ b/doc/tutorial/4-switch.md @@ -27,4 +27,6 @@ you a warning — try it! return 0; } -%% description = Usage in switch statements. +%% description = Better Enums can be used directly in switch statements like +normal enums, making it possible for the compiler to check that all cases are +listed, increasing the safety of your code. diff --git a/doc/tutorial/5-iostreams.md b/doc/tutorial/5-iostreams.md new file mode 100644 index 0000000..5f53cf4 --- /dev/null +++ b/doc/tutorial/5-iostreams.md @@ -0,0 +1,34 @@ +## 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 + #include + + 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 $cxx98 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: + +~~~comment +Channel channel = Channel::Blue; +std::cin >> channel; // Expects input such as "Green". +~~~ + +--- + +Only `char` streams are supported for the time being. + +%% description = Using Better Enums with stream input and output operators. diff --git a/doc/tutorial/5-safety.md b/doc/tutorial/6-safety.md similarity index 97% rename from doc/tutorial/5-safety.md rename to doc/tutorial/6-safety.md index cd96b18..ada4f1b 100644 --- a/doc/tutorial/5-safety.md +++ b/doc/tutorial/6-safety.md @@ -76,4 +76,4 @@ the one in that demo. return 0; } -%% description = Type safety features and limitations. +%% description = Better Enums type safety features and limitations. diff --git a/doc/tutorial/6-representation.md b/doc/tutorial/7-representation.md similarity index 96% rename from doc/tutorial/6-representation.md rename to doc/tutorial/7-representation.md index c5ea49d..d973959 100644 --- a/doc/tutorial/6-representation.md +++ b/doc/tutorial/7-representation.md @@ -80,4 +80,5 @@ types containg enums. The enums will behave as expected. compatible with $cxx98, where those names aren't available in a portable manner. -%% description = Underlying representation. +%% description = The underlying memory representation of a Better Enum, +including size and alignment. diff --git a/doc/tutorial/7-constexpr.md b/doc/tutorial/8-constexpr.md similarity index 92% rename from doc/tutorial/7-constexpr.md rename to doc/tutorial/8-constexpr.md index c913900..bd9fbcf 100644 --- a/doc/tutorial/7-constexpr.md +++ b/doc/tutorial/8-constexpr.md @@ -48,4 +48,5 @@ You can also do things such as: Which prints "5", the length of "Green". That 5 was also computed during compilation. -%% description = Introduction to compile-time conversions. +%% description = Better Enums can be used entirely at compile time in C++11. All +conversion functions are available for constexpr functions or templates. diff --git a/example/102-any-underlying.cc b/example/102-any-underlying.cc new file mode 100644 index 0000000..5bb8761 --- /dev/null +++ b/example/102-any-underlying.cc @@ -0,0 +1,153 @@ +// This file was generated automatically. + +// 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. +// +// Here's how to do it. + +#include +#include +typedef unsigned char uint8_t; // 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 { ... +namespace better_enums { + +template <> +struct integral_mapping { + 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 { +// 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. +// +// 1. 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. +// 2. 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/example/102-bitset.cc b/example/103-bitset.cc similarity index 100% rename from example/102-bitset.cc rename to example/103-bitset.cc diff --git a/example/103-quine.cc b/example/104-quine.cc similarity index 100% rename from example/103-quine.cc rename to example/104-quine.cc diff --git a/example/5-iostreams.cc b/example/5-iostreams.cc new file mode 100644 index 0000000..2d108f0 --- /dev/null +++ b/example/5-iostreams.cc @@ -0,0 +1,29 @@ +// This file was generated automatically. + +// Stream operators +// +// These work almost as you'd expect. Just make sure you include iostream before +// enum.h in any translation unit in which you intend to use the operators. + +#include +#include + +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. diff --git a/example/5-safety.cc b/example/6-safety.cc similarity index 100% rename from example/5-safety.cc rename to example/6-safety.cc diff --git a/example/6-representation.cc b/example/7-representation.cc similarity index 100% rename from example/6-representation.cc rename to example/7-representation.cc diff --git a/example/7-constexpr.cc b/example/8-constexpr.cc similarity index 100% rename from example/7-constexpr.cc rename to example/8-constexpr.cc diff --git a/test/expect/102-any-underlying b/test/expect/102-any-underlying new file mode 100644 index 0000000..9af72b8 --- /dev/null +++ b/test/expect/102-any-underlying @@ -0,0 +1,4 @@ +Red component: c4 +Green component: 74 +Blue component: 51 +darksalmon diff --git a/test/expect/102-bitset b/test/expect/103-bitset similarity index 100% rename from test/expect/102-bitset rename to test/expect/103-bitset diff --git a/test/expect/103-quine b/test/expect/104-quine similarity index 100% rename from test/expect/103-quine rename to test/expect/104-quine diff --git a/test/expect/5-iostreams b/test/expect/5-iostreams new file mode 100644 index 0000000..d30c108 --- /dev/null +++ b/test/expect/5-iostreams @@ -0,0 +1 @@ +Red diff --git a/test/expect/5-safety b/test/expect/6-safety similarity index 100% rename from test/expect/5-safety rename to test/expect/6-safety diff --git a/test/expect/6-representation b/test/expect/7-representation similarity index 100% rename from test/expect/6-representation rename to test/expect/7-representation diff --git a/test/expect/7-constexpr b/test/expect/8-constexpr similarity index 100% rename from test/expect/7-constexpr rename to test/expect/8-constexpr diff --git a/test/test.py b/test/test.py index 72f4f30..8de1280 100755 --- a/test/test.py +++ b/test/test.py @@ -152,8 +152,11 @@ class Configuration(object): -skip_cxx98 = ["101-special-values", "102-bitset", "103-quine", "7-constexpr"] -skip_strict = ["4-switch"] +skip_cxx98 = [ + "101-special-values", "102-any-underlying", "103-bitset", "104-quine", + "8-constexpr" +] +skip_strict = ["4-switch", "102-any-underlying"] def modern_gnu(command): return [