├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── appveyor.yml ├── doc ├── ApiReference.md ├── CONTRIBUTING.md ├── CONTRIBUTORS ├── CompilerSupport.md ├── DesignDecisionsFAQ.md ├── ExtendingLimits.md ├── Makefile ├── OptInFeatures.md ├── Performance.md ├── better-enums.css ├── demo │ ├── 101-special-values.md │ ├── 103-bitset.md │ ├── 104-quine.md │ └── 105-c++17-reflection.md ├── docs.py ├── image │ └── sample.gif ├── index.md ├── mistune.py ├── template │ ├── be.tmpl │ ├── cxx.tmpl │ ├── cxx11.tmpl │ ├── cxx14.tmpl │ ├── cxx17.tmpl │ ├── cxx98.tmpl │ ├── demo.tmpl │ ├── download.tmpl │ ├── footer.tmpl │ ├── ga.tmpl │ ├── ghfork.tmpl │ ├── ghstar.tmpl │ ├── ghwatch.tmpl │ ├── header.tmpl │ ├── last.tmpl │ ├── location.tmpl │ ├── next.tmpl │ ├── page.tmpl │ ├── project.tmpl │ ├── ref.tmpl │ ├── repo.tmpl │ ├── tocitem.tmpl │ ├── tutorial.tmpl │ └── version.tmpl ├── transform.py └── tutorial │ ├── 1-hello-world.md │ ├── 2-conversions.md │ ├── 3-iterate.md │ ├── 4-switch.md │ ├── 5-map.md │ ├── 6-iostreams.md │ ├── 7-safety.md │ ├── 8-representation.md │ └── 9-constexpr.md ├── enum.h ├── example ├── 1-hello-world.cc ├── 101-special-values.cc ├── 103-bitset.cc ├── 104-quine.cc ├── 105-c++17-reflection.cc ├── 2-conversions.cc ├── 3-iterate.cc ├── 4-switch.cc ├── 5-map.cc ├── 6-iostreams.cc ├── 7-safety.cc ├── 8-representation.cc ├── 9-constexpr.cc └── Makefile ├── extra └── better-enums │ └── n4428.h ├── script └── make_macros.py └── test ├── CMakeLists.txt ├── Makefile ├── cxxtest ├── general.h └── stream.h ├── expect ├── 1-hello-world ├── 101-special-values ├── 102-any-underlying ├── 103-bitset ├── 104-quine ├── 105-c++17-reflection ├── 2-conversions ├── 3-iterate ├── 4-switch ├── 5-map ├── 6-iostreams ├── 7-safety ├── 8-representation └── 9-constexpr ├── linking ├── helper.cc ├── helper.h ├── main.cc └── shared.h └── performance ├── 1-simple.cc ├── 2-include_empty.cc ├── 3-only_include_enum.cc ├── 4-declare_enums.cc ├── 5-iostream.cc └── empty.h /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: aantron 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.obj 3 | *.exe 4 | scratch/ 5 | doc/html/ 6 | doc-publish/ 7 | test/cxxtest/*.cc 8 | test/build 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | env: 4 | - COMPILER="clang++-3.7" 5 | - COMPILER="g++-5" 6 | - COMPILER="g++-4.6" 7 | - COMPILER="g++-4.7" 8 | - COMPILER="g++-4.4" 9 | - COMPILER="g++-4.8" 10 | - COMPILER="g++-4.9" 11 | - COMPILER="clang++-3.3" 12 | - COMPILER="clang++-3.4" 13 | - COMPILER="clang++-3.5" 14 | - COMPILER="clang++-3.6" 15 | 16 | matrix: 17 | allow_failures: 18 | - env: COMPILER="clang++-3.3" 19 | - env: COMPILER="clang++-3.5" 20 | 21 | before_script: 22 | - sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test 23 | - sudo add-apt-repository --yes ppa:h-rayflood/llvm 24 | - sudo add-apt-repository -y 'deb http://llvm.org/apt/precise/ llvm-toolchain-precise-3.5 main' 25 | - sudo add-apt-repository -y 'deb http://llvm.org/apt/precise/ llvm-toolchain-precise-3.6 main' 26 | - sudo add-apt-repository -y 'deb http://llvm.org/apt/precise/ llvm-toolchain-precise-3.7 main' 27 | - sudo apt-get update -qq 28 | - sudo apt-get install --allow-unauthenticated $COMPILER 29 | - git clone https://github.com/CxxTest/cxxtest.git cxxtest-ro 30 | - curl -O https://cmake.org/files/v3.2/cmake-3.2.3-Linux-x86_64.tar.gz 31 | - tar -xzf cmake-3.2.3-Linux-x86_64.tar.gz 32 | - sudo cp -fR cmake-3.2.3-Linux-x86_64/* /usr 33 | 34 | script: 35 | - export PATH="$PATH:$(pwd)/cxxtest-ro/bin" 36 | - ln -s cxxtest-ro/cxxtest cxxtest 37 | - cd test 38 | - make COMPILER=$COMPILER unix 39 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2024, Anton Bachin 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Better Enums   [![Try online][wandbox-img]][wandbox] [![Travis status][travis-img]][travis] [![AppVeyor status][appveyor-img]][appveyor] 2 | 3 | [wandbox]: http://melpon.org/wandbox/permlink/2QCi3cwQnplAToge 4 | [wandbox-img]: https://img.shields.io/badge/try%20it-online-blue.svg 5 | [appveyor]: https://ci.appveyor.com/project/aantron/better-enums/branch/master 6 | [travis]: https://travis-ci.org/aantron/better-enums/branches 7 | [travis-img]: https://img.shields.io/travis/aantron/better-enums/master.svg?label=travis 8 | [appveyor-img]: https://img.shields.io/appveyor/ci/aantron/better-enums/master.svg?label=appveyor 9 | [license-img]: https://img.shields.io/badge/license-BSD-lightgrey.svg 10 | 11 | Reflective compile-time enum library with clean syntax, in a single header 12 | file, and without dependencies. 13 | 14 | ![Better Enums code overview][sample] 15 | 16 | [sample]: https://raw.githubusercontent.com/aantron/better-enums/master/doc/image/sample.gif 17 | 18 | In C++11, *everything* can be used at compile time. You can convert your enums, 19 | loop over them, [find their max][max], 20 | [statically enforce conventions][enforce], and pass along the results as 21 | template arguments or to `constexpr` functions. All the reflection is available 22 | for your metaprogramming needs. 23 | 24 | The interface is the same for C++98 — you just have to use most of it at 25 | run time only. This library does provide scoped and sized enums, something not 26 | built into C++98. 27 | 28 | See the [project page][project] for full documentation. 29 | 30 | [max]: http://aantron.github.io/better-enums/demo/BitSets.html 31 | [enforce]: http://aantron.github.io/better-enums/demo/SpecialValues.html 32 | [project]: http://aantron.github.io/better-enums 33 | 34 |
35 | 36 | ## Installation 37 | 38 | Simply add `enum.h` to your project. 39 | 40 |
41 | 42 | ## Additional features 43 | 44 | - Uses only standard C++, though, for C++98, variadic macro support is required 45 | (major compilers have it). 46 | - Supported and tested on [clang, gcc, and msvc][testing]. 47 | - Fast compilation. You have to declare a few dozen enums to slow down your 48 | compiler as much as [only including `iostream` does][performance]. 49 | - Use any initializers and sparse ranges, just like with a built-in enum. 50 | - Control over size and alignment — you choose the representation type. 51 | - Stream operators. 52 | - Does not use the heap and can be compiled with exceptions disabled, for use in 53 | minimal freestanding environments. 54 | 55 | [testing]: http://aantron.github.io/better-enums/CompilerSupport.html 56 | [performance]: http://aantron.github.io/better-enums/Performance.html 57 | 58 |
59 | 60 | ## Limitations 61 | 62 | 1. The biggest limitation is that the `BETTER_ENUM` macro can't be used inside a 63 | class. This seems [difficult to remove][nested]. There is a workaround with 64 | `typedef` (or C++11 `using`): 65 | 66 | ```c++ 67 | BETTER_ENUM(SomePrefix_Color, uint8_t, Red, Green, Blue) 68 | 69 | struct triplet { 70 | typedef SomePrefix_Color Color; 71 | Color r, g, b; 72 | }; 73 | 74 | triplet::Color color; 75 | ``` 76 | 77 | You can, however, use `BETTER_ENUM` inside a namespace. 78 | 79 | 2. The macro has a soft limit of 64 declared constants. You can extend it by 80 | following [these instructions][extend]. Ultimately, the number of constants is 81 | limited by your compiler's maximum macro argument count. 82 | 83 | 3. In some cases, it is necessary to prefix constants such as `Channel::Red` with a 84 | `+` to explicitly promote them to type `Channel`. For example, if you are doing 85 | a comparison: 86 | 87 | ```c++ 88 | channel == +Channel::Red 89 | ``` 90 | 91 | 4. On msvc, you may need to enable [warning C4062][C4062] to get `switch` case exhaustiveness checking. 92 | 93 | [nested]: http://aantron.github.io/better-enums/DesignDecisionsFAQ.html#NoEnumInsideClass 94 | [extend]: http://aantron.github.io/better-enums/ExtendingLimits.html 95 | [C4062]: https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4062 96 | 97 |
98 | 99 | ## History 100 | 101 | The original version of the library was developed by the author in the winter of 102 | 2012-2013 at Hudson River Trading, as a replacement for an older generator 103 | called `BETTER_ENUM`. 104 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{build}" 2 | 3 | branches: 4 | except: 5 | - gh-pages 6 | - traits 7 | 8 | skip_tags : true 9 | shallow_clone: true 10 | 11 | os: Visual Studio 2017 12 | 13 | environment: 14 | matrix: 15 | - TITLE: vc2017 16 | COMPILER: Visual Studio 15 2017 17 | - TITLE: vc2015 18 | COMPILER: Visual Studio 14 2015 19 | - TITLE: vc2013 20 | COMPILER: Visual Studio 12 2013 21 | 22 | install: 23 | - git clone --depth 1 https://github.com/CxxTest/cxxtest.git 24 | 25 | build_script: 26 | - set CL=/I C:\projects\better-enums\cxxtest 27 | - set PATH=%PATH%;C:\projects\better-enums\cxxtest\bin;C:\cygwin\bin 28 | - cd test 29 | - make ms 30 | -------------------------------------------------------------------------------- /doc/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Better Enums 2 | 3 | All contributions are welcome: feedback, documentation changes, and, of course, 4 | pull requests and patch suggestions. A list of contributors is maintained in the 5 | `CONTRIBUTORS` file. I am grateful to everyone mentioned. 6 | 7 | ## Some major outstanding issues 8 | 9 | - Better Enums currently uses linear scans for lookup, so it could really 10 | benefit from a lookup data structure that is really fast to generate at 11 | compile time. All the sorts and other approaches I have tried so far, 12 | including MPL, Meta, and my own, have been 10-50 times too slow for practical 13 | use. 14 | - Alternatively, if there is a method to detect whether a given function is 15 | running at compile or run time, Better Enums could use linear scans during 16 | compilation, then do a sort at program initialization, and then use fast 17 | lookups. 18 | - It would be nice if name trimming was always `constexpr`. Right now, this is 19 | not the default, because it makes compilation of each Better Enum about four 20 | times slower. Better Enums needs a fast way to take a `const char*`, chop off 21 | any initializers, and return the new `const char*`. 22 | - I would like to enable more warning flags besides just 23 | `-Wall -Wextra -pedantic`, but CxxTest triggers the extra warnings. 24 | `CMakeLists.txt` should probably be modified to add the extra warnings to all 25 | targets that are not CxxTest tests. 26 | 27 | ## Testing 28 | 29 | I typically write small programs to play around with `enum.h`. The `scratch/` 30 | directory is in `.gitignore` for this purpose. Create `scratch/` and feel free 31 | to do anything you want in it. Once your change is nearly complete, you should 32 | run the automated test suite. 33 | 34 | While still actively developing, run `make -C test` to do quick builds. Before 35 | submitting your pull request, run `make -C test default-all`. The first command 36 | tests your code on your system compiler in one configuration, and the second 37 | command tests it in all configurations that your compiler supports. 38 | *Configurations* refers to the optional features of Better Enums, such as 39 | `constexpr` string conversion and using an `enum class` for `switch` statements. 40 | 41 | Once your pull request is submitted, the [AppVeyor][appveyor] and 42 | [Travis][travis] web services will automatically test it on many versions of 43 | GCC, Clang, and Visual C++. If you have more than one compiler installed 44 | locally, you can run either the `unix` or `ms` target in `test/Makefile` to test 45 | on a specific compiler. Open the `Makefile` file and find the targets for 46 | instructions. 47 | 48 | If your pull request does not include any changes to the code (for example, you 49 | have changed only documentation), add the text `[ci skip]` to the commit message 50 | to prevent AppVeyor and Travis from testing the commit. 51 | 52 | The `make` targets mentioned above depend on the following software: 53 | 54 | - Make 55 | - CMake 56 | - Python 2 57 | - [CxxTest][cxxtest] 58 | 59 | CxxTest's `bin/` directory has to be in `PATH` and the root `cxxtest/` directory 60 | has to be in whatever environment variable your system compiler uses to search 61 | for header files. 62 | 63 | On Windows, you also need [Cygwin][cygwin]. The directory containing 64 | `MSBuild.exe` must be in `PATH`. 65 | 66 | The programs in `example/` are generated from the Markdown files in 67 | `doc/tutorial/` and `doc/demo/`. If you have edited the Markdown files, you 68 | should run `make -C test examples` to update the example program sources. 69 | 70 | If you have created a new example program or compilation performance test, add 71 | it to `test/CMakeLists.txt`. Search for the name of an existing program, such as 72 | `1-hello-world`, and you should see where to add new ones. 73 | 74 | [cygwin]: https://www.cygwin.com 75 | [cxxtest]: http://cxxtest.com 76 | [appveyor]: https://ci.appveyor.com/project/aantron/better-enums 77 | [travis]: https://travis-ci.org/aantron/better-enums 78 | 79 | ## Commits 80 | 81 | Please write descriptive commit messages that follow the 50/72 rule. I am likely 82 | to edit commit messages when merging into `master`. I will also squash multiple 83 | commits in most cases. If you prefer I not do either one, let me know, but then 84 | we will have to go back and forth on the exact contents of the pull request. 85 | 86 | I am maintaining the `master` branch in such a way that every commit passes its 87 | tests, i.e. `master` is always stable. Every commit going into `master` is first 88 | fully tested in its pull request branch, or in a temporary staging branch, if 89 | necessary. 90 | 91 | ## Generating the documentation 92 | 93 | To generate an offline copy of the documentation, run `make -C doc`. To view it, 94 | open `doc/html/index.html`. 95 | -------------------------------------------------------------------------------- /doc/CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Here you will find a list of everyone I have to thank for contributing code, 2 | suggestions, comments, other feedback, or anything else that was useful to the 3 | library. This file never shrinks - if a name goes missing, it's because a chat 4 | handle was replaced by a real name. There is no significance to the order. 5 | 6 | Thanks to: 7 | 8 | Tony Van Eerd 9 | Ben Alex 10 | Simon Stienen 11 | Alexander Buddenbrock 12 | -------------------------------------------------------------------------------- /doc/CompilerSupport.md: -------------------------------------------------------------------------------- 1 | ## Compiler support 2 | 3 | Better Enums aims to support all major compilers. It is known to work on: 4 | 5 | - clang 3.3 to 3.9 6 | - gcc 4.3 to 5.3 7 | - Visual C++ 2008 to 2015. 8 | 9 | The library can be used with any compiler that supports either $cxx11, or $cxx98 10 | with the `__VA_ARGS__` extension. This includes every version of gcc and clang I 11 | have ever heard of, and Visual C++ down to 2005. 12 | 13 | To ensure that nothing is broken, every release of Better Enums is 14 | [tested]($repo/tree/master/test) in multiple configuratins on the compilers 15 | listed above. Testing includes the code in the tutorials, the unit tests, and a 16 | multiple translation unit linking test. The full list of tested compilers and 17 | configurations is given at [the end of this page](#TestedConfigurations). 18 | 19 | ### Compile-time reflection configurations 20 | 21 | Read this section if: 22 | 23 | - you want to use Better Enums reflection at compile time, and need to know 24 | exactly what features are supported on your compiler, or 25 | - Better Enums is choosing the wrong configuration automatically and you need 26 | to force a different choice. 27 | 28 | All features of Better Enums are always available for run-time use. However, for 29 | compile-time use, Better Enums has two main configurations: $cxx98 mode and 30 | `constexpr` mode. Better Enums tries to detect which compiler is compiling it 31 | and select the appropriate mode. 32 | 33 | For performance reasons, `constexpr` mode is subdivided into a "fast" 34 | `constexpr` mode and an 35 | [opt-in](${prefix}OptInFeatures.html#CompileTimeNameTrimming) "full" (slow) 36 | `constexpr` mode. The three modes can be ranked, with each next mode including 37 | all the features of the preceding ones: 38 | 39 | - $cxx98 40 | - fast `constexpr` 41 | - full `constexpr` 42 | 43 | Only `_size` is supported at compile time in $cxx98 mode. Fast `constexpr` mode 44 | adds all other members besides `_to_string` and `_names`. Full `constexpr` mode 45 | supports those at compile time as well. 46 | 47 | --- 48 | 49 | The mode selection code works as follows: 50 | 51 | - First, as of the time of this writing, only clang and gcc support 52 | `constexpr`. So, if you are using any other compiler, Better Enums is in 53 | $cxx98 mode. 54 | - If you are using gcc 4.7 or higher or clang, Better Enums uses those 55 | compilers' predefined macros to detect whether `constexpr` support is 56 | enabled with compiler flags. If so, it is in one of the `constexpr` mode. 57 | Otherwise, it falls back to $cxx98 mode. 58 | - The default `constexpr` mode is fast `constexpr`. If you want to enable 59 | full `constexpr` mode for some or all of your enums, follow 60 | [these](${prefix}OptInFeatures.html#CompileTimeNameTrimming) instructions. 61 | 62 | If Better Enums picks the wrong mode, you can force `constexpr` mode by defining 63 | `BETTER_ENUMS_CONSTEXPR` before including `enum.h`, typically by passing an 64 | option to your compiler, or you can force $cxx98 mode by defining 65 | `BETTER_ENUMS_NO_CONSTEXPR`. 66 | 67 | If you are using a compiler for which Better Enums makes the wrong choice, 68 | please [let me know](${prefix}Contact.html). I will fix it and you won't have to 69 | define these macros anymore. 70 | 71 | ### Tested configurations 72 | 73 | ~~~comment 74 | vc2015 /EHsc 75 | vc2015 /EHsc /DBETTER_ENUMS_STRICT_CONVERSION 76 | vc2013 /EHsc 77 | vc2013 /EHsc /DBETTER_ENUMS_STRICT_CONVERSION 78 | vc2012 /EHsc 79 | vc2010 /EHsc 80 | vc2008 /EHsc 81 | clang++39 -std=c++11 82 | clang++39 -std=c++11 -DBETTER_ENUMS_STRICT_CONVERSION 83 | clang++39 -std=c++11 -DBETTER_ENUMS_CONSTEXPR_TO_STRING 84 | clang++39 -std=c++98 85 | clang++38 -std=c++11 86 | clang++38 -std=c++11 -DBETTER_ENUMS_STRICT_CONVERSION 87 | clang++38 -std=c++11 -DBETTER_ENUMS_CONSTEXPR_TO_STRING 88 | clang++38 -std=c++98 89 | clang++37 -std=c++11 90 | clang++37 -std=c++11 -DBETTER_ENUMS_STRICT_CONVERSION 91 | clang++37 -std=c++11 -DBETTER_ENUMS_CONSTEXPR_TO_STRING 92 | clang++37 -std=c++98 93 | clang++36 -std=c++11 94 | clang++36 -std=c++11 -DBETTER_ENUMS_STRICT_CONVERSION 95 | clang++36 -std=c++11 -DBETTER_ENUMS_CONSTEXPR_TO_STRING 96 | clang++36 -std=c++98 97 | clang++35 -std=c++11 98 | clang++35 -std=c++11 -DBETTER_ENUMS_STRICT_CONVERSION 99 | clang++35 -std=c++11 -DBETTER_ENUMS_CONSTEXPR_TO_STRING 100 | clang++35 -std=c++98 101 | clang++34 -std=c++11 102 | clang++34 -std=c++11 -DBETTER_ENUMS_STRICT_CONVERSION 103 | clang++34 -std=c++11 -DBETTER_ENUMS_CONSTEXPR_TO_STRING 104 | clang++34 -std=c++98 105 | clang++33 -std=c++98 106 | g++53 -std=c++11 107 | g++53 -std=c++11 -DBETTER_ENUMS_STRICT_CONVERSION 108 | g++53 -std=c++11 -DBETTER_ENUMS_CONSTEXPR_TO_STRING 109 | g++53 -std=c++98 110 | g++49 -std=c++11 111 | g++49 -std=c++11 -DBETTER_ENUMS_STRICT_CONVERSION 112 | g++49 -std=c++11 -DBETTER_ENUMS_CONSTEXPR_TO_STRING 113 | g++49 -std=c++98 114 | g++48 -std=c++11 115 | g++48 -std=c++11 -DBETTER_ENUMS_STRICT_CONVERSION 116 | g++48 -std=c++11 -DBETTER_ENUMS_CONSTEXPR_TO_STRING 117 | g++48 -std=c++98 118 | g++47 -std=c++11 119 | g++47 -std=c++11 -DBETTER_ENUMS_STRICT_CONVERSION 120 | g++47 -std=c++11 -DBETTER_ENUMS_CONSTEXPR_TO_STRING 121 | g++47 -std=c++98 122 | g++46 -std=c++98 123 | g++45 -std=c++98 124 | g++44 -std=c++98 125 | g++43 -std=c++98 126 | ~~~ 127 | 128 | %% description = 129 | Better Enums compiler support, compatibility, feature detection, and automated 130 | testing. 131 | -------------------------------------------------------------------------------- /doc/DesignDecisionsFAQ.md: -------------------------------------------------------------------------------- 1 | ## Design decisions FAQ 2 | 3 | $be pushes at the edges of what is possible in standard $cxx, and I've had to 4 | make some difficult decisions as a result. You can imagine the set of 5 | potential reflective enum implementations as a space, with axes such as "concise 6 | syntax," "uniform interface," "compilation speed," "run-time performance," and 7 | so on. As is typical in engineering, the constraints are such that as you move 8 | to extremes along one axis, you have to retreat along others — for 9 | example, some desirable aspects of concise syntax conflict with having a uniform 10 | interface, which is nonetheless good for teachability, and compile-time 11 | performance is, in some ways, at odds with run-time performance. 12 | 13 | So, there are many variations possible on $be, and, in fact, I have tried and 14 | maintained a few. This page describes how I chose the one that is published. 15 | The choices are debatable, but I am attempting to record the debate. Hopefully, 16 | this will either convince you that I have made a good choice, or, if you 17 | disagree, you will have a good starting point for discussion, or even for 18 | implementing an alternative. 19 | 20 | I am always looking for new arguments and approaches. If you have an idea, 21 | comment, criticism, please do [let me know][contact]. 22 | 23 | $internal_toc 24 | 25 | ### Why do enum members have underscores? 26 | 27 | Enum members such as `_to_string` occupy the same scope as the names of 28 | constants declared by the user. I chose to prefix members with underscores to 29 | lessen the chances of collision. For example, take `_valid`, and suppose it was 30 | `valid` instead. That would make this enum impossible: 31 | 32 | BETTER_ENUM(Status, char, valid, invalid) 33 | 34 | because the constant `Status::valid` would clash with the member 35 | `Status::valid`. 36 | 37 | Of course, users could try to declare constants with underscores as well, but I 38 | find it easier to ask users not to do that, rather than ask them to worry about 39 | a potentially growing set of reserved names, which they wouldn't fully know even 40 | for a single version of Better Enums, without frequently looking at the API 41 | documentation. 42 | 43 | Alternatives to this involve separating the namespaces occupied by members and 44 | constants. I don't think increasing nesting is an option, since nobody wants to 45 | write `Status::values::valid` or `Status::operations::valid`. I don't think 46 | moving constants out of `Status` is a good idea, since scoped constants is a 47 | feature of Better Enums, which is especially important for $cxx98 usage. 48 | 49 | This leaves the possibility of moving the operations performed by the members 50 | into traits types, i.e. something like `traits::valid`. That is an 51 | interesting option, and it has [its own section][traits]. I have tried it, but 52 | the verbosity increase is much greater than the benefit of dropping underscores, 53 | so I chose not to do it. 54 | 55 | ### Why does Better Enums use a macro at all? 56 | 57 | Better Enums needs to turn the names of declared constants into strings, and I 58 | don't believe there is any way to do this in standard $cxx except by using the 59 | preprocessor's macro parameter stringization operator (`#`). So, at the top 60 | level, Better Enums has to provide a macro. I am, however, trying to keep the 61 | user-facing macros to a minimum; in particular there is only one. 62 | 63 | I think that's the best that is possible. Furthermore, apart from the macro 64 | itself, the declaration looks very similar to a $cxx11 `enum` declaration, with 65 | an underlying type, comma-separated constant list, and the same support for 66 | initializers as built-in enums. So, I am trying to keep even this one macro out 67 | of the user's way. I wouldn't accept any change that involved turning the 68 | declaration into a preprocessor sequence or tuple, i.e. something like 69 | 70 | BETTER_ENUM(Channel, int, (Red)(Green)((Blue)(5))) 71 | 72 | even if it promised extra capabilities. 73 | 74 | Better Enums uses additional macros internally, for two main purposes: to do the 75 | actual work of stringizing the declared constants and assembling them into 76 | arrays, and to configure itself by detecting which compiler it is running on. 77 | 78 | I am not a fan of gratuitous macros, but in these cases they are unavoidable, 79 | and, indeed, I am grateful for the stringization operator. 80 | 81 | 82 | ### Why is it not possible to declare a Better Enum inside a class? 83 | 84 | This is due to an interaction between linkage and `constexpr`. 85 | 86 | 1. Better Enums is a header-only library that declares arrays with static 87 | storage, such as the array of constant names for each declared enum. Such 88 | arrays can be declared in namespace scope, in class scope, or in function 89 | scope, but they also need to be defined somewhere. 90 | 91 | If `BETTER_ENUM` is to be usable in both namespace and class scope, it 92 | already can't assume that it can declare arrays in namespace scope, since if 93 | `BETTER_ENUM` is used in a class, its entire expansion will be enclosed in 94 | the declaration of that class. 95 | 96 | That leaves class scope and function scope. If the arrays are declared in 97 | class scope, there needs to be a separate definition of them in some 98 | translation unit. This is too burdensome for the user, because the separate 99 | definition would involve repetition of the constants in the macro parameter 100 | list, creating exactly the type of maintenance problem that Better Enums is 101 | trying to eliminate. 102 | 103 | Function scope is the only viable option remaining after considering linkage 104 | constraints. Each array can be wrapped in a static function, which has a 105 | static local variable, which is initialized with the array. The functions can 106 | be called to get references to the arrays. 107 | 108 | However.... 109 | 110 | 2. These arrays need to be accessible to `constexpr` code in $cxx11, and 111 | `constexpr` functions are not allowed to have static local variables. 112 | 113 | Ironically, this seems like one place where $cxx98 is more "flexible," but only 114 | for the reason that compile-time usage of Better Enums is not supported in 115 | $cxx98. 116 | 117 | 118 | ### Should Better Enums provide enum "objects" or traits types? 119 | 120 | A Better Enum value is an "object," whose memory representation is the same as 121 | its underlying type. For example, 122 | 123 | BETTER_ENUM(Channel, int, Red, Green, Blue) 124 | 125 | expands to something like 126 | 127 | struct Channel { 128 | enum _enumerated : int { Red, Green, Blue }; 129 | int _value; 130 | // Strings array, _to_string, _from_string, etc. 131 | }; 132 | 133 | --- 134 | 135 | There is an alternative interpretation, in which the Better Enums library 136 | generates enum traits instead, i.e. the generated arrays and members sit 137 | alongside built-in enums instead of wrapping them: 138 | 139 | BETTER_ENUM(Channel, int, Red, Green, Blue) 140 | 141 | generates 142 | 143 | enum class Channel : int { Red, Green, Blue }; 144 | 145 | template <> 146 | struct ::better_enums::traits { 147 | using _enumerated = Channel; 148 | // Strings array, to_string, from_string, etc. 149 | }; 150 | 151 | --- 152 | 153 | There are a number of advantages to the traits approach. 154 | 155 | - The underscore prefixes can be dropped from member names, since they no longer 156 | share a namespace with user-declared constants. 157 | - The interface, at first glance, becomes more uniform, since now every member 158 | is a static member of the traits type. Without traits, `_to_string` is a 159 | non-static member, while `_from_string` is a static member. 160 | - `Channel` is one of the language's own enum types, instead of some mystery 161 | type provided by Better Enums. This may make it easier to understand. It also 162 | eliminates the problem with different syntaxes for `switch` statements 163 | described [here][implicit]. 164 | 165 | However, it also introduces some difficulties. 166 | 167 | - The syntax is more verbose, since everything becomes a member of the traits 168 | type. For example, instead of `Channel::_from_string()`, you get 169 | `better_enums::traits::from_string()`. The underscore may be 170 | unpleasant, but so far I have preferred the underscore to boilerplate. 171 | - The uniform interface ends up getting wrapped behind functions anyway, for the 172 | sake of type inference. For example, the "naked" `to_string` function is 173 | called as `better_enums::traits::to_string(channel)`, which is 174 | redundant, because the compiler could infer the type parameter `Channel` if it 175 | was the parameter of the function instead of the traits type. So, the obvious 176 | thing is to define such a wrapper function, which can then be called as 177 | `better_enums::to_string(channel)`. No such function can be easily defined for 178 | `from_string` and other `from_*` functions, however, because the type 179 | parameters can't be inferred from arguments. So, the signatures of `to_*` and 180 | `from_*` functions again effectively diverge, negating this advantage of 181 | traits. The closest I can get with wrappers is 182 | `better_enums::from_string`, which has the same signature only in the 183 | formal sense, i.e. modulo the difference in type inference. 184 | 185 | I actually think there is a way to infer the type parameter from the return 186 | type, similar to how it is done [here][infer], but that will not be suitable 187 | for all contexts, and the user may be surprised by ambiguous resolution error 188 | messages when it is not. 189 | - Scoped constants are lost for $cxx98 unless Better Enums again wraps them in a 190 | generated type, though it will be more lightweight than a full Better Enum of 191 | the non-traits approach. 192 | - Traits types must be specialized in either the same namespace scope they are 193 | declared in, or in an enclosing scope. This makes it impossible to declare an 194 | enum and specialize traits for it in a user's custom namespace. 195 | 196 | Despite the disadvantages listed just above, I consider the traits approach 197 | interesting — it's a close call. There is an 198 | [out-of-date branch][traits-branch] containing a traits version of Better Enums. 199 | You can see some of the usage in its [samples][traits-samples] directory. I may 200 | update it from time to time, especially if there is interest. 201 | 202 | ### Why does Better Enums use linear scans for lookup? 203 | 204 | It seems that Better Enums needs to use the same algorithms at compile time as 205 | at run time, because I have not found a way (and doubt there is one) to 206 | determine, during the execution of a `constexpr` function, whether it is 207 | executing at compile time or at run time. So, whatever data structures I use to 208 | accelerate lookup, I have to generate them at compile time, to be available as 209 | early as possible. 210 | 211 | I tried to generate various data structures at compile time, but so far, 212 | generation has been too slow. The fastest algorithm so far, a compile-time merge 213 | sort based on template parameter packs, took over 100ms to run on the constant 214 | set of a large enum. I would have to run three of these per enum — for the 215 | constants, for the names, and for the names with case-insensitive comparison. 216 | This results in a 300ms slowdown per enum, which is not acceptable, given that 217 | on my system the same compiler takes 370ms to process `iostream`, and less than 218 | 10ms to process an enum without acceleration data structures. Declaring five 219 | large enums with accelerated lookup would take 1.5 seconds of compilation time. 220 | This doesn't scale to large projects with many translation units. 221 | 222 | I am continuing to look for faster algorithms or better approaches, so faster 223 | lookup may be coming to Better Enums in the future. 224 | 225 | So far, I have tried Boost.MPL sort, Eric Niebler's Meta sort, my own selection 226 | sort based on `constexpr`, and an insertion and merge sort based on parameter 227 | packs. I cannot use (Boost?).Hana sort, because that requires $cxx14. I am also 228 | considering various hash table-like data structures, and providing two sets of 229 | interfaces for compile-time and run-time usage, which is something I would 230 | really rather not have to do. The latter option would be worth considering, 231 | however, if I measured a significant improvement in running time from better 232 | data structures — something I haven't gotten to yet because there doesn't 233 | seem to be a data structure to measure that is not disqualified by the speed of 234 | generation. 235 | 236 | ### Why not use indices for the representation? 237 | 238 | Representing Better Enum values by their indices in the declaration list seems 239 | like a tempting solution for the problem of having multiple constants with the 240 | same numeric value. It also speeds up some operations. For example, if a Better 241 | Enum is simply an index, then getting its string representation is simply 242 | indexing an array, rather than some kind of data structure lookup. 243 | 244 | // Representations 0, 1, 2, 3 instead of 1, 2, 3, 1. 245 | BETTER_ENUM(Kind, int, A = 1, B, C, D = A) 246 | 247 | Choosing this approach has serious drawbacks. 248 | 249 | - The data structure lookup has simply been moved to another place. It now takes 250 | time to convert from a literal `Kind::D` to a Better Enum. 251 | - It is still impossible to disambiguate between the literals `Kind::D` and 252 | `Kind::A` (1 and 1). Only the Better Enums objects of type `Kind` that 253 | represent `D` and `A` are different from each other (0 and 3). This is not 254 | only a technical problem, but is also quite unintuitive. 255 | - Treating a Better Enum represented by an index as untyped memory produces 256 | surprising results. This makes Better Enums much less useful with functions 257 | such as `fwrite`. Worse, Better Enums become sensitive to declaration order 258 | even when initializers are given explicitly. Using indices for the 259 | representation makes it difficult to maintain compatibility with external 260 | protocols and file formats. 261 | 262 | [contact]: ${prefix}Contact.html 263 | [traits]: #Traits 264 | [implicit]: ${prefix}OptInFeatures.html#StrictConversions 265 | [infer]: ${prefix}demo/SpecialValues.html 266 | [traits-branch]: $repo/tree/traits 267 | [traits-samples]: $repo/tree/traits/samples 268 | 269 | %% description = Better Enums design decisions and tradeoffs. 270 | -------------------------------------------------------------------------------- /doc/ExtendingLimits.md: -------------------------------------------------------------------------------- 1 | ## Extending limits 2 | 3 | The `BETTER_ENUM` macro makes heavy use of the preprocessor, and some of the 4 | internal macros have size limits. There are two: on the number of constants you 5 | can declare, and on the maximum length of a constant name under very specific 6 | conditions. If you run into either one, you can extend the limit by following 7 | the instructions on this page. 8 | 9 | The second limit, on the maximum length of a constant name, applies only when 10 | you are compiling an enum in 11 | ["full" `constexpr`](${prefix}OptInFeatures.html#CompileTimeNameTrimming) mode 12 | *and* the constant has an initializer. Otherwise, your constants can have names 13 | of arbitrary length. 14 | 15 | The default limits are 64 constants in an enum and 23 characters for initialized 16 | constants of full-`constexpr` enums. To extend: 17 | 18 | 1. Pick your desired limits. I will use 512 constants and 63 characters as an 19 | example. Add 1 to the number of characters to account for the null 20 | terminator — our numbers are now 512 and 64. 21 | 2. Get `make_macros.py` from your copy of the full Better Enums distribution 22 | or from GitHub. 23 | 3. You will run this script to generate a header file containing some 24 | replacement macros for `enum.h` to use. Pick a name for this file and a 25 | location somewhere in your include path. I will assume that this file is 26 | `common/enum_macros.h` in your project. 27 | 4. Run `python make_macros.py 512 64 > common/enum_macros.h`. 28 | 5. Define `BETTER_ENUMS_MACRO_FILE ` before including 29 | `enum.h`. This is typically done by supplying extra flags to the compiler 30 | on the command line: 31 | 32 | - For g++ and clang++, `-DBETTER_ENUMS_MACRO_FILE=''` 33 | - For VC++, `\DBETTER_ENUMS_MACRO_FILE=''` 34 | - With CMake, you may need something like 35 | `add_definitions(-DBETTER_ENUMS_MACRO_FILE="$${CMAKE_SOURCE_DIR}/src/enum-macros.h")` 36 | 37 | You can also create a new header file that defines this macro, and then 38 | includes `enum.h`. Then, include your new file everywhere where you would 39 | otherwise include `enum.h`: 40 | 41 | ~~~comment 42 | #pragma once 43 | 44 | #define BETTER_ENUMS_MACRO_FILE 45 | #include 46 | ~~~ 47 | 48 | 6. Enjoy the looser limits. Just watch out — increasing the second 49 | number can really slow down compilation of full-`constexpr` enums. 50 | 7. You don't need `make_macros.py` anymore. It's not part of your build 51 | process and you can delete it. 52 | 53 | --- 54 | 55 | I am paying attention to feedback, so if more than a few users say that the 56 | default limit of 64 constants is too low, I will increase it to simplify 57 | everyone's command line. The current choice of 64 is basically an arbitrary 58 | guess, loosely informed by the following two facts about macro parameter limits: 59 | 60 | - The default limit in Boost.Preprocessor is 64. Though Better Enums does not 61 | use Boost, I took this number as a guideline. 62 | - The next power of two, 128, is more than [the number Visual C++ supports][vc] 63 | (127). 64 | 65 | [vc]: https://msdn.microsoft.com/en-us/library/ft39hh4x.aspx 66 | 67 | %% description = 68 | How to extend limits imposed by internal macros in Better Enums. 69 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | toexample = ../example/$(notdir $(1:.md=.cc)) 2 | 3 | SOURCE_MARKDOWN := $(wildcard tutorial/*.md) $(wildcard demo/*.md) 4 | SOURCE_CXX := $(foreach md,$(SOURCE_MARKDOWN),$(call toexample,$(md))) 5 | 6 | .PHONY : html 7 | html : 8 | python docs.py 9 | @echo "See html/index.html" 10 | 11 | .PHONY : publish 12 | publish : prepare 13 | cp -r html/* ../doc-publish 14 | cd ../doc-publish && git add . && git commit --amend --reset-author && \ 15 | git push -f 16 | 17 | .PHONY : prepare 18 | prepare : examples web 19 | 20 | .PHONY : web 21 | web : examples 22 | python docs.py --web 23 | 24 | .PHONY : examples 25 | examples : $(SOURCE_CXX) 26 | 27 | define EXAMPLE 28 | $(1) : $(2) 29 | python transform.py --o-cxx $(1) $(2) 30 | endef 31 | 32 | $(foreach md,$(SOURCE_MARKDOWN), \ 33 | $(eval $(call EXAMPLE,$(call toexample,$(md)),$(md)))) 34 | 35 | .PHONY : clean 36 | clean : 37 | rm -rf html 38 | -------------------------------------------------------------------------------- /doc/OptInFeatures.md: -------------------------------------------------------------------------------- 1 | ## Opt-in features 2 | 3 | Better Enums has two opt-in features. They are both "good," but they either hurt 4 | compilation time or break compatibility with $cxx98, so they are disabled by 5 | default. Read this page if you want to enable them. 6 | 7 | $internal_toc 8 | 9 | ### Default constructors 10 | 11 | Better Enums generate with inaccessible (private or deleted) default 12 | constructors. This is meant to help control where in your program Better Enums 13 | values are introduced, and thus ensure that only valid, properly initialized 14 | enum values are ever created. 15 | 16 | If you want Better Enums to have a default constructor, you can do something 17 | like the following: 18 | 19 | ~~~ 20 | #define BETTER_ENUMS_DEFAULT_CONSTRUCTOR(Enum) \ 21 | public: \ 22 | Enum() = default; 23 | 24 | #include <enum.h> 25 | ~~~ 26 | 27 | You can put this in its own header file, and include that header file instead of 28 | including `enum.h` directly. 29 | 30 | ### Compile-time name trimming 31 | 32 | This makes `_to_string` and `_names` `constexpr`, i.e. "full" `constexpr` mode. 33 | To enable this for all enums, define `BETTER_ENUMS_CONSTEXPR_TO_STRING` before 34 | including `enum.h`. Typically, you would pass an option to your compiler to do 35 | this. 36 | 37 | You can also enable this feature for individual enums instead, by declaring them 38 | using the alternative `SLOW_ENUM` macro. 39 | 40 | The feature is disabled because it increases compilation times by a factor of 41 | about 4. Compilation is still relatively fast — you need about a dozen 42 | slow enums to get the same penalty as including `iostream` — but it is 43 | a steep penalty nonetheless. I don't think most people need this feature most of 44 | the time, so it's too high a price to pay. If I improve compilation times to the 45 | point where compile-time name trimming can be the default, I will simply 46 | redefine `SLOW_ENUM` as `BETTER_ENUM` and deprecate it, so your code will still 47 | work. 48 | 49 | ### Strict conversions 50 | 51 | This disables implicit conversions to underlying integral types. At the moment, 52 | you can only enable this globally for all enums, by defining 53 | `BETTER_ENUMS_STRICT_CONVERSION` before including `enum.h`. 54 | 55 | The reason Better Enums have implicit conversions to integral types in the first 56 | place is that in $cxx98, Better Enums have to be implicitly convertible to their 57 | member [`_enumerated`](${prefix}ApiReference.html#Typedef_enumerated) types to 58 | be [usable](${prefix}tutorial/SafeSwitch.html) in `switch` statements. It is 59 | possible to avoid this in $cxx11 and convert to `enum class` types instead, but 60 | at the cost of breaking interface compatibility with $cxx98. 61 | 62 | - The "weaker" incompatibility is that you could write a bunch of $cxx98 code 63 | that relies on implicit integer conversions, and then try to switch to 64 | $cxx11. The code would then fail to compile. An example where implicit 65 | conversions are an "advantage" is when using Better Enums as arguments to 66 | the methods of `std::bitset`. I could have ignored this problem by declaring 67 | usage of implicit integer conversions unsupported, but in light of the next 68 | issue, I decided not to do that. 69 | - The "stronger" incompatibility is a difference in how `switch` cases must be 70 | written. The syntaxes for the two variants, implicitly-converting and 71 | strict, are mutually exclusive. They differ by a `+` character. 72 | 73 | Here they are: 74 | 75 | // Default variant 76 | switch (channel) { 77 | case Channel::Red: break; 78 | case Channel::Green: break; 79 | case Channel::Blue: break; 80 | } 81 | 82 | // Strict variant 83 | switch (channel) { 84 | case +Channel::Red: break; 85 | case +Channel::Green: break; 86 | case +Channel::Blue: break; 87 | } 88 | 89 | %% description = Optional Better Enums features, disabled by default for 90 | performance or compatibility reasons. 91 | 92 | ### Injecting tokens 93 | 94 | You can define `BETTER_ENUMS_CLASS_ATTRIBUTE` before declaring a Better Enum, to 95 | inject tokens into the class declaration. For example, in 96 | 97 | #define BETTER_ENUMS_CLASS_ATTRIBUTE __attribute__(foo) 98 | BETTER_ENUM(Channel, int, Red, Green, Blue) 99 | 100 | The resulting enum declaration will begin with 101 | 102 | class __attribute__(foo) Channel { 103 | -------------------------------------------------------------------------------- /doc/Performance.md: -------------------------------------------------------------------------------- 1 | ## Performance 2 | 3 | A basic performance test is run on 4 | [every compiler tested](${prefix}CompilerSupport.html#TestedConfigurations). It 5 | doesn't try to be very accurate — it just stress-tests the compiler once 6 | to get a rough idea of how long it takes to compile Better Enums. 7 | 8 | The files compared in the test are as follows: 9 | 10 | - [One file]($repo/blob/$ref/test/performance/4-declare_enums.cc) includes 11 | `enum.h` and declares 647 constants across 36 Better Enums. 12 | - The [other file]($repo/blob/$ref/test/performance/5-iostream.cc) *only* 13 | includes `iostream` and does nothing with it. 14 | 15 | The argument is that if compiling a bunch of Better Enums is faster, or about as 16 | fast as, including a single standard header such as `iostream`, then Better 17 | Enums is fast enough for general use. 18 | 19 | Results are given for select compilers and 20 | [configurations](${prefix}CompilerSupport.html#CompileTimeReflectionConfigurations) 21 | as ratios of how long it took to compile the Better Enums file to how long it 22 | took to compile the `iostream` file. The less the ratio, the better. Ratios less 23 | than 1 mean the enums compiled faster, and ratios greater than 1 mean `iostream` 24 | compiled faster. 25 | 26 | - clang 3.6, fast `constexpr`: 0.66 27 | - clang 3.6, full `constexpr`: 2.25 28 | - gcc 5.1, fast `constexpr`: 1.58 29 | - gcc 5.1, full `constexpr`: 4.23 30 | - VC2015RC, $cxx98: 1.18 31 | 32 | The time to merely include `enum.h` vary widely by compiler, with clang being 33 | by far the fastest. The ratios to `iostream` are given below. 34 | 35 | - clang 3.6: 0.15 36 | - gcc 5.1: 0.77 37 | - VC2015RC: 0.82 38 | 39 | On my test machines, clang processed the file in 40ms, gcc took 230ms, and 40 | VC2015 took 820ms. The first two are comparable to each other, but VC2015 runs 41 | on a different machine. 42 | 43 | --- 44 | 45 | In general, I am very sensitive to performance. Better Enums was originally 46 | developed in the context of a commercial project where slow running times *and* 47 | slow compilation times were unacceptable. I am continuing to develop it in this 48 | spirit. 49 | 50 | %% description = Better Enums compilation speed and performance testing results. 51 | -------------------------------------------------------------------------------- /doc/better-enums.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | color: #333; 4 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 5 | font-size: 18px; 6 | font-weight: 300; 7 | } 8 | 9 | pre, code, samp, h4, .contents ul { 10 | font-family: 11 | Consolas, "Liberation Mono", Menlo, "Courier New", Courier, monospace; 12 | } 13 | 14 | h1, .splash-text .self, nav .self { 15 | font-family: Georgia, Times, "Times New Roman", serif; 16 | } 17 | 18 | pre { 19 | background-color: #477093; 20 | padding: 1.5em 20px; 21 | border-radius: 5px; 22 | overflow: auto; 23 | color: rgba(255, 255, 255, 0.6); 24 | font-size: 78%; 25 | max-width: 84ex; 26 | margin-left: 1em; 27 | } 28 | 29 | pre em { 30 | font-style: normal; 31 | text-shadow: 0 -1px grey; 32 | color: white; 33 | } 34 | 35 | pre.comment { 36 | background-color: #EEF4F9; 37 | color: #888; 38 | border: 1px dashed #477093; 39 | } 40 | 41 | pre.comment em { 42 | color: #333; 43 | text-shadow: none; 44 | } 45 | 46 | code, samp { 47 | background-color: #EEF4F9; 48 | padding: 1px 3px; 49 | border-radius: 3px; 50 | font-size: 78%; 51 | } 52 | 53 | .container { 54 | margin-left: 150px; 55 | margin-right: 150px; 56 | } 57 | 58 | .main .container > * { 59 | max-width: 41em; 60 | } 61 | 62 | .index .main .container > * { 63 | max-width: none; 64 | } 65 | 66 | .main .container > .contents { 67 | max-width: none; 68 | } 69 | 70 | @media (max-width: 1400px) { 71 | .container { 72 | margin-left: auto; 73 | margin-right: auto; 74 | width: 1100px; 75 | } 76 | } 77 | 78 | @media (max-width: 1120px) { 79 | .container { 80 | margin-left: 10px; 81 | margin-right: 10px; 82 | width: auto; 83 | } 84 | } 85 | 86 | nav { 87 | position: fixed; 88 | width: 100%; 89 | z-index: 1; 90 | 91 | border-bottom: 1px solid #68a; 92 | color: white; 93 | opacity: 0.95; 94 | } 95 | 96 | nav, .spacer { 97 | padding: 0.75em 0; 98 | font-size: 14px; 99 | background-color: #222; 100 | } 101 | 102 | nav > * > span { 103 | float: right; 104 | opacity: 0.9; 105 | margin-right: 1em; 106 | } 107 | 108 | nav .self { 109 | font-size: 16px; 110 | padding-right: 0.5em; 111 | } 112 | 113 | nav a { 114 | margin-right: 2em; 115 | } 116 | 117 | nav a.first { 118 | font-weight: 600; 119 | font-size: 16px; 120 | } 121 | 122 | @media (max-width: 560px) { 123 | nav { 124 | position: initial; 125 | } 126 | 127 | .spacer { 128 | display: none; 129 | } 130 | } 131 | 132 | header { 133 | background-color: #4C6F8C; 134 | background: linear-gradient(#395E7E, #4A79A0); 135 | color: white; 136 | padding: 50px 0; 137 | } 138 | 139 | h1 { 140 | margin: 0; 141 | font-size: 60px; 142 | font-weight: normal; 143 | text-shadow: -2px 2px rgba(0, 0, 0, 0.3); 144 | } 145 | 146 | header section { 147 | float: left; 148 | } 149 | 150 | header section.buttons, header section.notes { 151 | float: right; 152 | margin-top: 1.75em; 153 | margin-left: 20px; 154 | } 155 | 156 | header section.notes { 157 | max-width: 20em; 158 | font-size: 75%; 159 | margin-right: 10px; 160 | opacity: 0.7; 161 | } 162 | 163 | header section.notes p { 164 | margin: 0.35em 0.35em; 165 | } 166 | 167 | header section.notes code { 168 | background-color: transparent; 169 | } 170 | 171 | header h2 { 172 | margin: 0; 173 | font-size: 24px; 174 | font-weight: 100; 175 | position: relative; 176 | left: 3px; 177 | } 178 | 179 | header h3 { 180 | margin: 0; 181 | font-size: 14px; 182 | font-weight: 300; 183 | position: relative; 184 | left: 4px; 185 | opacity: 0.5; 186 | } 187 | 188 | .buttons a { 189 | display: block; 190 | background-color: rgba(255, 255, 255, 0.2) !important; 191 | width: 10em; 192 | text-align: center; 193 | margin-bottom: 0.5em; 194 | padding: 0.25em 0; 195 | border-radius: 4px; 196 | } 197 | 198 | .buttons a:hover { 199 | background-color: white !important; 200 | color: #395E7E; 201 | } 202 | 203 | @media (max-width: 1000px) { 204 | header .notes { 205 | display: none; 206 | } 207 | 208 | header .buttons { 209 | margin-right: 2em; 210 | } 211 | } 212 | 213 | @media (max-width: 660px) { 214 | header .buttons { 215 | display: none; 216 | } 217 | } 218 | 219 | h2 { 220 | margin-top: 2em; 221 | font-size: 36px; 222 | font-weight: 300; 223 | } 224 | 225 | hr { 226 | border: none; 227 | height: 40px; 228 | } 229 | 230 | footer { 231 | font-size: 14px; 232 | margin-top: 100px; 233 | margin-bottom: 20px; 234 | opacity: 0.5; 235 | } 236 | 237 | a { 238 | text-decoration: none; 239 | color: white; 240 | /*background-color: red;*/ 241 | } 242 | 243 | a[href=""] { 244 | color: white !important; 245 | /*background-color: red !important;*/ 246 | } 247 | 248 | a:hover { 249 | text-decoration: underline; 250 | } 251 | 252 | header a:hover { 253 | text-decoration: none; 254 | } 255 | 256 | .main { 257 | margin-top: 50px; 258 | } 259 | 260 | .main a[href], footer a[href] { 261 | background-color: #edd; 262 | color: #844; 263 | letter-spacing: -0.5px; 264 | padding: 0 2px; 265 | border-radius: 3px; 266 | } 267 | 268 | header a[href], nav a[href] { 269 | color: inherit; 270 | background-color: transparent; 271 | letter-spacing: inherit; 272 | } 273 | 274 | a[href] code { 275 | background-color: transparent; 276 | } 277 | 278 | span.cpp, span.cc { 279 | font-size: 90%; 280 | } 281 | 282 | span.eleven { 283 | font-size: 85%; 284 | } 285 | 286 | span#note:target { 287 | background-color: yellow; 288 | } 289 | 290 | .pane { 291 | float: left; 292 | width: 49%; 293 | } 294 | 295 | .hack { 296 | clear: both; 297 | } 298 | 299 | .pane.left > * { 300 | margin-right: 10px; 301 | } 302 | 303 | .pane.right > * { 304 | margin-left: 10px; 305 | } 306 | 307 | .main h3 { 308 | font-size: 30px; 309 | margin-top: 2em; 310 | color: black; 311 | } 312 | 313 | h3 { 314 | margin-top: 0; 315 | margin-bottom: 0; 316 | font-weight: inherit; 317 | } 318 | 319 | h3.contents { 320 | font-size: 22px; 321 | } 322 | 323 | .pane pre { 324 | font-size: 14px; 325 | padding-top: 20px; 326 | padding-bottom: 20px; 327 | line-height: 1.15; 328 | } 329 | 330 | header { 331 | overflow: hidden; 332 | } 333 | 334 | header .container { 335 | position: relative; 336 | } 337 | 338 | div.back { 339 | position: absolute; 340 | bottom: -0.35em; 341 | left: -40px; 342 | font-size: 288px; 343 | font-weight: bold; 344 | letter-spacing: 20px; 345 | opacity: 0.1; 346 | text-shadow: -20px 20px #444; 347 | white-space: nowrap; 348 | } 349 | 350 | .panes { 351 | clear: both; 352 | margin-top: 2em; 353 | margin-bottom: 2em; 354 | overflow: auto; 355 | } 356 | 357 | .tutorial-footer { 358 | margin-top: 4em; 359 | } 360 | 361 | .tutorial-footer .next { 362 | font-weight: 100; 363 | font-size: 24px; 364 | } 365 | 366 | .tutorial-footer .next a[href] { 367 | font-weight: 300; 368 | } 369 | 370 | li { 371 | margin-top: 5px; 372 | } 373 | 374 | .blurbs { 375 | padding-left: 0; 376 | list-style-type: none; 377 | margin-top: 30px; 378 | } 379 | 380 | .blurbs > li { 381 | float: left; 382 | min-height: 5em; 383 | } 384 | 385 | @media (min-width: 1076px) { 386 | .blurbs > li { 387 | width: 28%; 388 | margin-right: 4%; 389 | } 390 | 391 | .blurbs > li.zero-mod-three { 392 | clear: both; 393 | margin-left: 2%; 394 | } 395 | } 396 | 397 | @media (max-width: 1075px) { 398 | .blurbs > li { 399 | width: 45%; 400 | margin-right: 4%; 401 | } 402 | 403 | .blurbs > li.zero-mod-two { 404 | clear: both; 405 | margin-left: 2%; 406 | } 407 | } 408 | 409 | @media (max-width: 620px) { 410 | .blurbs > li { 411 | float: none; 412 | width: 100%; 413 | min-height: 3em; 414 | } 415 | 416 | .blurbs > li.zero-mod-two { 417 | margin-left: 0; 418 | margin-right: 0; 419 | } 420 | } 421 | 422 | .blurbs strong { 423 | font-weight: inherit; 424 | font-size: 110%; 425 | } 426 | 427 | .blurbs em { 428 | display: block; 429 | margin-bottom: 1em; 430 | font-size: 80%; 431 | font-style: normal; 432 | } 433 | 434 | .act strong { 435 | font-weight: bold; 436 | font-size: 120%; 437 | } 438 | 439 | .act strong code { 440 | font-size: 110%; 441 | } 442 | 443 | .resources > li { 444 | margin-bottom: 3.5em; 445 | } 446 | 447 | .resources li ul, .resources li ol { 448 | margin-top: 1.5em; 449 | padding-left: 25px; 450 | } 451 | 452 | .splash-text { 453 | padding-top: 1em; 454 | font-size: 150%; 455 | font-weight: normal; 456 | color: #555; 457 | } 458 | 459 | .splash-text em { 460 | border-bottom: 1px solid #888; 461 | } 462 | 463 | .splash-text .self { 464 | color: #777; 465 | letter-spacing: 0; 466 | } 467 | 468 | .splash { 469 | float: right; 470 | text-align: center; 471 | white-space: nowrap; 472 | } 473 | 474 | .splash pre { 475 | display: inline-block; 476 | } 477 | 478 | .splash pre.left { 479 | text-align: right; 480 | color: black; 481 | font-weight: bold; 482 | background-color: transparent; 483 | padding-right: 0; 484 | } 485 | 486 | .splash pre.right { 487 | text-align: left; 488 | background-color: black; 489 | } 490 | 491 | @media (max-width: 1000px) { 492 | .splash { 493 | float: none; 494 | margin-left: -10%; 495 | } 496 | } 497 | 498 | @media (max-width: 700px) { 499 | .splash { 500 | margin-left: 0; 501 | } 502 | 503 | .splash pre.left { 504 | display: none; 505 | } 506 | } 507 | 508 | a[id] { 509 | display: block; 510 | position: relative; 511 | top: -25px; 512 | } 513 | 514 | h4 { 515 | font-weight: normal; 516 | margin-top: 3em; 517 | letter-spacing: -1px; 518 | color: #888; 519 | padding-top: 1em; 520 | white-space: nowrap; 521 | font-size: 125%; 522 | } 523 | 524 | h4 em { 525 | color: #555; 526 | font-style: normal; 527 | font-weight: bold; 528 | } 529 | 530 | .api ul.contents { 531 | -webkit-columns: 300px 3; 532 | -moz-columns: 300px 3; 533 | columns: 300px 3; 534 | } 535 | 536 | .api ul.contents > li { 537 | -webkit-column-break-inside: avoid; 538 | page-break-inside: avoid; 539 | break-inside: avoid; 540 | } 541 | 542 | .main h3 { 543 | margin-top: 4em; 544 | clear: both; 545 | } 546 | 547 | h3.contents { 548 | margin-top: 2em; 549 | } 550 | 551 | .index .main h3 { 552 | margin-top: 2em; 553 | } 554 | 555 | .api .contents ul { 556 | font-size: 75%; 557 | } 558 | 559 | .api .contents > li { 560 | margin-top: 0; 561 | padding-bottom: 1em; 562 | } 563 | 564 | .buttons-bar { 565 | background-color: #f4f4f4; 566 | padding-top: 0.5em; 567 | padding-bottom: 0.5em; 568 | height: 20px; 569 | } 570 | 571 | .buttons-bar a img { 572 | margin-right: 25px; 573 | } 574 | 575 | .buttons-bar iframe.gh-button { 576 | width: 95px; 577 | } 578 | 579 | .buttons-bar .tweet-share, 580 | .buttons-bar .gh-button { 581 | display: none; 582 | } 583 | 584 | .index .buttons-bar .tweet-share, 585 | .index .buttons-bar .gh-button { 586 | display: initial; 587 | } 588 | 589 | .buttons-bar iframe.gh-watch { 590 | width: 103px; 591 | } 592 | 593 | .external { 594 | font-size: 75%; 595 | } 596 | -------------------------------------------------------------------------------- /doc/demo/101-special-values.md: -------------------------------------------------------------------------------- 1 | ## Special values 2 | 3 | Suppose your project has a convention where each enum has special *invalid* and 4 | *default* values — for example, `Enum::Invalid` is *invalid*, and the 5 | first valid constant is *default*. With Better Enums, you can get the compiler 6 | to enforce the convention. At the end of this demo, we will have defined 7 | functions and templates that allow us to write: 8 | 9 | ~~~comment 10 | Channel channel = default_; 11 | Channel channel = invalid; 12 | 13 | void do_something(Channel channel); 14 | 15 | do_something(default_); 16 | do_something(invalid); 17 | ~~~ 18 | 19 | The compiler will compute default and invalid values automatically, but the 20 | programmer will also be able to override the choice. Obviously, the syntax above 21 | is very legible and maintainable — the intent is clear and your code base 22 | will respond automatically to changes in enum definitions. 23 | 24 | $internal_toc 25 | 26 | ### Invalid values 27 | 28 | Let's start by defining the invalid values. 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | Perhaps the convention is that the invalid value is usually called `Invalid`, 35 | but not for all enums. We will encode that using a template function. The 36 | unspecialized version will encode the default policy: 37 | 38 | template <typename Enum> 39 | constexpr Enum invalid_impl() { return Enum::Invalid; } 40 | 41 | A macro allows us to override the invalid value by specializing the template: 42 | 43 | #define OVERRIDE_INVALID(Enum, Value) \ 44 | template<> \ 45 | constexpr Enum invalid_impl<Enum>() { return Enum::Value; } 46 | 47 | Now, we can declare enums like these: 48 | 49 | BETTER_ENUM(Channel, int, Red, Green, Blue, Invalid) 50 | // Invalid is the invalid value by default 51 | 52 | BETTER_ENUM(Compression, int, Undefined, None, Huffman) 53 | OVERRIDE_INVALID(Compression, Undefined) 54 | 55 | and use them: 56 | 57 | static_assert(invalid_impl<Channel>() == +Channel::Invalid, ""); 58 | static_assert(invalid_impl<Compression>() == +Compression::Undefined, ""); 59 | 60 | This even supports enums that don't have an invalid value at all. As long as 61 | they don't have a constant called `Invalid`, you will get a compile-time error 62 | if you try to call `invalid_impl<>()` on them — as you probably should! 63 | 64 | ### Default values 65 | 66 | Perhaps here the convention is the first value that is not invalid is default, 67 | unless, again, overridden by the programmer. This can be encoded using only a 68 | slightly more complex template function for the general case: 69 | 70 | template <typename Enum> 71 | constexpr Enum default_impl() 72 | { 73 | return 74 | Enum::_size() < 2 ? 75 | throw std::logic_error("enum has no valid constants") : 76 | Enum::_values()[0] == invalid_impl() ? 77 | Enum::_values()[1] : 78 | Enum::_values()[0]; 79 | } 80 | 81 | The above code gives us the first value if it is not invalid, otherwise the 82 | second value. 83 | 84 | The companion macro for overriding the choice of default value is almost the 85 | same as it was for invalid. The difference is that we do an extra sanity check 86 | to make sure the programmer doesn't declare the invalid value to be the default. 87 | If the sanity check fails, we produce a nice error message. Again, we are 88 | assuming that this is dictated by policy. 89 | 90 | #define OVERRIDE_DEFAULT(Enum, Value) \ 91 | static_assert(Enum::Value != Enum::Invalid, \ 92 | #Enum ": default cannot equal invalid"); \ 93 | template<> \ 94 | constexpr Enum default_impl<Enum>() { return Enum::Value; } 95 | 96 | And, as before, the usage: 97 | 98 | static_assert(default_impl<Channel>() == +Channel::Red, ""); 99 | static_assert(default_impl<Compression>() == +Compression::None, ""); 100 | 101 | And, if you do 102 | 103 | BETTER_ENUM(Answer, int, Yes, No, Invalid) 104 | // OVERRIDE_DEFAULT(Answer, Invalid) 105 | 106 | you will get a helpful compile-time error saying 107 | `Answer: default cannot equal invalid`. 108 | 109 | ### Making the syntax nicer 110 | 111 | At this point, our policy is encoded by the ugly-looking functions 112 | `invalid_impl` and `default_impl`. We want a nicer syntax. The main reason we 113 | don't just use these functions directly is that the compiler wouldn't infer 114 | their template arguments from the context. For example, we would have to write 115 | things like 116 | 117 | ~~~comment 118 | Channel channel = invalid_impl(); 119 | ~~~ 120 | 121 | which is unfortunate, because it results in repetition. 122 | 123 | In this section, we introduce two global objects called `invalid` and `default_` 124 | that will implicitly convert to any Better Enum type, and provide the invalid 125 | or default value, respectively, when they do so. They will act as new 126 | "keywords". 127 | 128 | struct invalid_t { 129 | template <typename To> 130 | constexpr operator To() const { return invalid_impl(); } 131 | }; 132 | 133 | struct default_t { 134 | template <typename To> 135 | constexpr operator To() const { return default_impl(); } 136 | }; 137 | 138 | constexpr invalid_t invalid{}; 139 | constexpr default_t default_{}; 140 | 141 | As you can see, both of these provide the families of implicit conversions that 142 | we need. Now, we can test: 143 | 144 | static_assert(+Channel::Invalid == invalid, ""); 145 | static_assert(+Compression::Undefined == invalid, ""); 146 | 147 | static_assert(+Channel::Red == default_, ""); 148 | static_assert(+Compression::None == default_, ""); 149 | 150 | Finally, we can have nice code such as this: 151 | 152 | void dump(Channel channel) 153 | { 154 | std::cout << channel._to_string() << std::endl; 155 | } 156 | 157 | int main() 158 | { 159 | dump(invalid); 160 | 161 | Channel channel = default_; 162 | dump(channel); 163 | 164 | return 0; 165 | } 166 | 167 | --- 168 | 169 | There are many possible variations of these policies, but I think most of them 170 | can be encoded in a reasonable fashion using the tools Better Enums provides. 171 | Enjoy! 172 | 173 | %% description = An example that uses Better Enums compile-time reflection to 174 | create invalid and default values for each enum, enforced statically by the 175 | compiler, for readability and maintainability. 176 | -------------------------------------------------------------------------------- /doc/demo/103-bitset.md: -------------------------------------------------------------------------------- 1 | ## Bit sets 2 | 3 | If you want to use `std::bitset` or a similar library to have enums be keys into 4 | a bit set, you need to know the number of bits at compile time. You can easily 5 | automate this with Better Enums, even when constants are not declared in 6 | increasing order. 7 | 8 | --- 9 | 10 | We simply need to find the maximum value of any given enum type. 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | template <typename Enum> 17 | constexpr Enum max_loop(Enum accumulator, size_t index) 18 | { 19 | return 20 | index >= Enum::_size() ? accumulator : 21 | Enum::_values()[index] > accumulator ? 22 | max_loop(Enum::_values()[index], index + 1) : 23 | max_loop(accumulator, index + 1); 24 | } 25 | 26 | template <typename Enum> 27 | constexpr Enum max() 28 | { 29 | return max_loop(Enum::_values()[0], 1); 30 | } 31 | 32 | And use that to declare a bit set template: 33 | 34 | template <typename Enum> 35 | using EnumSet = std::bitset<max()._to_integral() + 1>; 36 | 37 | Now, we can have bit sets that are wide enough to hold whatever range we 38 | declared. We just declare enums, and the numeric values of their constants will 39 | be bit indices. The rest is straightforward. 40 | 41 | BETTER_ENUM(EFLAGS, int, 42 | Carry, Parity = 2, Adjust = 4, Zero, Sign, Trap, Interrupt, Direction, 43 | Overflow, NestedTask = 14, Resume = 16, V8086, AlignmentCheck, 44 | CPUIDPresent = 21) 45 | 46 | int main() 47 | { 48 | EnumSet<EFLAGS> eflags = 1 << EFLAGS::Carry | 1 << EFLAGS::Zero; 49 | 50 | if (eflags.test(EFLAGS::Carry)) 51 | eflags.set(EFLAGS::Trap); 52 | 53 | std::cout << eflags << std::endl; 54 | 55 | return 0; 56 | } 57 | 58 | --- 59 | 60 | If we want bit sets of fixed known width instead, we can use the code above to 61 | check that we haven't declared any bit indices out of range: 62 | 63 | ~~~comment 64 | static_assert(max()._to_integral() < 32, 65 | "some bit indices are out of range"); 66 | ~~~ 67 | 68 | %% description = Finding the maximum value of a Better Enum for use in declaring 69 | statically-sized bit set types. 70 | -------------------------------------------------------------------------------- /doc/demo/104-quine.md: -------------------------------------------------------------------------------- 1 | ## Semi-quine 2 | 3 | Let's make a Better Enum assemble its own definition in memory. It won't be 4 | literally as defined, since we will lose the exact initializer expressions, but 5 | we will be able to preserve the numeric values. We will reserve the memory 6 | buffer for the definition at compile time. 7 | 8 | Ok, so it's not really a quine, because we won't be writing all the code needed 9 | to generate the definition to the buffer as well. And, there are better ways to 10 | dump the definition than shown here. You could simply define a macro that 11 | expands to an `BETTER_ENUM` declaration and also stringizes it. 12 | 13 | But that's not the point here. The point of this page is to show some of the 14 | reflective capabilities of Better Enums, so you can adapt them for cases where a 15 | macro is not sufficient :) 16 | 17 | $internal_toc 18 | 19 | --- 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | --- 26 | 27 | First, we will need 28 | [full compile-time reflection](${prefix}OptInFeatures.html#CompileTimeNameTrimming), 29 | since we will be calling `_to_string`. Let's make sure it's enabled by defining 30 | `BETTER_ENUMS_CONSTEXPR_TO_STRING` before including `enum.h`: 31 | 32 | #ifndef BETTER_ENUMS_CONSTEXPR_TO_STRING 33 | #define BETTER_ENUMS_CONSTEXPR_TO_STRING 34 | #endif 35 | 36 | #include 37 | 38 | Now, let's declare some enums to dump later: 39 | 40 | BETTER_ENUM(Channel, int, Red, Green, Blue) 41 | BETTER_ENUM(Depth, int, TrueColor = 1, HighColor = 0) 42 | 43 | 44 | 45 | ### Computing the size of the buffer 46 | 47 | First, we need to be able to get the length of each declaration above. We will 48 | assume that the underlying type is always `int`, and that the spacing convention 49 | is followed as above. 50 | 51 | First, let's get the lengths of basic components: 52 | 53 | // Returns the length of the string representation of the number n 54 | constexpr size_t value_length(int n, int bound = 10, size_t digits = 1) 55 | { 56 | return 57 | n < bound ? digits : value_length(n, bound * 10, digits + 1); 58 | } 59 | 60 | // Returns the length of s 61 | constexpr size_t string_length(const char *s, size_t index = 0) 62 | { 63 | return s[index] == '\0' ? index : string_length(s, index + 1); 64 | } 65 | 66 | Now, the length of the constant declaration. Here is where we lose information 67 | about initializers. We are going to format the constant declarations like this: 68 | 69 | ~~~comment 70 | Red = 0, Green = 1, Blue = 2 71 | TrueColor = 1, HighColor = 0 72 | ~~~ 73 | 74 | This is because Better Enums doesn't provide a way to know what the exact 75 | initializer was or whether there even was one — just the numeric value of 76 | each constant. If we were trying to be clever, we could avoid formatting 77 | initializers for sequential values, but I won't go through this exercise here. 78 | 79 | // Returns the length of the constants portion of the declaration of Enum, 80 | // as described above. 81 | template <typename Enum> 82 | constexpr size_t constants_length(size_t index = 0, size_t accumulator = 0) 83 | { 84 | return 85 | index >= Enum::_size() ? accumulator : 86 | constants_length( 87 | index + 1, accumulator 88 | + string_length(", ") 89 | + string_length(Enum::_names()[index]) 90 | + string_length(" = ") 91 | + value_length( 92 | Enum::_values()[index]._to_integral())); 93 | } 94 | 95 | Finally, we can combine these to get the length of the formatted declaration of 96 | the whole enum: 97 | 98 | // Returns the length of the whole declaration of Enum, assuming the 99 | // underlying type is int, and the constants are initialized as assumed by 100 | // constants_length() above. 101 | template <typename Enum> 102 | constexpr size_t declaration_length() 103 | { 104 | return 105 | string_length("BETTER_ENUM(") 106 | + string_length(Enum::_name()) 107 | + string_length(", int") 108 | + constants_length() 109 | + string_length(")"); 110 | } 111 | 112 | 113 | 114 | ### Formatting the enums 115 | 116 | Now, we can declare the buffers. The memory will be reserved at load time by the 117 | binary's loader. The extra one byte in each buffer is for the null terminator. 118 | 119 | char channel_definition[declaration_length() + 1]; 120 | char depth_definition[declaration_length() + 1]; 121 | 122 | Let's also create the formatting function. This is executed at run time, but we 123 | will be giving it pointers to our statically-allocated buffers. It will format 124 | the enum declaration and then return the number of bytes it wrote to the buffer, 125 | so that we can do a sanity check on it. 126 | 127 | template <typename Enum> 128 | size_t format(char *buffer) 129 | { 130 | size_t offset = 0; 131 | 132 | offset += std::sprintf(buffer, "BETTER_ENUM(%s, int", Enum::_name()); 133 | 134 | for (Enum value : Enum::_values()) { 135 | offset += 136 | std::sprintf(buffer + offset, 137 | ", %s = %i", 138 | value._to_string(), value._to_integral()); 139 | } 140 | 141 | offset += std::sprintf(buffer + offset, ")"); 142 | 143 | return offset; 144 | } 145 | 146 | 147 | 148 | ### Checking our work 149 | 150 | Now, we can write and run this code. 151 | 152 | int main() 153 | { 154 | size_t channel_length = format(channel_definition); 155 | assert(channel_length + 1 == sizeof(channel_definition)); 156 | 157 | size_t depth_length = format(depth_definition); 158 | assert(depth_length + 1 == sizeof(depth_definition)); 159 | 160 | std::cout << channel_definition << std::endl; 161 | std::cout << depth_definition << std::endl; 162 | 163 | return 0; 164 | } 165 | 166 | It prints: 167 | 168 | ~~~comment 169 | BETTER_ENUM(Channel, int, Red = 0, Green = 1, Blue = 2) 170 | BETTER_ENUM(Depth, int, TrueColor = 1, HighColor = 0) 171 | ~~~ 172 | 173 | %% description = Have a Better Enum print its own definition. Shows how to 174 | compute the amount of memory necessary from the reflective information provided 175 | by a Better Enum. 176 | -------------------------------------------------------------------------------- /doc/demo/105-c++17-reflection.md: -------------------------------------------------------------------------------- 1 | ## C++17 reflection proposal 2 | 3 | *You can try this demo [live online][live].* 4 | 5 | Better Enums can be used to implement the enums portion of the 6 | [$cxx17 reflection proposal N4428][n4428] in $cxx11. N4428 proposes the 7 | following traits interface: 8 | 9 | ~~~comment 10 | namespace std { 11 | 12 | template 13 | struct enum_traits { 14 | struct enumerators { 15 | constexpr static size_t size; 16 | 17 | template 18 | struct get { 19 | constexpr string_literal identifier; 20 | constexpr static E value; 21 | }; 22 | }; 23 | }; 24 | 25 | } 26 | ~~~ 27 | 28 | So, the basic usage would be: 29 | 30 | ~~~comment 31 | enum class Foo {A, B, C}; 32 | 33 | constexpr size_t size = 34 | std::enum_traits::enumerators::size; 35 | 36 | constexpr Foo value_0 = 37 | std::enum_traits::enumerators::get<0>::value; 38 | 39 | constexpr string_literal name_1 = 40 | std::enum_traits::enumerators::get<1>::identifier; 41 | ~~~ 42 | 43 | Resulting in the values `3`, `Foo::A`, and `"B"`, respectively. 44 | 45 | --- 46 | 47 | The interface is implemented in the optional header file 48 | [`extra/better-enums/n4428.h`][header]. There is a necessary difference: the 49 | interface is only available for enums declared through the `BETTER_ENUM` macro. 50 | This is because the macro is what generates the information necessary for 51 | reflection. 52 | 53 | ### Demo 54 | 55 | So, with that out of the way, we can do a little test. Let's assume that 56 | `extra/` has been added as a directory to search for include files. 57 | 58 | #ifndef BETTER_ENUMS_CONSTEXPR_TO_STRING 59 | #define BETTER_ENUMS_CONSTEXPR_TO_STRING 60 | #endif 61 | 62 | #include 63 | #include <enum.h> 64 | #include <better-enums/n4428.h> 65 | 66 | --- 67 | 68 | Let's declare an enum: 69 | 70 | BETTER_ENUM(Channel, char, Red = 1, Green, Blue) 71 | 72 | ...and try N4428: 73 | 74 | constexpr std::size_t size = 75 | std::enum_traits<Channel>::enumerators::size; 76 | 77 | constexpr Channel value_0 = 78 | std::enum_traits<Channel>::enumerators::get<0>::value; 79 | 80 | constexpr Channel value_1 = 81 | std::enum_traits<Channel>::enumerators::get<1>::value; 82 | 83 | constexpr const char *identifier_2 = 84 | std::enum_traits<Channel>::enumerators::get<2>::identifier; 85 | 86 | ...and check the results: 87 | 88 | static_assert(size == 3, ""); 89 | 90 | static_assert(value_0 == +Channel::Red, ""); 91 | static_assert(value_1 == +Channel::Green, ""); 92 | 93 | int main() 94 | { 95 | std::cout << identifier_2 << std::endl; 96 | return 0; 97 | } 98 | 99 | That prints `Blue`, as you would expect. 100 | 101 | ### Quirk 102 | 103 | The reason for the `#define` in the code above is that there is one quirk: 104 | the interface above is available only for Better Enums for which 105 | [compile-time name trimming][slow-enum] is enabled — those declared when 106 | `BETTER_ENUMS_CONSTEXPR_TO_STRING` was defined, or declared with the `SLOW_ENUM` 107 | variant of `BETTER_ENUM`. As mentioned on the linked page, the reason 108 | compile-time name trimming is not the default is that, while still pretty fast, 109 | it is four times slower than program-startup-time name trimming. The latter is 110 | the default. 111 | 112 | Despite the above, a variation on the interface is available for enums without 113 | compile-time name trimming: 114 | 115 | ~~~comment 116 | namespace std { 117 | 118 | template 119 | struct enum_traits { 120 | struct enumerators { 121 | constexpr static size_t size; 122 | 123 | template 124 | struct get { 125 | constexpr const char *identifier; 126 | constexpr static E value; 127 | }; 128 | 129 | // For enums without compile-time name trimming. 130 | template 131 | struct get_alt { 132 | static const char* identifier(); 133 | constexpr static E value; 134 | }; 135 | }; 136 | }; 137 | 138 | } 139 | ~~~ 140 | 141 | As you can see, the difference is that `identifier` is a non-`constexpr` 142 | function, and you have to access it through `get_alt`. 143 | 144 | ~~~comment 145 | // Without compile-time name trimming. 146 | BETTER_ENUM(Depth, int, HighColor, TrueColor) 147 | 148 | int main() 149 | { 150 | std::cout 151 | << std::enum_traits::enumerators::get_alt<1>::identifier() 152 | << std::endl; 153 | 154 | return 0; 155 | } 156 | ~~~ 157 | 158 | ### The future 159 | 160 | N4428 is the fourth in a series of revisions: [N3815][n3815], [N4027][n4027], 161 | [N4113][n4113], N4428. If there are more revisions that change the proposal for 162 | enums, I will try to implement those as well. 163 | 164 | [n4428]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4428.pdf 165 | [n4113]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4113.pdf 166 | [n4027]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4027.pdf 167 | [n3815]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3815.html 168 | [slow-enum]: ${prefix}OptInFeatures.html#CompileTimeNameTrimming 169 | [header]: https://github.com/aantron/better-enums/blob/$ref/extra/better-enums/n4428.h 170 | [live]: http://melpon.org/wandbox/permlink/QelcwZNLi4gIx8Ux 171 | 172 | %% description = Approximate implementation of N4428 enum reflection based on 173 | Better Enums. 174 | -------------------------------------------------------------------------------- /doc/docs.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import glob 4 | import re 5 | import shutil 6 | import string 7 | import transform 8 | import os 9 | import os.path 10 | import sys 11 | import urllib 12 | 13 | TEMPLATE_DIRECTORY = "template" 14 | OUTPUT_DIRECTORY = "html" 15 | TUTORIAL_DIRECTORY = "tutorial" 16 | DEMO_DIRECTORY = "demo" 17 | CXX_EXTENSION = "cc" 18 | 19 | templates = {} 20 | 21 | generated = [] 22 | 23 | def load_templates(): 24 | listing = os.listdir(TEMPLATE_DIRECTORY) 25 | 26 | for file in listing: 27 | title = os.path.splitext(file)[0] 28 | stream = open(os.path.join(TEMPLATE_DIRECTORY, file)) 29 | 30 | try: 31 | templates[title] = stream.read() 32 | finally: 33 | stream.close() 34 | 35 | def apply_template(template, args = {}, lax = False, **kwargs): 36 | if not lax: 37 | return string.Template(template).substitute(args, **kwargs) 38 | else: 39 | return string.Template(template).safe_substitute(args, **kwargs) 40 | 41 | def scrub_comments(text): 42 | return re.sub("|$)", "", 43 | text, flags = re.DOTALL) 44 | 45 | def path_to_html(relative_path): 46 | return os.path.splitext(relative_path)[0] + ".html" 47 | 48 | def path_to_md(relative_path): 49 | return os.path.splitext(relative_path)[0] + ".md" 50 | 51 | def path_to_output(relative_path): 52 | path = os.path.join(OUTPUT_DIRECTORY, relative_path) 53 | 54 | directory = os.path.dirname(path) 55 | if not os.path.lexists(directory): 56 | os.makedirs(directory) 57 | 58 | return path 59 | 60 | def remove_output_directory(): 61 | path = OUTPUT_DIRECTORY 62 | if os.path.lexists(path): 63 | shutil.rmtree(path) 64 | 65 | def compose_page(relative_path, definitions): 66 | definitions.update(templates) 67 | 68 | html_file = path_to_html(relative_path) 69 | 70 | if html_file == "index.html": 71 | canonical = templates["location"] 72 | definitions["title"] = \ 73 | definitions["project"] + " - " + definitions["title"] 74 | else: 75 | canonical = os.path.join(templates["location"], html_file) 76 | definitions["title"] = \ 77 | definitions["title"] + " - " + definitions["project"] 78 | 79 | generated.append(canonical) 80 | 81 | prefix = re.sub("[^/]+", r"..", os.path.split(relative_path)[0]) 82 | if len(prefix) > 0: 83 | prefix += "/" 84 | 85 | definitions["canonical"] = canonical 86 | definitions["prefix"] = prefix 87 | 88 | definitions["quoted_url"] = urllib.quote(definitions["canonical"], "") 89 | definitions["quoted_title"] = urllib.quote(definitions["title"], "") 90 | 91 | if "class" not in definitions: 92 | definitions["class"] = "" 93 | 94 | text = templates["page"] 95 | 96 | while True: 97 | new_text = scrub_comments(text) 98 | new_text = re.sub("\$\$", "$$$$", new_text) 99 | new_text = apply_template(new_text, definitions) 100 | 101 | if new_text == text: 102 | text = apply_template(new_text, definitions) 103 | break 104 | 105 | text = new_text 106 | 107 | text = "\n\n" + text 108 | 109 | return text 110 | 111 | def write_file(relative_path, text): 112 | path = path_to_output(relative_path) 113 | 114 | stream = open(path, "w") 115 | try: 116 | stream.write(text) 117 | finally: 118 | stream.close() 119 | 120 | def write_page(relative_path, text): 121 | write_file(path_to_html(relative_path), text) 122 | 123 | def copy_static_file(relative_path): 124 | output_path = path_to_output(relative_path) 125 | shutil.copy(relative_path, output_path) 126 | 127 | def read_extended_markdown(relative_path): 128 | md_file = path_to_md(relative_path) 129 | 130 | stream = open(md_file) 131 | try: 132 | text = stream.read() 133 | return transform.to_html(text) 134 | finally: 135 | stream.close() 136 | 137 | def compose_general_page(relative_path): 138 | definitions = read_extended_markdown(relative_path) 139 | text = compose_page(relative_path, definitions) 140 | write_page(relative_path, text) 141 | 142 | def list_general_pages(): 143 | return filter( 144 | lambda s: not os.path.splitext(os.path.basename(s))[0].isupper(), 145 | glob.glob("*.md")) 146 | 147 | def process_threaded(directory): 148 | sources = glob.glob(os.path.join(directory, "*.md")) 149 | 150 | contents = [] 151 | for file in sources: 152 | definitions = read_extended_markdown(file) 153 | 154 | transformed_title = transform.camel_case(definitions["title"]) 155 | html_file = os.path.join(directory, transformed_title + ".html") 156 | 157 | source_file = \ 158 | os.path.splitext(os.path.basename(file))[0] + "." + CXX_EXTENSION 159 | source_link = "$repo/blob/$ref/example/" + source_file 160 | 161 | definitions[directory + "_body"] = definitions["body"] 162 | definitions["body"] = templates[directory] 163 | definitions["source"] = source_link 164 | 165 | contents.append((definitions["title"], html_file, definitions)) 166 | 167 | index = 0 168 | for title, html_file, definitions in contents: 169 | if index < len(contents) - 1: 170 | next_title, next_file, _ = contents[index + 1] 171 | definitions["computed_next"] = templates["next"] 172 | definitions["next_link"] = next_file 173 | definitions["next_title"] = next_title 174 | else: 175 | definitions["computed_next"] = templates["last"] 176 | 177 | text = compose_page(html_file, definitions) 178 | write_page(html_file, text) 179 | 180 | index += 1 181 | 182 | text = "" 183 | for title, html_file, _ in contents: 184 | item = apply_template(templates["tocitem"], {}, True, 185 | link = html_file, title = title) 186 | text += item + "\n" 187 | 188 | templates[directory + "_toc"] = text 189 | 190 | def generate_sitemap(): 191 | text = "" 192 | 193 | text += "\n" 194 | text += "\n" 195 | 196 | for url in generated: 197 | text += " \n" 198 | text += " %s\n" % url 199 | 200 | if ".html" not in url: 201 | text += " 1.0\n" 202 | 203 | text += " \n" 204 | 205 | text += "\n" 206 | 207 | write_file("sitemap.xml", text) 208 | 209 | def main(): 210 | load_templates() 211 | 212 | if not (len(sys.argv) >= 2 and sys.argv[1] == "--web"): 213 | templates["ga"] = "" 214 | 215 | remove_output_directory() 216 | 217 | process_threaded(TUTORIAL_DIRECTORY) 218 | process_threaded(DEMO_DIRECTORY) 219 | 220 | general_pages = list_general_pages() 221 | for page in general_pages: 222 | compose_general_page(page) 223 | 224 | copy_static_file("better-enums.css") 225 | 226 | generate_sitemap() 227 | 228 | if __name__ == "__main__": 229 | main() 230 | -------------------------------------------------------------------------------- /doc/image/sample.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aantron/better-enums/520d8ee39037c9c94aa6e708a4fd6c0fa313ae80/doc/image/sample.gif -------------------------------------------------------------------------------- /doc/index.md: -------------------------------------------------------------------------------- 1 |
2 |
enable
  3 | 
  4 | declare
  5 | 
  6 | 
  7 | parse
  8 | format
  9 | 
 10 | 
 11 | count
 12 | iterate
 13 | 
 14 | 
 15 | 
 16 | 
 17 | switch
 18 | 
 19 | 
 20 | 
 21 | 
 22 | 
 23 | 
 24 | safe cast
 25 | 
 26 | 
 27 | during
 28 | compilation
 29 | 
30 |
#include <enum.h>
 31 | 
 32 | BETTER_ENUM(Channel, int, Red = 1, Green, Blue)
 33 | 
 34 | 
 35 | Channel     c = Channel::_from_string("Red");
 36 | const char  *s = c._to_string();
 37 | 
 38 | 
 39 | size_t      n = Channel::_size();
 40 | for (Channel c : Channel::_values()) {
 41 |     run_some_function(c);
 42 | }
 43 | 
 44 | 
 45 | switch (c) {
 46 |     case Channel::Red:    // ...
 47 |     case Channel::Green:  // ...
 48 |     case Channel::Blue:   // ...
 49 | }
 50 | 
 51 | 
 52 | Channel     c = Channel::_from_integral(3);
 53 | 
 54 | 
 55 | constexpr Channel c =
 56 |     Channel::_from_string("Blue");
57 |
58 | 59 |

60 | $be is a single, lightweight header file that makes your compiler generate 61 | reflective enum types. 62 |

63 | 64 | That means you can easily convert enums to and from strings, 65 | validate them, and loop over them. In $cxx11, you can do it all at 66 | compile time. 67 | 68 | It's what built-in enums ought to support. Better Enums simply adds the missing 69 | features. And, it is based on the best known techniques, thoroughly tested, 70 | fast, portable, and documented exhaustively. 71 | 72 | To use it, just include enum.h and begin the 73 | [tutorial](${prefix}tutorial/HelloWorld.html)! 74 | 75 |
76 | 77 | ### Highlights 78 | 79 |
    80 |
  • 81 | Unobtrusive syntax 82 | 83 | No ugly macros. Use initializers as with built-in enums. 84 | Internal members have underscores to avoid clashing with your constant 85 | names. 86 | 87 |
  • 88 | 89 |
  • 90 | No external dependencies 91 | 92 | Uses only standard $cxx. Installation is simple — just download 93 | enum.h. There are no objects or libraries to link with. 94 | 95 |
  • 96 | 97 |
  • 98 | No generator program needed 99 | 100 | Just include enum.h. It's a metaprogram executed by your 101 | compiler. 102 | 103 |
  • 104 | 105 |
  • 106 | Plays nice with switch 107 | 108 | Use a Better Enum like a built-in enum, and still have the 109 | compiler do case checking. 110 | 111 |
  • 112 | 113 |
  • 114 | Don't repeat yourself 115 | 116 | No more unmaintanable maps or switch statements for 117 | converting enums to strings. 118 | 119 |
  • 120 | 121 |
  • 122 | Non-contiguous sequences 123 | 124 | Iteration and counting are much easier to maintain than with an extra 125 | Count constant and assuming a dense range. 126 | 127 |
  • 128 | 129 |
  • 130 | Fast compilation 131 | 132 | Much less impact on build time than even just including 133 | iostream. enum.h is only slightly more than 1000 134 | lines long. 135 | 136 |
  • 137 | 138 |
  • 139 | Compile-time reflection 140 | 141 | Have the compiler do additional enum processing using your own 142 | templates or constexpr functions. 143 | 144 |
  • 145 | 146 |
  • 147 | Uniform interface for $cxx98, $cxx11 148 | 149 | Scoped, sized, reflective enums for $cxx98, and an easy upgrade 150 | path. 151 | 152 |
  • 153 | 154 |
  • 155 | Stream operators 156 | 157 | Write enum names directly to std::cout or use 158 | boost::lexical_cast. 159 | 160 |
  • 161 | 162 |
  • 163 | Free and open source 164 | 165 | Released under the BSD license for use in any project, free or commercial. 166 | 167 |
  • 168 |
169 | 170 |
171 | 172 | ### Documentation 173 | 174 | 210 | 211 |
212 | 213 | %% title = Clean reflective enums for C++ 214 | 215 | %% description = Reflective enums in a single header file, with clean syntax. 216 | The enums can be converted to string, iterated, and counted, at run time or 217 | as part of metaprogramming. Free and open source under the BSD license. 218 | 219 | %% class = index 220 | -------------------------------------------------------------------------------- /doc/template/be.tmpl: -------------------------------------------------------------------------------- 1 | Better Enums -------------------------------------------------------------------------------- /doc/template/cxx.tmpl: -------------------------------------------------------------------------------- 1 | C++ -------------------------------------------------------------------------------- /doc/template/cxx11.tmpl: -------------------------------------------------------------------------------- 1 | C++11 -------------------------------------------------------------------------------- /doc/template/cxx14.tmpl: -------------------------------------------------------------------------------- 1 | C++14 -------------------------------------------------------------------------------- /doc/template/cxx17.tmpl: -------------------------------------------------------------------------------- 1 | C++17 -------------------------------------------------------------------------------- /doc/template/cxx98.tmpl: -------------------------------------------------------------------------------- 1 | C++98 -------------------------------------------------------------------------------- /doc/template/demo.tmpl: -------------------------------------------------------------------------------- 1 |

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

6 | 7 | $demo_body 8 | 9 | 14 | -------------------------------------------------------------------------------- /doc/template/download.tmpl: -------------------------------------------------------------------------------- 1 | href="https://raw.githubusercontent.com/aantron/better-enums/$ref/enum.h" 2 | download -------------------------------------------------------------------------------- /doc/template/footer.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |
6 | Copyright © 2015-2024 Anton Bachin. Released under the BSD 2-clause 7 | license. See 8 | 9 | LICENSE. 10 |
11 | This page is part of the documentation for Better Enums $version. 12 |
13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /doc/template/ga.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/template/ghfork.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/template/ghstar.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/template/ghwatch.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/template/header.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | $title 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | $ga 17 | 18 | 19 | 20 | 21 | 30 | 31 |
 
32 | 33 |
34 |
35 |
36 |

Better Enums

37 |

Reflective compile-time enums for $cxx

38 |

Open-source under the BSD license

39 |
40 | 41 |
42 |

Version $version

43 |

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

44 |

45 | Visit the GitHub repo for issues, feedback, and the latest development. 46 |

47 |
48 | 49 |
50 | Download enum.h 51 | GitHub 52 |
53 |
54 |
55 | 56 |
57 |
58 | -------------------------------------------------------------------------------- /doc/template/last.tmpl: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /doc/template/location.tmpl: -------------------------------------------------------------------------------- 1 | http://aantron.github.io/better-enums -------------------------------------------------------------------------------- /doc/template/next.tmpl: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /doc/template/page.tmpl: -------------------------------------------------------------------------------- 1 | $header 2 | 3 | $body 4 | 5 | $footer 6 | -------------------------------------------------------------------------------- /doc/template/project.tmpl: -------------------------------------------------------------------------------- 1 | Better Enums -------------------------------------------------------------------------------- /doc/template/ref.tmpl: -------------------------------------------------------------------------------- 1 | 0.11.3 -------------------------------------------------------------------------------- /doc/template/repo.tmpl: -------------------------------------------------------------------------------- 1 | https://github.com/aantron/better-enums -------------------------------------------------------------------------------- /doc/template/tocitem.tmpl: -------------------------------------------------------------------------------- 1 |
  • $title
  • -------------------------------------------------------------------------------- /doc/template/tutorial.tmpl: -------------------------------------------------------------------------------- 1 |

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

    6 | 7 | $tutorial_body 8 | 9 | $computed_next 10 | -------------------------------------------------------------------------------- /doc/template/version.tmpl: -------------------------------------------------------------------------------- 1 | 0.11.3 -------------------------------------------------------------------------------- /doc/transform.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | # Reads mixed source/text markdown files and outputs HTML and/or C++ source. 4 | # Usage: 5 | # ./transform.py --o-html OUT.html --o-cxx OUT.cc in.md 6 | 7 | import argparse 8 | import mistune 9 | import re 10 | import sys 11 | 12 | parser = argparse.ArgumentParser(description = "Translate markdown pages.", 13 | epilog = "At least one output file must be specified") 14 | parser.add_argument("--o-html", metavar = "HTML", dest = "html_file", 15 | help = "HTML output file name") 16 | parser.add_argument("--o-cxx", metavar = "CXX", dest = "cxx_file", 17 | help = "C++ output file name") 18 | parser.add_argument("md_file", metavar = "MD") 19 | 20 | def pretty_print(text, prefix, start_with_prefix = True): 21 | words = text.split() 22 | 23 | index = 0 24 | 25 | if start_with_prefix: 26 | result = prefix 27 | else: 28 | result = "" 29 | 30 | while index < len(words): 31 | column = len(prefix) 32 | 33 | result += words[index] 34 | column += len(words[index]) 35 | index += 1 36 | 37 | while index < len(words) and column + 1 + len(words[index]) <= 80: 38 | result += " " 39 | result += words[index] 40 | column += 1 + len(words[index]) 41 | index += 1 42 | 43 | result += "\n" 44 | if index < len(words): 45 | result += prefix 46 | 47 | return result 48 | 49 | def camel_case(text): 50 | components = re.split("[ -]+", text) 51 | components = map(lambda s: s.capitalize(), components) 52 | result = "".join(components) 53 | result = filter(lambda c: c not in ",!:-$()?", result) 54 | return result 55 | 56 | class HtmlRenderer(mistune.Renderer): 57 | def __init__(self): 58 | super(HtmlRenderer, self).__init__() 59 | self._definitions = {} 60 | self._table_of_contents = [] 61 | self._second_level = None 62 | 63 | def header(self, text, level, raw = None): 64 | if level == 2: 65 | if "title" not in self._definitions: 66 | self._definitions["title"] = text 67 | 68 | if level == 3: 69 | anchor_name = camel_case(text) 70 | prefix = "" % anchor_name 71 | 72 | self._second_level = [] 73 | self._table_of_contents.append( 74 | (anchor_name, text, self._second_level)) 75 | 76 | elif level == 4: 77 | anchor_text = re.search("(.+)", text).group(1) 78 | anchor_name = camel_case(anchor_text) 79 | prefix = "" % anchor_name 80 | 81 | self._second_level.append((anchor_name, anchor_text)) 82 | 83 | else: 84 | prefix = "" 85 | 86 | return prefix + super(HtmlRenderer, self).header(text, level, raw) 87 | 88 | def paragraph(self, text): 89 | if text.startswith("%%"): 90 | tokens = text[2:].split("=", 1) 91 | if len(tokens) == 2: 92 | pass 93 | key = tokens[0].strip() 94 | value = tokens[1].strip() 95 | 96 | self._definitions[key] = value 97 | 98 | return "" 99 | 100 | return super(HtmlRenderer, self).paragraph(text) 101 | 102 | def block_code(self, code, lang): 103 | escaped = mistune.escape(re.sub("\n*$", "", code)) 104 | replaced = re.sub("<em>", "", escaped) 105 | replaced = re.sub("</em>", "", replaced) 106 | 107 | if lang == "comment": 108 | start_tag = "
    "
    109 |         else:
    110 |             start_tag = "
    "
    111 | 
    112 |         return start_tag + replaced + "
    " 113 | 114 | def definitions(self): 115 | toc_text = "" 116 | toc_text += "

    Contents

    " 117 | toc_text += "
      " 118 | 119 | for entry in self._table_of_contents: 120 | anchor, title, second_level = entry 121 | toc_text += "
    • %s" % (anchor, title) 122 | 123 | if len(second_level) > 0: 124 | toc_text += "
        " 125 | for entry in second_level: 126 | toc_text += "
      • %s
      • " % entry 127 | toc_text += "
      " 128 | 129 | toc_text += "
    • " 130 | 131 | toc_text += "
    " 132 | 133 | self._definitions["internal_toc"] = toc_text 134 | 135 | return self._definitions 136 | 137 | def to_html(text): 138 | renderer = HtmlRenderer() 139 | html = mistune.Markdown(renderer = renderer).render(text) 140 | definitions = renderer.definitions() 141 | definitions["body"] = html 142 | return definitions 143 | 144 | def clean_text(text): 145 | text = re.sub("<(?P[^> ]+)[^>]*>(.*?)", "\g<2>", text) 146 | text = re.sub("<(?P[^> ]+)[^>]*>(.*?)", "\g<2>", text) 147 | text = re.sub("—", "-", text) 148 | text = re.sub("\$cxx", "C++", text) 149 | return text 150 | 151 | class CxxRenderer(mistune.Renderer): 152 | def __init__(self): 153 | super(CxxRenderer, self).__init__() 154 | self._not_in_list() 155 | self._not_paragraph() 156 | 157 | def header(self, text, level, raw = None): 158 | self._not_in_list() 159 | return self._join_paragraph() + pretty_print(text, "// ") 160 | 161 | def paragraph(self, text): 162 | if text.startswith("%%"): 163 | return "" 164 | 165 | if text == "$internal_toc": 166 | return "" 167 | 168 | self._not_in_list() 169 | text = clean_text(text) 170 | return self._join_paragraph() + pretty_print(text, "// ") 171 | 172 | def codespan(self, text): 173 | return text 174 | 175 | def link(self, link, title, content): 176 | return content 177 | 178 | def list(self, body, ordered = True): 179 | return self._join_paragraph() + body 180 | 181 | def list_item(self, text): 182 | return ("// %i. " % self._number_list_item()) + \ 183 | pretty_print(clean_text(text), "// ", False) 184 | 185 | def block_code(self, code, lang): 186 | self._not_in_list() 187 | 188 | code = re.sub("", "", code) 189 | 190 | if lang == "comment": 191 | code = re.sub("^(.)", "// \g<1>", code, flags = re.MULTILINE) 192 | code = re.sub("^$", "//", code, flags = re.MULTILINE) 193 | return self._join_paragraph() + code + "\n" 194 | else: 195 | self._not_paragraph() 196 | return "\n" + code 197 | 198 | def hrule(self): 199 | self._not_in_list() 200 | self._not_paragraph() 201 | return "\n" 202 | 203 | def footnote_ref(self, key, index): 204 | return "" 205 | 206 | def footnotes(self, text): 207 | return "" 208 | 209 | def _not_in_list(self): 210 | self._list_index = None 211 | 212 | def _number_list_item(self): 213 | if self._list_index == None: 214 | self._list_index = 2 215 | return 1 216 | else: 217 | result = self._list_index 218 | self._list_index += 1 219 | return result 220 | 221 | def _not_paragraph(self): 222 | self._join = False 223 | 224 | def _paragraph(self): 225 | self._join = True 226 | 227 | def _join_paragraph(self): 228 | if self._join: 229 | result = "//\n" 230 | else: 231 | result = "" 232 | 233 | self._join = True 234 | 235 | return result 236 | 237 | def main(md_file, html_file, cxx_file): 238 | markdown = open(md_file, "r").read() 239 | 240 | if html_file != None: 241 | html = mistune.markdown(markdown) 242 | open(html_file, "w").write(html) 243 | 244 | if cxx_file != None: 245 | renderer = CxxRenderer() 246 | source = mistune.Markdown(renderer = renderer).render(markdown) 247 | source = re.sub(r"\n*$", "\n", source) 248 | source = "// This file was generated automatically.\n\n" + source 249 | open(cxx_file, "w").write(source) 250 | 251 | if __name__ == "__main__": 252 | arguments = parser.parse_args() 253 | if arguments.html_file == None and arguments.cxx_file == None: 254 | parser.print_help() 255 | sys.exit(1) 256 | 257 | main(arguments.md_file, arguments.html_file, arguments.cxx_file) 258 | -------------------------------------------------------------------------------- /doc/tutorial/1-hello-world.md: -------------------------------------------------------------------------------- 1 | ## Hello, World! 2 | 3 | Download enum.h, then compile this program: 4 | 5 | #include 6 | #include "enum.h" 7 | 8 | BETTER_ENUM(Word, int, Hello, World) 9 | 10 | int main() 11 | { 12 | std::cout << (+Word::Hello)._to_string() << ", " 13 | << (+Word::World)._to_string() << "!" 14 | << std::endl; 15 | 16 | return 0; 17 | } 18 | 19 | Run it, and you should see the output "Hello, World!" 20 | 21 | Congratulations, you have just created your first Better Enum! 22 | 23 | %% description = Introductory Better Enums tutorial - a simple, but complete, 24 | Hello World program. 25 | -------------------------------------------------------------------------------- /doc/tutorial/2-conversions.md: -------------------------------------------------------------------------------- 1 | ## Conversions 2 | 3 | Let's begin by including `enum.h` and declaring our enum: 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | BETTER_ENUM(Channel, int, Cyan = 1, Magenta, Yellow, Black) 11 | 12 | We now have an `int`-sized enum with four constants. 13 | 14 | There are three groups of conversion functions: for strings, case-insensitive 15 | strings, and integers. They all follow the same pattern, so I'll explain the 16 | string functions in detail, and the rest can be understood by analogy. 17 | 18 | $internal_toc 19 | 20 | ### Strings 21 | 22 | There are three functions: 23 | 24 | 1. `._to_string` 25 | 2. `::_from_string` 26 | 3. `::_from_string_nothrow` 27 | 28 | 29 | int main() 30 | { 31 | Channel channel = Channel::Cyan; 32 | std::cout << channel._to_string() << " "; 33 | 34 | As you'd expect, the code above prints "Cyan". 35 | 36 | If `channel` is invalid — for example, if you simply cast the number "42" 37 | to `Channel` — then the result of `to_string` is undefined. 38 | 39 | --- 40 | 41 | channel = Channel::_from_string("Magenta"); 42 | std::cout << channel._to_string() << " "; 43 | 44 | This is also straightforward. If you pass a string which is not the name of a 45 | declared value, `_from_string` throws `std::runtime_error`. 46 | 47 | --- 48 | 49 | If you don't want an exception, there is `_from_string_nothrow`: 50 | 51 | better_enums::optional maybe_channel = 52 | Channel::_from_string_nothrow("Yellow"); 53 | 54 | if (!maybe_channel) 55 | std::cout << "error"; 56 | else 57 | std::cout << maybe_channel->_to_string() << " "; 58 | 59 | This returns an *optional value*, in the style of 60 | [`boost::optional`](http://www.boost.org/doc/libs/1_58_0/libs/optional/doc/html/index.html) 61 | or the proposed 62 | [`std::optional`](http://en.cppreference.com/w/cpp/experimental/optional). 63 | 64 | What that means for the above code is: 65 | 66 | - if the conversion succeeds, `maybe_channel` converts to `true` and 67 | `*maybe_channel` is the converted value of type `Channel`, 68 | - if the conversion fails, `maybe_channel` converts to `false`. 69 | 70 | In $cxx11, you can use `auto` to avoid writing out the optional type: 71 | 72 | ~~~comment 73 | auto maybe_channel = Channel::_from_string_nothrow("Yellow"); 74 | if (!maybe_channel) 75 | std::cout << "error"; 76 | else 77 | std::cout << maybe_channel->_to_string() << " "; 78 | ~~~ 79 | 80 | ### Case-insensitive strings 81 | 82 | The "`_nocase`" string conversions follow the same pattern, except for the lack 83 | of a "`to_string_nocase`". 84 | 85 | 1. `::_from_string_nocase` 86 | 2. `::_from_string_nocase_nothrow` 87 | 88 | 89 | channel = Channel::_from_string_nocase("cYaN"); 90 | std::cout << channel._to_string() << " "; 91 | 92 | maybe_channel = Channel::_from_string_nocase_nothrow("rEeD"); 93 | assert(!maybe_channel); 94 | 95 | ### Integers 96 | 97 | And, it is similar with the *representation type* `int`: 98 | 99 | 1. `._to_integral` 100 | 2. `::_from_integral` 101 | 3. `::_from_integral_nothrow` 102 | 4. `::_from_integral_unchecked` 103 | 104 | 105 | channel = Channel::Cyan; 106 | std::cout << channel._to_integral() << " "; 107 | 108 | channel = Channel::_from_integral(2); 109 | std::cout << channel._to_string() << " "; 110 | 111 | maybe_channel = Channel::_from_integral_nothrow(0); 112 | assert(!maybe_channel); 113 | 114 | That prints "1 Magenta". 115 | 116 | `_from_integral_unchecked` is a no-op unchecked cast of integers to enums, so 117 | use it carefully. 118 | 119 | channel = Channel::_from_integral_unchecked(0); 120 | // Invalid - better not to try converting it to string! 121 | 122 | ### Validity checking 123 | 124 | For completeness, Better Enums also provides three validity checking functions, 125 | one for each of the groups of conversions — string, case-insensitive 126 | string, and integer: 127 | 128 | assert(Channel::_is_valid(3)); 129 | assert(Channel::_is_valid("Magenta")); 130 | assert(Channel::_is_valid_nocase("cYaN")); 131 | 132 | --- 133 | 134 | Almost done. 135 | 136 | There is one unfortunate wrinkle. You cannot convert a literal constant such as 137 | `Channel::Cyan` directly to, for example, a string. You have to prefix it with 138 | `+`: 139 | 140 | std::cout << (+Channel::Cyan)._to_string(); 141 | 142 | This is due to some type gymnastics in the implementation of Better Enums. The 143 | reference has a 144 | [full explanation](${prefix}ApiReference.html#HelperFunctionsAndTypes). 145 | 146 | --- 147 | 148 | std::cout << std::endl; 149 | return 0; 150 | } 151 | 152 | %% description = Better Enums conversion functions. Converting to string, from 153 | string, to int, from int, and validation, both case-sensitive and 154 | case-insensitive. Exception-throwing and non-throwing variants presented. 155 | -------------------------------------------------------------------------------- /doc/tutorial/3-iterate.md: -------------------------------------------------------------------------------- 1 | ## Iteration 2 | 3 | Better Enums makes it easy to iterate over the values you have declared. For 4 | example, this: 5 | 6 | #include 7 | #include 8 | 9 | BETTER_ENUM(Channel, int, Red, Green = 2, Blue) 10 | 11 | int main() 12 | { 13 | 14 | for (size_t index = 0; index < Channel::_size(); ++index) { 15 | Channel channel = Channel::_values()[index]; 16 | std::cout << channel._to_integral() << " "; 17 | } 18 | std::cout << std::endl; 19 | 20 | will print "0 2 3". And this: 21 | 22 | for (size_t index = 0; index < Channel::_size(); ++index) { 23 | const char *name = Channel::_names()[index]; 24 | std::cout << name << " "; 25 | } 26 | std::cout << std::endl; 27 | 28 | will print "Red Green Blue". 29 | 30 | --- 31 | 32 | If you are using $cxx11, you can have much nicer syntax: 33 | 34 | ~~~comment 35 | for (Channel channel : Channel::_values()) 36 | std::cout << channel._to_integral() << " "; 37 | std::cout << std::endl; 38 | 39 | for (const char *name : Channel::_names()) 40 | std::cout << name << " "; 41 | std::cout << std::endl; 42 | ~~~ 43 | 44 | --- 45 | 46 | return 0; 47 | } 48 | 49 | %% description = Using Better Enums to iterate over all the constants of an 50 | enum, as well as over its names. Also shows the same with C++11 for-each syntax. 51 | -------------------------------------------------------------------------------- /doc/tutorial/4-switch.md: -------------------------------------------------------------------------------- 1 | ## Safe switch 2 | 3 | A Better Enum can be used directly in a `switch` statement: 4 | 5 | #include 6 | #include 7 | 8 | BETTER_ENUM(Channel, int, Red, Green, Blue) 9 | 10 | int main() 11 | { 12 | Channel channel = Channel::Green; 13 | int n; 14 | 15 | switch (channel) { 16 | case Channel::Red: n = 13; break; 17 | case Channel::Green: n = 37; break; 18 | case Channel::Blue: n = 42; break; 19 | } 20 | 21 | If you miss a case or add a redundant one, your compiler should be able to give 22 | you a warning — try it! 23 | 24 | Note that on msvc, you may need to enable [warning C4062][C4062]. 25 | 26 | [C4062]: https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4062 27 | 28 | --- 29 | 30 | std::cout << n << std::endl; 31 | return 0; 32 | } 33 | 34 | %% description = Better Enums can be used directly in switch statements like 35 | normal enums, making it possible for the compiler to check that all cases are 36 | listed, increasing the safety of your code. 37 | -------------------------------------------------------------------------------- /doc/tutorial/5-map.md: -------------------------------------------------------------------------------- 1 | ## Maps 2 | 3 | It is possible to create `constexpr` bidirectional maps between Better Enums and 4 | any type. This is currently an experimental feature. Feedback is very much 5 | wanted, but please don't build any mission-critical code on top of this :) 6 | 7 | The way it works is you give Better Enums a function — say, 8 | `const char* describe(Channel)`. The library enumerates it to make a map. 9 | 10 | The reason for using a function is that a `switch` statement is, I believe, the 11 | only place where a compiler will check for exhaustiveness. If you forget to 12 | create a case for one of the enum's constants, the compiler can let you know. 13 | Obviously, a `switch` statement is not data, and needs to be inside a function. 14 | It can only be inside a `constexpr` function in $cxx14, so this feature is most 15 | natural in $cxx14. When you pass the function to Better Enums, the library can 16 | build up a lookup data structure at compile time. 17 | 18 | Actually, right now, Better Enums doesn't quite do that — it enumerates 19 | the function *every* time you want to convert to an enum (but not *from* an 20 | enum). It simply does a linear scan every time. This is because I haven't yet 21 | found a data structure whose compile-time generation is fast enough for 22 | practical use. 23 | 24 | --- 25 | 26 | #include 27 | #include <enum.h> 28 | 29 | BETTER_ENUM(Channel, int, Red, Green, Blue) 30 | 31 | We will create a map from this function: 32 | 33 | constexpr const char* describe(Channel channel) 34 | { 35 | switch(channel) { 36 | case Channel::Red: return "the red channel"; 37 | case Channel::Green: return "the green channel"; 38 | case Channel::Blue: return "the blue channel"; 39 | } 40 | 41 | return "needed for gcc 5"; 42 | } 43 | 44 | Here is the map. The actual type is `better_enums::map`. 45 | 46 | constexpr auto descriptions = better_enums::make_map(describe); 47 | 48 | And the usage: 49 | 50 | int main() 51 | { 52 | std::cout << descriptions[Channel::Red] << std::endl; 53 | 54 | std::cout << descriptions.from_enum(Channel::Red) << std::endl; 55 | std::cout << descriptions.to_enum("the green channel") << std::endl; 56 | 57 | auto not_a_literal = std::string("the blue channel"); 58 | std::cout << descriptions.to_enum(not_a_literal.c_str()) << std::endl; 59 | 60 | return 0; 61 | } 62 | 63 | --- 64 | 65 | `make_map` above produces a value of type `better_enums::map`. The full 66 | signature of the template `better_enums::map` is 67 | 68 | ~~~comment 69 | template > 70 | ~~~ 71 | 72 | `Compare` has to be a class with a static member function 73 | `bool less(const T&, const T&)`. The default implementation 74 | `better_enums::map_compare` simply applies `operator <`, except when `T` is 75 | `const char*` or `const wchar_t*`. In that case, it does lexicographic comparison. 76 | 77 | %% description = Mapping enums to arbitrary types and vice versa. 78 | -------------------------------------------------------------------------------- /doc/tutorial/6-iostreams.md: -------------------------------------------------------------------------------- 1 | ## Stream operators 2 | 3 | These work *almost* as you'd expect. First, make sure you include `iostream` 4 | before `enum.h` in any translation unit in which you intend to use the 5 | operators: 6 | 7 | #include 8 | #include 9 | 10 | BETTER_ENUM(Channel, int, Red, Green, Blue) 11 | 12 | int main() 13 | { 14 | std::cout << +Channel::Red << std::endl; 15 | return 0; 16 | } 17 | 18 | The thing to watch for is the `+`: without it, `Channel::Red` is a value of type 19 | `Channel::_enumerated`, a $cxx98 enum type, so writing that to `cout` will 20 | output an integer. `+Channel::Red`, however, is a value of type `Channel`, and 21 | writing *that* instead will output the string `"Red"`. 22 | 23 | Input is also supported: 24 | 25 | ~~~comment 26 | Channel channel = Channel::Blue; 27 | std::cin >> channel; // Expects input such as "Green". 28 | ~~~ 29 | 30 | --- 31 | 32 | Only `char` streams are supported for the time being. 33 | 34 | %% description = Using Better Enums with stream input and output operators. 35 | -------------------------------------------------------------------------------- /doc/tutorial/7-safety.md: -------------------------------------------------------------------------------- 1 | ## Scope and safety 2 | 3 | This tutorial shows some of the safety features of Better Enums: scope, how to 4 | control conversions, and the lack of a default constructor. 5 | 6 | On balance, Better Enums are in one way less type-safe than enum class, and in 7 | another way more type-safe. The first difference in safety is the presence of 8 | implicit conversion to integral types. The second difference is the lack of a 9 | default constructor. Both of these can be toggled, so you can make Better Enums 10 | strictly safer than enum class, or just as safe. 11 | 12 | $internal_toc 13 | 14 | ### Scope 15 | 16 | You have probably noticed by now that Better Enums are scoped: when you declare 17 | 18 | #include 19 | #include 20 | 21 | BETTER_ENUM(Channel, int, Red = 1, Green, Blue) 22 | 23 | you don't get names such as `Red` in the global namespace. Instead, you get 24 | `Channel`, and `Red` is accessible as `Channel::Red`. This is no big deal in 25 | $cxx11, which has `enum class`. In $cxx98, however, this typically requires 26 | effort. Better Enums brings scope uniformly to both variants. So, despite the 27 | above declaration, you can safely declare 28 | 29 | BETTER_ENUM(Node, char, Red, Black) 30 | 31 | and everything will work as expected. 32 | 33 | int main() 34 | { 35 | assert((+Channel::Red)._to_integral() != (+Node::Red)._to_integral()); 36 | 37 | ### Implicit conversion 38 | 39 | A major complaint in $cxx98 is that `enums` are implicitly convertible to 40 | integers. Unfortunately, that is also true of Better Enums, and I haven't found 41 | a way to forbid the conversions and still have switch case checking. 42 | 43 | Better Enums can be made as safe as `enum class` in $cxx11, however. If your 44 | compiler supports `enum class` and you define 45 | `BETTER_ENUMS_STRICT_CONVERSION` before including `enum.h`, the following code 46 | will not compile: 47 | 48 | ~~~comment 49 | Channel channel = Channel::Red; 50 | int n = channel; 51 | ~~~ 52 | 53 | The reason this is not enabled by default is explained in the reference page on 54 | [strict conversions](${prefix}OptInFeatures.html#StrictConversions). 55 | 56 | You can conveniently define the macro on your compiler's command line, or by 57 | creating a little header file that defines it, and then includes 58 | enum.h. You can then include this new header file in your project 59 | everywhere where you would have included enum.h. 60 | 61 | ### Default constructor 62 | 63 | Better Enums generate without a default constructor. The purpose is to support 64 | the convention where if a Better Enum exists, then it has a valid value. So, if 65 | you uncomment this code, the program won't compile: 66 | 67 | ~~~comment 68 | Channel channel; 69 | ~~~ 70 | 71 | If this is too strict for your project, you can relax it as described 72 | [here](${prefix}OptInFeatures.html#DefaultConstructors). 73 | 74 | --- 75 | 76 | return 0; 77 | } 78 | 79 | %% description = Better Enums type safety features and limitations. 80 | -------------------------------------------------------------------------------- /doc/tutorial/8-representation.md: -------------------------------------------------------------------------------- 1 | ## Representation 2 | 3 | Let's go over some of the low-level properties of a Better Enum. This time, we 4 | will declare a more unusual enum than the ones we have seen. 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | BETTER_ENUM(ContentType, short, 11 | CompressedVideo = 5, PCM = 8, Subtitles = 17, Comment = 44) 12 | 13 | This is for a hypothetical multimedia container file format. Perhaps the files 14 | have sections, and each one has a header: 15 | 16 | struct Header { 17 | ContentType type; 18 | short flags; 19 | int offset; 20 | }; 21 | 22 | --- 23 | 24 | Here is what we have. 25 | 26 | int main() 27 | { 28 | assert(sizeof(ContentType) == 2); 29 | 30 | `ContentType` behaves just like a `short`[^*], in fact it simply wraps one. This 31 | makes it possible to lay out structures in a predictable fashion: 32 | 33 | Header header = {ContentType::PCM, 0, 0}; 34 | 35 | assert(sizeof(header) == 8); 36 | assert((size_t)&header.flags - (size_t)&header.type == 2); 37 | 38 | --- 39 | 40 | `uint16_t` is called `ContentType`'s *underlying* or *representation* type. If 41 | you want to know the representation type of any enum you have declared, it is 42 | available as the member type `::_integral`: 43 | 44 | ContentType::_integral untrusted_value = 44; 45 | 46 | Use this if you want a sized field to receive untrusted data, but aren't willing 47 | to call it `ContentType` yet because you have not validated it. Your validator 48 | will likely call `::_from_integral_nothrow`, perform any other validation your 49 | application requires, and then return `ContentType`. 50 | 51 | ContentType type = 52 | ContentType::_from_integral(untrusted_value); 53 | std::cout << type._to_string() << std::endl; 54 | 55 | --- 56 | 57 | You have probably noticed the initializers on each of the constants in 58 | `ContentType`. This allows you to declare sparse enums for compatibility with 59 | external protocols or previous versions of your software. The initializers don't 60 | need to be literal integers — they can be anything that the compiler would 61 | accept in a normal `enum` declaration. If there was a macro called 62 | `BIG_FAT_MACRO` declared above, we could have written 63 | `Subtitles = BIG_FAT_MACRO`. We could also have written 64 | `Subtitles = CompressedVideo`. 65 | 66 | --- 67 | 68 | The in-memory representation of an enum value is simply the number it has been 69 | assigned by the compiler. You should be safe passing enums to functions like 70 | `fread` and `fwrite`, and casting memory blocks known to be safe to `struct` 71 | types containg enums. The enums will behave as expected. 72 | 73 | --- 74 | 75 | return 0; 76 | } 77 | 78 | [^*]: It should properly be a `uint16_t`, and the rest of the header fields 79 | should also be explicitly sized. However, this code is trying to be 80 | compatible with $cxx98, where those names aren't available in a portable 81 | manner. 82 | 83 | %% description = The underlying memory representation of a Better Enum, 84 | including size and alignment. 85 | -------------------------------------------------------------------------------- /doc/tutorial/9-constexpr.md: -------------------------------------------------------------------------------- 1 | ## Compile-time usage 2 | 3 | When used with $cxx11, Better Enums are generated entirely during compilation. 4 | All the data is available for use by your own `constexpr` functions. The 5 | examples in *this* tutorial aren't very useful, but look at the 6 | [demos](${prefix}index.html#CompileTimeDemos) at the bottom of the main page to 7 | get an idea of what can be done. Here, you will see the basics. 8 | 9 | #include 10 | 11 | // The reason for this is explained below. 12 | #ifndef BETTER_ENUMS_CONSTEXPR_TO_STRING 13 | #define BETTER_ENUMS_CONSTEXPR_TO_STRING 14 | #endif 15 | 16 | #include 17 | 18 | BETTER_ENUM(Channel, int, Red = 1, Green = 2, Blue = 3) 19 | 20 | constexpr Channel channel = Channel::_from_integral(2); 21 | constexpr int value = channel._to_integral(); 22 | 23 | constexpr const char *name = channel._to_string(); 24 | constexpr Channel parsed = Channel::_from_string("Red"); 25 | 26 | All of the above are computed during compilation. The reason for the macro 27 | definition at the top of the file is explained on the 28 | [opt-in features page](${prefix}OptInFeatures.html#CompileTimeNameTrimming). 29 | Basically, it makes `_to_string` `constexpr`, but slows down compilation. 30 | 31 | You can also do things such as: 32 | 33 | constexpr size_t length(const char *s, size_t index = 0) 34 | { 35 | return s[index] == '\0' ? index : length(s, index + 1); 36 | } 37 | 38 | constexpr size_t length_of_name_of_second_constant = 39 | length(Channel::_names()[1]); 40 | 41 | int main() 42 | { 43 | std::cout << length_of_name_of_second_constant << std::endl; 44 | 45 | return 0; 46 | } 47 | 48 | Which prints "5", the length of "Green". That 5 was also computed during 49 | compilation. 50 | 51 | %% description = Better Enums can be used entirely at compile time in C++11. All 52 | conversion functions are available for constexpr functions or templates. 53 | -------------------------------------------------------------------------------- /example/1-hello-world.cc: -------------------------------------------------------------------------------- 1 | // This file was generated automatically. 2 | 3 | // Hello, World! 4 | // 5 | // Download enum.h, then compile this program: 6 | 7 | #include 8 | #include "enum.h" 9 | 10 | BETTER_ENUM(Word, int, Hello, World) 11 | 12 | int main() 13 | { 14 | std::cout << (+Word::Hello)._to_string() << ", " 15 | << (+Word::World)._to_string() << "!" 16 | << std::endl; 17 | 18 | return 0; 19 | } 20 | 21 | // Run it, and you should see the output "Hello, World!" 22 | // 23 | // Congratulations, you have just created your first Better Enum! 24 | -------------------------------------------------------------------------------- /example/101-special-values.cc: -------------------------------------------------------------------------------- 1 | // This file was generated automatically. 2 | 3 | // Special values 4 | // 5 | // Suppose your project has a convention where each enum has special invalid and 6 | // default values - for example, Enum::Invalid is invalid, and the first valid 7 | // constant is default. With Better Enums, you can get the compiler to enforce 8 | // the convention. At the end of this demo, we will have defined functions and 9 | // templates that allow us to write: 10 | // 11 | // Channel channel = default_; 12 | // Channel channel = invalid; 13 | // 14 | // void do_something(Channel channel); 15 | // 16 | // do_something(default_); 17 | // do_something(invalid); 18 | // 19 | // The compiler will compute default and invalid values automatically, but the 20 | // programmer will also be able to override the choice. Obviously, the syntax 21 | // above is very legible and maintainable - the intent is clear and your code 22 | // base will respond automatically to changes in enum definitions. 23 | // 24 | // Invalid values 25 | // 26 | // Let's start by defining the invalid values. 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | // Perhaps the convention is that the invalid value is usually called Invalid, 33 | // but not for all enums. We will encode that using a template function. The 34 | // unspecialized version will encode the default policy: 35 | 36 | template 37 | constexpr Enum invalid_impl() { return Enum::Invalid; } 38 | 39 | // A macro allows us to override the invalid value by specializing the template: 40 | 41 | #define OVERRIDE_INVALID(Enum, Value) \ 42 | template<> \ 43 | constexpr Enum invalid_impl() { return Enum::Value; } 44 | 45 | // Now, we can declare enums like these: 46 | 47 | BETTER_ENUM(Channel, int, Red, Green, Blue, Invalid) 48 | // Invalid is the invalid value by default 49 | 50 | BETTER_ENUM(Compression, int, Undefined, None, Huffman) 51 | OVERRIDE_INVALID(Compression, Undefined) 52 | 53 | // and use them: 54 | 55 | static_assert(invalid_impl() == +Channel::Invalid, ""); 56 | static_assert(invalid_impl() == +Compression::Undefined, ""); 57 | 58 | // This even supports enums that don't have an invalid value at all. As long as 59 | // they don't have a constant called Invalid, you will get a compile-time error 60 | // if you try to call invalid_impl<>() on them - as you probably should! 61 | // 62 | // Default values 63 | // 64 | // Perhaps here the convention is the first value that is not invalid is 65 | // default, unless, again, overridden by the programmer. This can be encoded 66 | // using only a slightly more complex template function for the general case: 67 | 68 | template 69 | constexpr Enum default_impl() 70 | { 71 | return 72 | Enum::_size() < 2 ? 73 | throw std::logic_error("enum has no valid constants") : 74 | Enum::_values()[0] == invalid_impl() ? 75 | Enum::_values()[1] : 76 | Enum::_values()[0]; 77 | } 78 | 79 | // The above code gives us the first value if it is not invalid, otherwise the 80 | // second value. 81 | // 82 | // The companion macro for overriding the choice of default value is almost the 83 | // same as it was for invalid. The difference is that we do an extra sanity 84 | // check to make sure the programmer doesn't declare the invalid value to be the 85 | // default. If the sanity check fails, we produce a nice error message. Again, 86 | // we are assuming that this is dictated by policy. 87 | 88 | #define OVERRIDE_DEFAULT(Enum, Value) \ 89 | static_assert(Enum::Value != Enum::Invalid, \ 90 | #Enum ": default cannot equal invalid"); \ 91 | template<> \ 92 | constexpr Enum default_impl() { return Enum::Value; } 93 | 94 | // And, as before, the usage: 95 | 96 | static_assert(default_impl() == +Channel::Red, ""); 97 | static_assert(default_impl() == +Compression::None, ""); 98 | 99 | // And, if you do 100 | 101 | BETTER_ENUM(Answer, int, Yes, No, Invalid) 102 | // OVERRIDE_DEFAULT(Answer, Invalid) 103 | 104 | // you will get a helpful compile-time error saying Answer: default cannot equal 105 | // invalid. 106 | // 107 | // Making the syntax nicer 108 | // 109 | // At this point, our policy is encoded by the ugly-looking functions 110 | // invalid_impl and default_impl. We want a nicer syntax. The main reason we 111 | // don't just use these functions directly is that the compiler wouldn't infer 112 | // their template arguments from the context. For example, we would have to 113 | // write things like 114 | // 115 | // Channel channel = invalid_impl(); 116 | // 117 | // which is unfortunate, because it results in repetition. 118 | // 119 | // In this section, we introduce two global objects called invalid and default_ 120 | // that will implicitly convert to any Better Enum type, and provide the invalid 121 | // or default value, respectively, when they do so. They will act as new 122 | // "keywords". 123 | 124 | struct invalid_t { 125 | template 126 | constexpr operator To() const { return invalid_impl(); } 127 | }; 128 | 129 | struct default_t { 130 | template 131 | constexpr operator To() const { return default_impl(); } 132 | }; 133 | 134 | constexpr invalid_t invalid{}; 135 | constexpr default_t default_{}; 136 | 137 | // As you can see, both of these provide the families of implicit conversions 138 | // that we need. Now, we can test: 139 | 140 | static_assert(+Channel::Invalid == invalid, ""); 141 | static_assert(+Compression::Undefined == invalid, ""); 142 | 143 | static_assert(+Channel::Red == default_, ""); 144 | static_assert(+Compression::None == default_, ""); 145 | 146 | // Finally, we can have nice code such as this: 147 | 148 | void dump(Channel channel) 149 | { 150 | std::cout << channel._to_string() << std::endl; 151 | } 152 | 153 | int main() 154 | { 155 | dump(invalid); 156 | 157 | Channel channel = default_; 158 | dump(channel); 159 | 160 | return 0; 161 | } 162 | 163 | 164 | // There are many possible variations of these policies, but I think most of 165 | // them can be encoded in a reasonable fashion using the tools Better Enums 166 | // provides. Enjoy! 167 | -------------------------------------------------------------------------------- /example/103-bitset.cc: -------------------------------------------------------------------------------- 1 | // This file was generated automatically. 2 | 3 | // Bit sets 4 | // 5 | // If you want to use std::bitset or a similar library to have enums be keys 6 | // into a bit set, you need to know the number of bits at compile time. You can 7 | // easily automate this with Better Enums, even when constants are not declared 8 | // in increasing order. 9 | 10 | // We simply need to find the maximum value of any given enum type. 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | template 17 | constexpr Enum max_loop(Enum accumulator, size_t index) 18 | { 19 | return 20 | index >= Enum::_size() ? accumulator : 21 | Enum::_values()[index] > accumulator ? 22 | max_loop(Enum::_values()[index], index + 1) : 23 | max_loop(accumulator, index + 1); 24 | } 25 | 26 | template 27 | constexpr Enum max() 28 | { 29 | return max_loop(Enum::_values()[0], 1); 30 | } 31 | 32 | // And use that to declare a bit set template: 33 | 34 | template 35 | using EnumSet = std::bitset()._to_integral() + 1>; 36 | 37 | // Now, we can have bit sets that are wide enough to hold whatever range we 38 | // declared. We just declare enums, and the numeric values of their constants 39 | // will be bit indices. The rest is straightforward. 40 | 41 | BETTER_ENUM(EFLAGS, int, 42 | Carry, Parity = 2, Adjust = 4, Zero, Sign, Trap, Interrupt, Direction, 43 | Overflow, NestedTask = 14, Resume = 16, V8086, AlignmentCheck, 44 | CPUIDPresent = 21) 45 | 46 | int main() 47 | { 48 | EnumSet eflags = 1 << EFLAGS::Carry | 1 << EFLAGS::Zero; 49 | 50 | if (eflags.test(EFLAGS::Carry)) 51 | eflags.set(EFLAGS::Trap); 52 | 53 | std::cout << eflags << std::endl; 54 | 55 | return 0; 56 | } 57 | 58 | 59 | // If we want bit sets of fixed known width instead, we can use the code above 60 | // to check that we haven't declared any bit indices out of range: 61 | // 62 | // static_assert(max()._to_integral() < 32, 63 | // "some bit indices are out of range"); 64 | -------------------------------------------------------------------------------- /example/104-quine.cc: -------------------------------------------------------------------------------- 1 | // This file was generated automatically. 2 | 3 | // Semi-quine 4 | // 5 | // Let's make a Better Enum assemble its own definition in memory. It won't be 6 | // literally as defined, since we will lose the exact initializer expressions, 7 | // but we will be able to preserve the numeric values. We will reserve the 8 | // memory buffer for the definition at compile time. 9 | // 10 | // Ok, so it's not really a quine, because we won't be writing all the code 11 | // needed to generate the definition to the buffer as well. And, there are 12 | // better ways to dump the definition than shown here. You could simply define a 13 | // macro that expands to an BETTER_ENUM declaration and also stringizes it. 14 | // 15 | // But that's not the point here. The point of this page is to show some of the 16 | // reflective capabilities of Better Enums, so you can adapt them for cases 17 | // where a macro is not sufficient :) 18 | 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | 25 | // First, we will need full compile-time reflection, since we will be calling 26 | // _to_string. Let's make sure it's enabled by defining 27 | // BETTER_ENUMS_CONSTEXPR_TO_STRING before including enum.h: 28 | 29 | #ifndef BETTER_ENUMS_CONSTEXPR_TO_STRING 30 | #define BETTER_ENUMS_CONSTEXPR_TO_STRING 31 | #endif 32 | 33 | #include 34 | 35 | // Now, let's declare some enums to dump later: 36 | 37 | BETTER_ENUM(Channel, int, Red, Green, Blue) 38 | BETTER_ENUM(Depth, int, TrueColor = 1, HighColor = 0) 39 | 40 | 41 | 42 | // Computing the size of the buffer 43 | // 44 | // First, we need to be able to get the length of each declaration above. We 45 | // will assume that the underlying type is always int, and that the spacing 46 | // convention is followed as above. 47 | // 48 | // First, let's get the lengths of basic components: 49 | 50 | // Returns the length of the string representation of the number n 51 | constexpr size_t value_length(int n, int bound = 10, size_t digits = 1) 52 | { 53 | return 54 | n < bound ? digits : value_length(n, bound * 10, digits + 1); 55 | } 56 | 57 | // Returns the length of s 58 | constexpr size_t string_length(const char *s, size_t index = 0) 59 | { 60 | return s[index] == '\0' ? index : string_length(s, index + 1); 61 | } 62 | 63 | // Now, the length of the constant declaration. Here is where we lose 64 | // information about initializers. We are going to format the constant 65 | // declarations like this: 66 | // 67 | // Red = 0, Green = 1, Blue = 2 68 | // TrueColor = 1, HighColor = 0 69 | // 70 | // This is because Better Enums doesn't provide a way to know what the exact 71 | // initializer was or whether there even was one - just the numeric value of 72 | // each constant. If we were trying to be clever, we could avoid formatting 73 | // initializers for sequential values, but I won't go through this exercise 74 | // here. 75 | 76 | // Returns the length of the constants portion of the declaration of Enum, 77 | // as described above. 78 | template 79 | constexpr size_t constants_length(size_t index = 0, size_t accumulator = 0) 80 | { 81 | return 82 | index >= Enum::_size() ? accumulator : 83 | constants_length( 84 | index + 1, accumulator 85 | + string_length(", ") 86 | + string_length(Enum::_names()[index]) 87 | + string_length(" = ") 88 | + value_length( 89 | Enum::_values()[index]._to_integral())); 90 | } 91 | 92 | // Finally, we can combine these to get the length of the formatted declaration 93 | // of the whole enum: 94 | 95 | // Returns the length of the whole declaration of Enum, assuming the 96 | // underlying type is int, and the constants are initialized as assumed by 97 | // constants_length() above. 98 | template 99 | constexpr size_t declaration_length() 100 | { 101 | return 102 | string_length("BETTER_ENUM(") 103 | + string_length(Enum::_name()) 104 | + string_length(", int") 105 | + constants_length() 106 | + string_length(")"); 107 | } 108 | 109 | 110 | 111 | // Formatting the enums 112 | // 113 | // Now, we can declare the buffers. The memory will be reserved at load time by 114 | // the binary's loader. The extra one byte in each buffer is for the null 115 | // terminator. 116 | 117 | char channel_definition[declaration_length() + 1]; 118 | char depth_definition[declaration_length() + 1]; 119 | 120 | // Let's also create the formatting function. This is executed at run time, but 121 | // we will be giving it pointers to our statically-allocated buffers. It will 122 | // format the enum declaration and then return the number of bytes it wrote to 123 | // the buffer, so that we can do a sanity check on it. 124 | 125 | template 126 | size_t format(char *buffer) 127 | { 128 | size_t offset = 0; 129 | 130 | offset += std::sprintf(buffer, "BETTER_ENUM(%s, int", Enum::_name()); 131 | 132 | for (Enum value : Enum::_values()) { 133 | offset += 134 | std::sprintf(buffer + offset, 135 | ", %s = %i", 136 | value._to_string(), value._to_integral()); 137 | } 138 | 139 | offset += std::sprintf(buffer + offset, ")"); 140 | 141 | return offset; 142 | } 143 | 144 | 145 | 146 | // Checking our work 147 | // 148 | // Now, we can write and run this code. 149 | 150 | int main() 151 | { 152 | size_t channel_length = format(channel_definition); 153 | assert(channel_length + 1 == sizeof(channel_definition)); 154 | 155 | size_t depth_length = format(depth_definition); 156 | assert(depth_length + 1 == sizeof(depth_definition)); 157 | 158 | std::cout << channel_definition << std::endl; 159 | std::cout << depth_definition << std::endl; 160 | 161 | return 0; 162 | } 163 | 164 | // It prints: 165 | // 166 | // BETTER_ENUM(Channel, int, Red = 0, Green = 1, Blue = 2) 167 | // BETTER_ENUM(Depth, int, TrueColor = 1, HighColor = 0) 168 | -------------------------------------------------------------------------------- /example/105-c++17-reflection.cc: -------------------------------------------------------------------------------- 1 | // This file was generated automatically. 2 | 3 | // C++17 reflection proposal 4 | // 5 | // You can try this demo live online. 6 | // 7 | // Better Enums can be used to implement the enums portion of the C++17 8 | // reflection proposal N4428 in C++11. N4428 proposes the following traits 9 | // interface: 10 | // 11 | // namespace std { 12 | // 13 | // template 14 | // struct enum_traits { 15 | // struct enumerators { 16 | // constexpr static size_t size; 17 | // 18 | // template 19 | // struct get { 20 | // constexpr string_literal identifier; 21 | // constexpr static E value; 22 | // }; 23 | // }; 24 | // }; 25 | // 26 | // } 27 | // 28 | // So, the basic usage would be: 29 | // 30 | // enum class Foo {A, B, C}; 31 | // 32 | // constexpr size_t size = 33 | // std::enum_traits::enumerators::size; 34 | // 35 | // constexpr Foo value_0 = 36 | // std::enum_traits::enumerators::get<0>::value; 37 | // 38 | // constexpr string_literal name_1 = 39 | // std::enum_traits::enumerators::get<1>::identifier; 40 | // 41 | // Resulting in the values 3, Foo::A, and "B", respectively. 42 | 43 | // The interface is implemented in the optional header file 44 | // extra/better-enums/n4428.h. There is a necessary difference: the interface is 45 | // only available for enums declared through the BETTER_ENUM macro. This is 46 | // because the macro is what generates the information necessary for reflection. 47 | // 48 | // Demo 49 | // 50 | // So, with that out of the way, we can do a little test. Let's assume that 51 | // extra/ has been added as a directory to search for include files. 52 | 53 | #ifndef BETTER_ENUMS_CONSTEXPR_TO_STRING 54 | #define BETTER_ENUMS_CONSTEXPR_TO_STRING 55 | #endif 56 | 57 | #include 58 | #include 59 | #include 60 | 61 | 62 | // Let's declare an enum: 63 | 64 | BETTER_ENUM(Channel, char, Red = 1, Green, Blue) 65 | 66 | // ...and try N4428: 67 | 68 | constexpr std::size_t size = 69 | std::enum_traits::enumerators::size; 70 | 71 | constexpr Channel value_0 = 72 | std::enum_traits::enumerators::get<0>::value; 73 | 74 | constexpr Channel value_1 = 75 | std::enum_traits::enumerators::get<1>::value; 76 | 77 | constexpr const char *identifier_2 = 78 | std::enum_traits::enumerators::get<2>::identifier; 79 | 80 | // ...and check the results: 81 | 82 | static_assert(size == 3, ""); 83 | 84 | static_assert(value_0 == +Channel::Red, ""); 85 | static_assert(value_1 == +Channel::Green, ""); 86 | 87 | int main() 88 | { 89 | std::cout << identifier_2 << std::endl; 90 | return 0; 91 | } 92 | 93 | // That prints Blue, as you would expect. 94 | // 95 | // Quirk 96 | // 97 | // The reason for the #define in the code above is that there is one quirk: the 98 | // interface above is available only for Better Enums for which compile-time 99 | // name trimming is enabled - those declared when 100 | // BETTER_ENUMS_CONSTEXPR_TO_STRING was defined, or declared with the SLOW_ENUM 101 | // variant of BETTER_ENUM. As mentioned on the linked page, the reason 102 | // compile-time name trimming is not the default is that, while still pretty 103 | // fast, it is four times slower than program-startup-time name trimming. The 104 | // latter is the default. 105 | // 106 | // Despite the above, a variation on the interface is available for enums 107 | // without compile-time name trimming: 108 | // 109 | // namespace std { 110 | // 111 | // template 112 | // struct enum_traits { 113 | // struct enumerators { 114 | // constexpr static size_t size; 115 | // 116 | // template 117 | // struct get { 118 | // constexpr const char *identifier; 119 | // constexpr static E value; 120 | // }; 121 | // 122 | // // For enums without compile-time name trimming. 123 | // template 124 | // struct get_alt { 125 | // static const char* identifier(); 126 | // constexpr static E value; 127 | // }; 128 | // }; 129 | // }; 130 | // 131 | // } 132 | // 133 | // As you can see, the difference is that identifier is a non-constexpr 134 | // function, and you have to access it through get_alt. 135 | // 136 | // // Without compile-time name trimming. 137 | // BETTER_ENUM(Depth, int, HighColor, TrueColor) 138 | // 139 | // int main() 140 | // { 141 | // std::cout 142 | // << std::enum_traits::enumerators::get_alt<1>::identifier() 143 | // << std::endl; 144 | // 145 | // return 0; 146 | // } 147 | // 148 | // The future 149 | // 150 | // N4428 is the fourth in a series of revisions: N3815, N4027, N4113, N4428. If 151 | // there are more revisions that change the proposal for enums, I will try to 152 | // implement those as well. 153 | -------------------------------------------------------------------------------- /example/2-conversions.cc: -------------------------------------------------------------------------------- 1 | // This file was generated automatically. 2 | 3 | // Conversions 4 | // 5 | // Let's begin by including enum.h and declaring our enum: 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | BETTER_ENUM(Channel, int, Cyan = 1, Magenta, Yellow, Black) 13 | 14 | // We now have an int-sized enum with four constants. 15 | // 16 | // There are three groups of conversion functions: for strings, case-insensitive 17 | // strings, and integers. They all follow the same pattern, so I'll explain the 18 | // string functions in detail, and the rest can be understood by analogy. 19 | // 20 | // Strings 21 | // 22 | // There are three functions: 23 | // 24 | // 1. ._to_string 25 | // 2. ::_from_string 26 | // 3. ::_from_string_nothrow 27 | 28 | int main() 29 | { 30 | Channel channel = Channel::Cyan; 31 | std::cout << channel._to_string() << " "; 32 | 33 | // As you'd expect, the code above prints "Cyan". 34 | // 35 | // If channel is invalid - for example, if you simply cast the number "42" to 36 | // Channel - then the result of to_string is undefined. 37 | 38 | 39 | channel = Channel::_from_string("Magenta"); 40 | std::cout << channel._to_string() << " "; 41 | 42 | // This is also straightforward. If you pass a string which is not the name of a 43 | // declared value, _from_string throws std::runtime_error. 44 | 45 | // If you don't want an exception, there is _from_string_nothrow: 46 | 47 | better_enums::optional maybe_channel = 48 | Channel::_from_string_nothrow("Yellow"); 49 | 50 | if (!maybe_channel) 51 | std::cout << "error"; 52 | else 53 | std::cout << maybe_channel->_to_string() << " "; 54 | 55 | // This returns an optional value, in the style of boost::optional or the 56 | // proposed std::optional. 57 | // 58 | // What that means for the above code is: 59 | // 60 | // 1. if the conversion succeeds, maybe_channel converts to true and 61 | // *maybe_channel is the converted value of type Channel, 62 | // 2. if the conversion fails, maybe_channel converts to false. 63 | // 64 | // In C++11, you can use auto to avoid writing out the optional type: 65 | // 66 | // auto maybe_channel = Channel::_from_string_nothrow("Yellow"); 67 | // if (!maybe_channel) 68 | // std::cout << "error"; 69 | // else 70 | // std::cout << maybe_channel->_to_string() << " "; 71 | // 72 | // Case-insensitive strings 73 | // 74 | // The "_nocase" string conversions follow the same pattern, except for the lack 75 | // of a "to_string_nocase". 76 | // 77 | // 1. ::_from_string_nocase 78 | // 2. ::_from_string_nocase_nothrow 79 | 80 | channel = Channel::_from_string_nocase("cYaN"); 81 | std::cout << channel._to_string() << " "; 82 | 83 | maybe_channel = Channel::_from_string_nocase_nothrow("rEeD"); 84 | assert(!maybe_channel); 85 | 86 | // Integers 87 | // 88 | // And, it is similar with the representation type int: 89 | // 90 | // 1. ._to_integral 91 | // 2. ::_from_integral 92 | // 3. ::_from_integral_nothrow 93 | // 4. ::_from_integral_unchecked 94 | 95 | channel = Channel::Cyan; 96 | std::cout << channel._to_integral() << " "; 97 | 98 | channel = Channel::_from_integral(2); 99 | std::cout << channel._to_string() << " "; 100 | 101 | maybe_channel = Channel::_from_integral_nothrow(0); 102 | assert(!maybe_channel); 103 | 104 | // That prints "1 Magenta". 105 | // 106 | // _from_integral_unchecked is a no-op unchecked cast of integers to enums, so 107 | // use it carefully. 108 | 109 | channel = Channel::_from_integral_unchecked(0); 110 | // Invalid - better not to try converting it to string! 111 | 112 | // Validity checking 113 | // 114 | // For completeness, Better Enums also provides three validity checking 115 | // functions, one for each of the groups of conversions - string, 116 | // case-insensitive string, and integer: 117 | 118 | assert(Channel::_is_valid(3)); 119 | assert(Channel::_is_valid("Magenta")); 120 | assert(Channel::_is_valid_nocase("cYaN")); 121 | 122 | 123 | // Almost done. 124 | // 125 | // There is one unfortunate wrinkle. You cannot convert a literal constant such 126 | // as Channel::Cyan directly to, for example, a string. You have to prefix it 127 | // with +: 128 | 129 | std::cout << (+Channel::Cyan)._to_string(); 130 | 131 | // This is due to some type gymnastics in the implementation of Better Enums. 132 | // The reference has a full explanation. 133 | 134 | 135 | std::cout << std::endl; 136 | return 0; 137 | } 138 | -------------------------------------------------------------------------------- /example/3-iterate.cc: -------------------------------------------------------------------------------- 1 | // This file was generated automatically. 2 | 3 | // Iteration 4 | // 5 | // Better Enums makes it easy to iterate over the values you have declared. For 6 | // example, this: 7 | 8 | #include 9 | #include 10 | 11 | BETTER_ENUM(Channel, int, Red, Green = 2, Blue) 12 | 13 | int main() 14 | { 15 | 16 | for (size_t index = 0; index < Channel::_size(); ++index) { 17 | Channel channel = Channel::_values()[index]; 18 | std::cout << channel._to_integral() << " "; 19 | } 20 | std::cout << std::endl; 21 | 22 | // will print "0 2 3". And this: 23 | 24 | for (size_t index = 0; index < Channel::_size(); ++index) { 25 | const char *name = Channel::_names()[index]; 26 | std::cout << name << " "; 27 | } 28 | std::cout << std::endl; 29 | 30 | // will print "Red Green Blue". 31 | 32 | // If you are using C++11, you can have much nicer syntax: 33 | // 34 | // for (Channel channel : Channel::_values()) 35 | // std::cout << channel._to_integral() << " "; 36 | // std::cout << std::endl; 37 | // 38 | // for (const char *name : Channel::_names()) 39 | // std::cout << name << " "; 40 | // std::cout << std::endl; 41 | 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /example/4-switch.cc: -------------------------------------------------------------------------------- 1 | // This file was generated automatically. 2 | 3 | // Safe switch 4 | // 5 | // A Better Enum can be used directly in a switch statement: 6 | 7 | #include 8 | #include 9 | 10 | BETTER_ENUM(Channel, int, Red, Green, Blue) 11 | 12 | int main() 13 | { 14 | Channel channel = Channel::Green; 15 | int n; 16 | 17 | switch (channel) { 18 | case Channel::Red: n = 13; break; 19 | case Channel::Green: n = 37; break; 20 | case Channel::Blue: n = 42; break; 21 | } 22 | 23 | // If you miss a case or add a redundant one, your compiler should be able to 24 | // give you a warning - try it! 25 | // 26 | // Note that on msvc, you may need to enable warning C4062. 27 | 28 | 29 | std::cout << n << std::endl; 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /example/5-map.cc: -------------------------------------------------------------------------------- 1 | // This file was generated automatically. 2 | 3 | // Maps 4 | // 5 | // It is possible to create constexpr bidirectional maps between Better Enums 6 | // and any type. This is currently an experimental feature. Feedback is very 7 | // much wanted, but please don't build any mission-critical code on top of this 8 | // :) 9 | // 10 | // The way it works is you give Better Enums a function - say, const char* 11 | // describe(Channel). The library enumerates it to make a map. 12 | // 13 | // The reason for using a function is that a switch statement is, I believe, the 14 | // only place where a compiler will check for exhaustiveness. If you forget to 15 | // create a case for one of the enum's constants, the compiler can let you know. 16 | // Obviously, a switch statement is not data, and needs to be inside a function. 17 | // It can only be inside a constexpr function in C++14, so this feature is most 18 | // natural in C++14. When you pass the function to Better Enums, the library can 19 | // build up a lookup data structure at compile time. 20 | // 21 | // Actually, right now, Better Enums doesn't quite do that - it enumerates the 22 | // function every time you want to convert to an enum (but not from an enum). It 23 | // simply does a linear scan every time. This is because I haven't yet found a 24 | // data structure whose compile-time generation is fast enough for practical 25 | // use. 26 | 27 | 28 | #include 29 | #include 30 | 31 | BETTER_ENUM(Channel, int, Red, Green, Blue) 32 | 33 | // We will create a map from this function: 34 | 35 | constexpr const char* describe(Channel channel) 36 | { 37 | switch(channel) { 38 | case Channel::Red: return "the red channel"; 39 | case Channel::Green: return "the green channel"; 40 | case Channel::Blue: return "the blue channel"; 41 | } 42 | 43 | return "needed for gcc 5"; 44 | } 45 | 46 | // Here is the map. The actual type is better_enums::map. 47 | 48 | constexpr auto descriptions = better_enums::make_map(describe); 49 | 50 | // And the usage: 51 | 52 | int main() 53 | { 54 | std::cout << descriptions[Channel::Red] << std::endl; 55 | 56 | std::cout << descriptions.from_enum(Channel::Red) << std::endl; 57 | std::cout << descriptions.to_enum("the green channel") << std::endl; 58 | 59 | auto not_a_literal = std::string("the blue channel"); 60 | std::cout << descriptions.to_enum(not_a_literal.c_str()) << std::endl; 61 | 62 | return 0; 63 | } 64 | 65 | 66 | // make_map above produces a value of type better_enums::map. The full 67 | // signature of the template better_enums::map is 68 | // 69 | // template > 70 | // 71 | // Compare has to be a class with a static member function bool less(const T&, 72 | // const T&). The default implementation better_enums::map_compare simply 73 | // applies operator <, except when T is const char*. In that case, it does 74 | // lexicographic comparison. 75 | -------------------------------------------------------------------------------- /example/6-iostreams.cc: -------------------------------------------------------------------------------- 1 | // This file was generated automatically. 2 | 3 | // Stream operators 4 | // 5 | // These work almost as you'd expect. First, make sure you include iostream 6 | // before enum.h in any translation unit in which you intend to use the 7 | // operators: 8 | 9 | #include 10 | #include 11 | 12 | BETTER_ENUM(Channel, int, Red, Green, Blue) 13 | 14 | int main() 15 | { 16 | std::cout << +Channel::Red << std::endl; 17 | return 0; 18 | } 19 | 20 | // The thing to watch for is the +: without it, Channel::Red is a value of type 21 | // Channel::_enumerated, a C++98 enum type, so writing that to cout will output 22 | // an integer. +Channel::Red, however, is a value of type Channel, and writing 23 | // that instead will output the string "Red". 24 | // 25 | // Input is also supported: 26 | // 27 | // Channel channel = Channel::Blue; 28 | // std::cin >> channel; // Expects input such as "Green". 29 | 30 | // Only char streams are supported for the time being. 31 | -------------------------------------------------------------------------------- /example/7-safety.cc: -------------------------------------------------------------------------------- 1 | // This file was generated automatically. 2 | 3 | // Scope and safety 4 | // 5 | // This tutorial shows some of the safety features of Better Enums: scope, how 6 | // to control conversions, and the lack of a default constructor. 7 | // 8 | // On balance, Better Enums are in one way less type-safe than enum class, and 9 | // in another way more type-safe. The first difference in safety is the presence 10 | // of implicit conversion to integral types. The second difference is the lack 11 | // of a default constructor. Both of these can be toggled, so you can make 12 | // Better Enums strictly safer than enum class, or just as safe. 13 | // 14 | // Scope 15 | // 16 | // You have probably noticed by now that Better Enums are scoped: when you 17 | // declare 18 | 19 | #include 20 | #include 21 | 22 | BETTER_ENUM(Channel, int, Red = 1, Green, Blue) 23 | 24 | // you don't get names such as Red in the global namespace. Instead, you get 25 | // Channel, and Red is accessible as Channel::Red. This is no big deal in C++11, 26 | // which has enum class. In C++98, however, this typically requires effort. 27 | // Better Enums brings scope uniformly to both variants. So, despite the above 28 | // declaration, you can safely declare 29 | 30 | BETTER_ENUM(Node, char, Red, Black) 31 | 32 | // and everything will work as expected. 33 | 34 | int main() 35 | { 36 | assert((+Channel::Red)._to_integral() != (+Node::Red)._to_integral()); 37 | 38 | // Implicit conversion 39 | // 40 | // A major complaint in C++98 is that enums are implicitly convertible to 41 | // integers. Unfortunately, that is also true of Better Enums, and I haven't 42 | // found a way to forbid the conversions and still have switch case checking. 43 | // 44 | // Better Enums can be made as safe as enum class in C++11, however. If your 45 | // compiler supports enum class and you define BETTER_ENUMS_STRICT_CONVERSION 46 | // before including enum.h, the following code will not compile: 47 | // 48 | // Channel channel = Channel::Red; 49 | // int n = channel; 50 | // 51 | // The reason this is not enabled by default is explained in the reference page 52 | // on strict conversions. 53 | // 54 | // You can conveniently define the macro on your compiler's command line, or by 55 | // creating a little header file that defines it, and then includes enum.h. You 56 | // can then include this new header file in your project everywhere where you 57 | // would have included enum.h. 58 | // 59 | // Default constructor 60 | // 61 | // Better Enums generate without a default constructor. The purpose is to 62 | // support the convention where if a Better Enum exists, then it has a valid 63 | // value. So, if you uncomment this code, the program won't compile: 64 | // 65 | // Channel channel; 66 | // 67 | // If this is too strict for your project, you can relax it as described here. 68 | 69 | 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /example/8-representation.cc: -------------------------------------------------------------------------------- 1 | // This file was generated automatically. 2 | 3 | // Representation 4 | // 5 | // Let's go over some of the low-level properties of a Better Enum. This time, 6 | // we will declare a more unusual enum than the ones we have seen. 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | BETTER_ENUM(ContentType, short, 13 | CompressedVideo = 5, PCM = 8, Subtitles = 17, Comment = 44) 14 | 15 | // This is for a hypothetical multimedia container file format. Perhaps the 16 | // files have sections, and each one has a header: 17 | 18 | struct Header { 19 | ContentType type; 20 | short flags; 21 | int offset; 22 | }; 23 | 24 | 25 | // Here is what we have. 26 | 27 | int main() 28 | { 29 | assert(sizeof(ContentType) == 2); 30 | 31 | // ContentType behaves just like a short, in fact it simply wraps one. This 32 | // makes it possible to lay out structures in a predictable fashion: 33 | 34 | Header header = {ContentType::PCM, 0, 0}; 35 | 36 | assert(sizeof(header) == 8); 37 | assert((size_t)&header.flags - (size_t)&header.type == 2); 38 | 39 | 40 | // uint16_t is called ContentType's underlying or representation type. If you 41 | // want to know the representation type of any enum you have declared, it is 42 | // available as the member type ::_integral: 43 | 44 | ContentType::_integral untrusted_value = 44; 45 | 46 | // Use this if you want a sized field to receive untrusted data, but aren't 47 | // willing to call it ContentType yet because you have not validated it. Your 48 | // validator will likely call ::_from_integral_nothrow, perform any other 49 | // validation your application requires, and then return ContentType. 50 | 51 | ContentType type = 52 | ContentType::_from_integral(untrusted_value); 53 | std::cout << type._to_string() << std::endl; 54 | 55 | 56 | // You have probably noticed the initializers on each of the constants in 57 | // ContentType. This allows you to declare sparse enums for compatibility with 58 | // external protocols or previous versions of your software. The initializers 59 | // don't need to be literal integers - they can be anything that the compiler 60 | // would accept in a normal enum declaration. If there was a macro called 61 | // BIG_FAT_MACRO declared above, we could have written Subtitles = 62 | // BIG_FAT_MACRO. We could also have written Subtitles = CompressedVideo. 63 | 64 | // The in-memory representation of an enum value is simply the number it has 65 | // been assigned by the compiler. You should be safe passing enums to functions 66 | // like fread and fwrite, and casting memory blocks known to be safe to struct 67 | // types containg enums. The enums will behave as expected. 68 | 69 | 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /example/9-constexpr.cc: -------------------------------------------------------------------------------- 1 | // This file was generated automatically. 2 | 3 | // Compile-time usage 4 | // 5 | // When used with C++11, Better Enums are generated entirely during compilation. 6 | // All the data is available for use by your own constexpr functions. The 7 | // examples in this tutorial aren't very useful, but look at the demos at the 8 | // bottom of the main page to get an idea of what can be done. Here, you will 9 | // see the basics. 10 | 11 | #include 12 | 13 | // The reason for this is explained below. 14 | #ifndef BETTER_ENUMS_CONSTEXPR_TO_STRING 15 | #define BETTER_ENUMS_CONSTEXPR_TO_STRING 16 | #endif 17 | 18 | #include 19 | 20 | BETTER_ENUM(Channel, int, Red = 1, Green = 2, Blue = 3) 21 | 22 | constexpr Channel channel = Channel::_from_integral(2); 23 | constexpr int value = channel._to_integral(); 24 | 25 | constexpr const char *name = channel._to_string(); 26 | constexpr Channel parsed = Channel::_from_string("Red"); 27 | 28 | // All of the above are computed during compilation. The reason for the macro 29 | // definition at the top of the file is explained on the opt-in features page. 30 | // Basically, it makes _to_string constexpr, but slows down compilation. 31 | // 32 | // You can also do things such as: 33 | 34 | constexpr size_t length(const char *s, size_t index = 0) 35 | { 36 | return s[index] == '\0' ? index : length(s, index + 1); 37 | } 38 | 39 | constexpr size_t length_of_name_of_second_constant = 40 | length(Channel::_names()[1]); 41 | 42 | int main() 43 | { 44 | std::cout << length_of_name_of_second_constant << std::endl; 45 | 46 | return 0; 47 | } 48 | 49 | // Which prints "5", the length of "Green". That 5 was also computed during 50 | // compilation. 51 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | ifndef CXX 2 | CXX := c++ 3 | endif 4 | 5 | ifndef CXXFLAGS 6 | CXXFLAGS := -std=c++11 -Wall -I .. -I../extra -o 7 | endif 8 | 9 | SOURCES := $(wildcard *.cc) 10 | BINARIES := $(SOURCES:.cc=.exe) 11 | 12 | .PHONY : default 13 | default : run 14 | @: 15 | 16 | .PHONY : all 17 | all : $(BINARIES) 18 | 19 | %.exe : %.cc ../*.h Makefile 20 | $(CXX) $(CXXFLAGS) $@ $< 21 | 22 | .PHONY : clean 23 | clean : 24 | rm -rf *.exe *.obj 25 | 26 | .PHONY : run 27 | run : all 28 | @for BINARY in $(BINARIES) ; \ 29 | do \ 30 | echo ./$$BINARY ; \ 31 | ./$$BINARY | sed -e "s/^/ /" ; \ 32 | done 33 | -------------------------------------------------------------------------------- /extra/better-enums/n4428.h: -------------------------------------------------------------------------------- 1 | // This file is part of Better Enums, released under the BSD 2-clause license. 2 | // See doc/LICENSE for details, or visit http://github.com/aantron/better-enums. 3 | 4 | // This file provides an implementation of the enum reflection interface 5 | // proposed in 6 | // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4428.pdf 7 | // on top of Better Enums. 8 | 9 | // See further discussion and a demonstration of usage at 10 | // example/107-c++17-reflection.cc, or visit: 11 | // http://aantron.github.io/better-enums/demo/C++17ReflectionProposal.html 12 | 13 | #pragma once 14 | 15 | #ifndef BETTER_ENUMS_N4428_H 16 | #define BETTER_ENUMS_N4428_H 17 | 18 | 19 | 20 | namespace std { 21 | 22 | template 23 | struct enum_traits { 24 | struct enumerators { 25 | constexpr static const size_t size = Enum::_size(); 26 | 27 | template 28 | struct get { 29 | constexpr static Enum value = Enum::_values()[Index]; 30 | constexpr static const char *identifier = Enum::_names()[Index]; 31 | }; 32 | 33 | // The above declarations implement N4428 (except for const char* 34 | // instead of string_literal). The alternative get below is needed for 35 | // Better Enums that weren't generated with compile-time name trimming, 36 | // i.e. without BETTER_ENUMS_CONSTEXPR_TO_STRING declared and not 37 | // through the SLOW_ENUM macro. That is currently the default for 38 | // Better Enums, since compile-time name trimming is pretty slow, so you 39 | // would want to use get_alt instead of get. 40 | template 41 | struct get_alt { 42 | constexpr static Enum value = Enum::_values()[Index]; 43 | static const char* identifier() { return Enum::_names()[Index]; }; 44 | }; 45 | }; 46 | }; 47 | 48 | } 49 | 50 | 51 | 52 | #endif // #ifndef BETTER_ENUMS_N4428_H 53 | -------------------------------------------------------------------------------- /script/make_macros.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | # This file is part of Better Enums, released under the BSD 2-clause license. 4 | # See LICENSE for details, or visit http://github.com/aantron/better-enums. 5 | 6 | # You only need this script if you are developing enum.h, or run into a limit. 7 | # 8 | # This script generates the macros BETTER_ENUMS_PP_MAP and BETTER_ENUMS_ITERATE, 9 | # used internally by enum.h. These are already inlined into enum.h. 10 | # 11 | # BETTER_ENUMS_PP_MAP has a limit, which determines the maximum number of 12 | # constants an enum can have. By default, this limit is 64 constants. 13 | # 14 | # BETTER_ENUMS_ITERATE also has a limit. This one determines the maximum length 15 | # of the name of a constant that is followed by an initializer (" = 2") when 16 | # compiling an enum with constexpr _to_string function (i.e. usually, this limit 17 | # does not apply). By default, the limit is 23 characters (24 with the 18 | # obligatory null terminator). 19 | # 20 | # If either of these limits is inadequate, you can still compile your code 21 | # without changing enum.h. You need to generate an external macro file with 22 | # definitions of these macros with relaxed limits, and tell enum.h to use the 23 | # external macro file. Here is how this is done, supposing you want support for 24 | # 512 constants of length up to 127 (128 with null terminator): 25 | # 26 | # 0. MACRO_FILE is the name of the external macro file. Make sure you put it 27 | # somewhere in your include path. 28 | # 1. Run python make_macros.py 512 128 > MACRO_FILE 29 | # 2. Build your code with an additional compiler flag: 30 | # - for gcc and clang, -DBETTER_ENUMS_MACRO_FILE='' 31 | # - for VC++, /DBETTER_ENUMS_MACRO_FILE='' 32 | # or use any other method of getting these macros defined. 33 | # 3. Compile your code. Your macro file should be included, and enum.h should 34 | # happily work with whatever limits you chose. 35 | 36 | import os 37 | import sys 38 | 39 | class MultiLine(object): 40 | def __init__(self, stream, indent = 4, columns = 80, initial_column = 0): 41 | self._columns_left = columns - initial_column 42 | self._indent = indent 43 | self._columns = columns 44 | self._stream = stream 45 | 46 | def write(self, token, last = False): 47 | break_line = False 48 | if last: 49 | if len(token) > self._columns_left: 50 | break_line = True 51 | else: 52 | if len(token) > self._columns_left - 1: 53 | break_line = True 54 | 55 | if break_line: 56 | print >> self._stream, ' ' * (self._columns_left - 1) + '\\' 57 | self._stream.write(' ' * self._indent) 58 | self._columns_left = self._columns - self._indent 59 | token = token.lstrip() 60 | 61 | self._stream.write(token) 62 | self._columns_left -= len(token) 63 | 64 | def generate(stream, constants, length, script): 65 | print >> stream, '// This file was automatically generated by ' + script 66 | 67 | print >> stream, '' 68 | print >> stream, '#pragma once' 69 | print >> stream, '' 70 | print >> stream, '#ifndef BETTER_ENUMS_MACRO_FILE_H' 71 | print >> stream, '#define BETTER_ENUMS_MACRO_FILE_H' 72 | 73 | print >> stream, '' 74 | print >> stream, '#define BETTER_ENUMS_PP_MAP(macro, data, ...) \\' 75 | print >> stream, ' BETTER_ENUMS_ID( \\' 76 | print >> stream, ' BETTER_ENUMS_APPLY( \\' 77 | print >> stream, ' BETTER_ENUMS_PP_MAP_VAR_COUNT, \\' 78 | print >> stream, ' BETTER_ENUMS_PP_COUNT(__VA_ARGS__)) \\' 79 | print >> stream, ' (macro, data, __VA_ARGS__))' 80 | 81 | print >> stream, '' 82 | print >> stream, '#define BETTER_ENUMS_PP_MAP_VAR_COUNT(count) ' + \ 83 | 'BETTER_ENUMS_M ## count' 84 | 85 | print >> stream, '' 86 | print >> stream, '#define BETTER_ENUMS_APPLY(macro, ...) ' + \ 87 | 'BETTER_ENUMS_ID(macro(__VA_ARGS__))' 88 | 89 | print >> stream, '' 90 | print >> stream, '#define BETTER_ENUMS_ID(x) x' 91 | 92 | print >> stream, '' 93 | print >> stream, '#define BETTER_ENUMS_M1(m, d, x) m(d,0,x)' 94 | for index in range(2, constants + 1): 95 | print >> stream, '#define BETTER_ENUMS_M' + str(index) + \ 96 | '(m,d,x,...) m(d,' + str(index - 1) + ',x) \\' 97 | print >> stream, ' BETTER_ENUMS_ID(BETTER_ENUMS_M' + \ 98 | str(index - 1) + '(m,d,__VA_ARGS__))' 99 | 100 | print >> stream, '' 101 | pp_count_impl_prefix = '#define BETTER_ENUMS_PP_COUNT_IMPL(_1,' 102 | stream.write(pp_count_impl_prefix) 103 | pp_count_impl = MultiLine(stream = stream, indent = 4, 104 | initial_column = len(pp_count_impl_prefix)) 105 | for index in range(2, constants + 1): 106 | pp_count_impl.write(' _' + str(index) + ',') 107 | pp_count_impl.write(' count,') 108 | pp_count_impl.write(' ...)') 109 | pp_count_impl.write(' count', last = True) 110 | print >> stream, '' 111 | 112 | print >> stream, '' 113 | print >> stream, '#define BETTER_ENUMS_PP_COUNT(...) \\' 114 | pp_count_prefix = \ 115 | ' BETTER_ENUMS_ID(BETTER_ENUMS_PP_COUNT_IMPL(__VA_ARGS__,' 116 | stream.write(pp_count_prefix) 117 | pp_count = MultiLine(stream = stream, indent = 8, 118 | initial_column = len(pp_count_prefix)) 119 | for index in range(0, constants - 1): 120 | pp_count.write(' ' + str(constants - index) + ',') 121 | pp_count.write(' 1))', last = True) 122 | print >> stream, '' 123 | 124 | print >> stream, '' 125 | iterate_prefix = '#define BETTER_ENUMS_ITERATE(X, f, l)' 126 | stream.write(iterate_prefix) 127 | iterate = MultiLine(stream = stream, indent = 4, 128 | initial_column = len(iterate_prefix)) 129 | for index in range(0, length): 130 | iterate.write(' X(f, l, %i)' % index) 131 | print >> stream, '' 132 | 133 | print >> stream, '' 134 | print >> stream, '#endif // #ifndef BETTER_ENUMS_MACRO_FILE_H' 135 | 136 | if __name__ == '__main__': 137 | if len(sys.argv) != 3: 138 | print >> sys.stderr, \ 139 | 'Usage: ' + sys.argv[0] + ' CONSTANTS LENGTH > FILE' 140 | print >> sys.stderr, '' 141 | print >> sys.stderr, 'Prints map macro definition to FILE.' 142 | print >> sys.stderr, 'CONSTANTS is the number of constants to support.' 143 | print >> sys.stderr, 'LENGTH is the maximum length of a constant name.' 144 | sys.exit(1) 145 | 146 | generate(sys.stdout, int(sys.argv[1]), int(sys.argv[2]), 147 | os.path.basename(sys.argv[0])) 148 | 149 | sys.exit(0) 150 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Invoked automatically by the Makefile. 2 | 3 | cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR) 4 | 5 | project("Better Enums Testing" CXX) 6 | 7 | 8 | # Detect compiler feature support. 9 | 10 | list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_constexpr CONSTEXPR_INDEX) 11 | if(CONSTEXPR_INDEX EQUAL -1) 12 | set(SUPPORTS_CONSTEXPR 0) 13 | else() 14 | set(SUPPORTS_CONSTEXPR 1) 15 | endif() 16 | 17 | list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_relaxed_constexpr 18 | RELAXED_CONSTEXPR_INDEX) 19 | if(RELAXED_CONSTEXPR_INDEX EQUAL -1) 20 | set(SUPPORTS_RELAXED_CONSTEXPR 0) 21 | else() 22 | set(SUPPORTS_RELAXED_CONSTEXPR 1) 23 | endif() 24 | 25 | # Current versions of CMake report VS2015 as supporting constexpr. However, the 26 | # support is too buggy to build Better Enums. Avoid trying to build constexpr 27 | # configurations on MSVC. 28 | if(${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC) 29 | set(SUPPORTS_CONSTEXPR 0) 30 | set(SUPPORTS_RELAXED_CONSTEXPR 0) 31 | endif() 32 | 33 | list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_strong_enums ENUM_CLASS_INDEX) 34 | if(ENUM_CLASS_INDEX EQUAL -1) 35 | set(SUPPORTS_ENUM_CLASS 0) 36 | else() 37 | set(SUPPORTS_ENUM_CLASS 1) 38 | endif() 39 | 40 | # Not supporting C++11 usage on g++46 due to buggy constexpr. 41 | 42 | if(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7) 43 | set(SUPPORTS_CONSTEXPR 0) 44 | set(SUPPORTS_ENUM_CLASS 0) 45 | endif() 46 | 47 | # Not supporting C++14 testing on clang++34 due to buggy library installed in 48 | # Travis Ubuntu image. 49 | 50 | if(CMAKE_CXX_COMPILER_ID STREQUAL Clang 51 | AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5) 52 | set(SUPPORTS_RELAXED_CONSTEXPR 0) 53 | endif() 54 | 55 | 56 | # Select standard based on the requested configuration. If the compiler does not 57 | # support the requested configuration, write a message and generate a no-op 58 | # build. This condition is not a failure. 59 | # 60 | # If no configuration is explicitly requested, default to compiling with no 61 | # special flags, with the latest standard supported by the compiler. 62 | 63 | set(DO_NOT_TEST_FILE "${CMAKE_BINARY_DIR}/do-not-test") 64 | 65 | if(CONFIGURATION STREQUAL CONSTEXPR) 66 | if(SUPPORTS_CONSTEXPR) 67 | set(CMAKE_CXX_STANDARD 11) 68 | else() 69 | message(WARNING "This compiler does not support constexpr") 70 | file(WRITE "${DO_NOT_TEST_FILE}") 71 | return() 72 | endif() 73 | elseif(CONFIGURATION STREQUAL FULL_CONSTEXPR) 74 | if(SUPPORTS_CONSTEXPR) 75 | set(CMAKE_CXX_STANDARD 11) 76 | add_definitions(-DBETTER_ENUMS_CONSTEXPR_TO_STRING) 77 | else() 78 | message(WARNING "This compiler does not support constexpr") 79 | file(WRITE "${DO_NOT_TEST_FILE}") 80 | return() 81 | endif() 82 | elseif(CONFIGURATION STREQUAL STRICT_CONVERSION) 83 | if(SUPPORTS_ENUM_CLASS) 84 | set(CMAKE_CXX_STANDARD 11) 85 | add_definitions(-DBETTER_ENUMS_STRICT_CONVERSION) 86 | else() 87 | message(WARNING "This compiler does not support enum class") 88 | file(WRITE "${DO_NOT_TEST_FILE}") 89 | return() 90 | endif() 91 | elseif(CONFIGURATION STREQUAL CXX98) 92 | set(CMAKE_CXX_STANDARD 98) 93 | elseif(CONFIGURATION STREQUAL CXX14) 94 | if(SUPPORTS_RELAXED_CONSTEXPR) 95 | set(CMAKE_CXX_STANDARD 14) 96 | else() 97 | message(WARNING "This compiler does not support relaxed constexpr") 98 | file(WRITE "${DO_NOT_TEST_FILE}") 99 | return() 100 | endif() 101 | else() 102 | set(CMAKE_CXX_STANDARD 11) 103 | endif() 104 | 105 | 106 | # Basic tests. 107 | 108 | add_executable(cxxtest cxxtest/tests.cc) 109 | add_executable(linking linking/helper.cc linking/main.cc) 110 | 111 | set(PERFORMANCE_TESTS 112 | 1-simple 2-include_empty 3-only_include_enum 4-declare_enums 5-iostream) 113 | 114 | foreach(TEST ${PERFORMANCE_TESTS}) 115 | add_executable(performance-${TEST} performance/${TEST}.cc) 116 | endforeach(TEST) 117 | 118 | 119 | # Select examples to build. 120 | 121 | set(EXAMPLES 122 | 1-hello-world 2-conversions 3-iterate 4-switch 6-iostreams 7-safety 123 | 8-representation 9-constexpr 101-special-values 103-bitset 104-quine 124 | 105-c++17-reflection) 125 | 126 | set(SKIPPED_FOR_CXX98 127 | 5-map 9-constexpr 101-special-values 103-bitset 104-quine 128 | 105-c++17-reflection) 129 | 130 | set(SKIPPED_FOR_STRICT_CONVERSION 4-switch) 131 | 132 | if(CONFIGURATION STREQUAL CXX98 OR NOT SUPPORTS_CONSTEXPR) 133 | list(REMOVE_ITEM EXAMPLES ${SKIPPED_FOR_CXX98}) 134 | endif() 135 | 136 | if(CONFIGURATION STREQUAL STRICT_CONVERSION) 137 | list(REMOVE_ITEM EXAMPLES ${SKIPPED_FOR_STRICT_CONVERSION}) 138 | endif() 139 | 140 | if(CONFIGURATION STREQUAL CXX14) 141 | set(EXAMPLES 5-map) 142 | endif() 143 | 144 | foreach(EXAMPLE ${EXAMPLES}) 145 | add_executable(example-${EXAMPLE} ../example/${EXAMPLE}.cc) 146 | endforeach(EXAMPLE) 147 | 148 | 149 | # Add compiler flags. 150 | 151 | include_directories(..) 152 | include_directories(../extra) 153 | 154 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES Clang) 155 | include(CheckCXXCompilerFlag) 156 | 157 | macro(add_cxx_flag_if_supported FLAG) 158 | check_cxx_compiler_flag("${FLAG}" HAVE_FLAG_${FLAG}) 159 | 160 | if(HAVE_FLAG_${FLAG}) 161 | add_definitions(${FLAG}) 162 | endif() 163 | endmacro() 164 | 165 | macro(add_cxx_flag_to_target_if_supported TARGET FLAG) 166 | string(REPLACE "=" "_equals_" ESCAPED ${FLAG}) 167 | string(REPLACE "+" "_plus_" ESCAPED ${ESCAPED}) 168 | check_cxx_compiler_flag("${FLAG}" HAVE_FLAG_${FLAG}) 169 | 170 | if(HAVE_FLAG_${FLAG}) 171 | get_target_property(FLAGS ${TARGET} COMPILE_FLAGS) 172 | if(${FLAGS} STREQUAL "FLAGS-NOTFOUND") 173 | set(FLAGS "") 174 | endif() 175 | set_target_properties( 176 | ${TARGET} PROPERTIES COMPILE_FLAGS "${FLAGS} ${FLAG}") 177 | endif() 178 | endmacro() 179 | 180 | add_cxx_flag_if_supported("-Wpedantic") 181 | add_cxx_flag_if_supported("-Wall") 182 | add_cxx_flag_if_supported("-Wextra") 183 | add_cxx_flag_if_supported("-Wno-variadic-macros") 184 | add_cxx_flag_if_supported("-Wno-unused-const-variable") 185 | 186 | add_cxx_flag_to_target_if_supported(linking "-Weverything") 187 | add_cxx_flag_to_target_if_supported(linking "-Wno-c++98-compat-pedantic") 188 | add_cxx_flag_to_target_if_supported(linking "-Wno-padded") 189 | add_cxx_flag_to_target_if_supported(linking "-Wno-global-constructors") 190 | add_cxx_flag_to_target_if_supported(linking "-Wno-old-style-cast") 191 | add_cxx_flag_to_target_if_supported(linking "-Wno-missing-prototypes") 192 | add_cxx_flag_to_target_if_supported(linking "-Wshadow") 193 | add_cxx_flag_to_target_if_supported(linking "-Weffc++") 194 | add_cxx_flag_to_target_if_supported(linking "-Wstrict-aliasing") 195 | add_cxx_flag_to_target_if_supported(linking "-Wformat") 196 | add_cxx_flag_to_target_if_supported(linking "-Wmissing-include-dirs") 197 | add_cxx_flag_to_target_if_supported(linking "-Wsync-nand") 198 | add_cxx_flag_to_target_if_supported(linking "-Wconditionally-supported") 199 | add_cxx_flag_to_target_if_supported(linking "-Wconversion") 200 | add_cxx_flag_to_target_if_supported(linking "-Wuseless-cast") 201 | 202 | add_definitions("-Werror") 203 | endif() 204 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | # Run "make" for quick builds while developing. 2 | # Run "make default-all" before submitting a pull request. 3 | # Run "make clean" to clean up. 4 | # See doc/CONTRIBUTING.md for full instructions. 5 | 6 | CXXTEST_GENERATED := cxxtest/tests.cc 7 | 8 | UNIX_MAKE_COMMAND := make 9 | WINDOWS_MAKE_COMMAND := "MSBuild.exe \"Better Enums Testing.sln\"" 10 | 11 | UNIX_OUTPUT_DIRECTORY := . 12 | WINDOWS_OUTPUT_DIRECTORY := Debug 13 | 14 | ifdef COMSPEC 15 | WIN32 := true 16 | endif 17 | ifdef ComSpec 18 | WIN32 := true 19 | endif 20 | 21 | ifndef WIN32 22 | 23 | DEFAULT_MAKE_COMMAND := $(UNIX_MAKE_COMMAND) 24 | DEFAULT_OUTPUT_DIRECTORY := $(UNIX_OUTPUT_DIRECTORY) 25 | define PATH_FIX 26 | @true 27 | endef 28 | SUFFIX := 29 | CXXTESTGEN := cxxtestgen 30 | 31 | else 32 | 33 | DEFAULT_MAKE_COMMAND := $(WINDOWS_MAKE_COMMAND) 34 | DEFAULT_OUTPUT_DIRECTORY := $(WINDOWS_OUTPUT_DIRECTORY) 35 | define PATH_FIX 36 | sed 's!include "/!include "C:/cygwin/!g' $1 > $$$$ && mv $$$$ $1 37 | endef 38 | SUFFIX := .exe 39 | CXXTESTGEN := python `which cxxtestgen | sed -E 's!(/cygdrive)?/c/!c:/!'` 40 | 41 | endif 42 | 43 | DEFAULTS := \ 44 | TITLE=default \ 45 | MAKE_COMMAND=$(DEFAULT_MAKE_COMMAND) \ 46 | OUTPUT_DIRECTORY=$(DEFAULT_OUTPUT_DIRECTORY) 47 | 48 | # Builds one configuration with the system compiler. This will be either a 49 | # regular C++11 or C++98 build (no constexpr to_string and no strict 50 | # conversions). 51 | .PHONY : default 52 | default : examples 53 | make $(DEFAULTS) one-configuration 54 | 55 | # Builds all configurations with the system compiler. 56 | .PHONY : default-all 57 | default-all : examples 58 | make $(DEFAULTS) all-configurations 59 | 60 | .PHONY : examples 61 | examples : 62 | make -C ../doc examples 63 | 64 | # Example: make COMPILER=clang++36 unix 65 | .PHONY : unix 66 | unix : 67 | make TITLE=$(COMPILER) CMAKE_OPTIONS="-DCMAKE_CXX_COMPILER=$(COMPILER)" \ 68 | MAKE_COMMAND=$(UNIX_MAKE_COMMAND) \ 69 | OUTPUT_DIRECTORY=$(UNIX_OUTPUT_DIRECTORY) all-configurations 70 | 71 | # Example: make TITLE=vc2013 COMPILER="Visual Studio 12 2013" ms 72 | .PHONY : ms 73 | ms : 74 | make TITLE=$(TITLE) CMAKE_OPTIONS="-G \\\"$(COMPILER)\\\"" \ 75 | MAKE_COMMAND=$(WINDOWS_MAKE_COMMAND) \ 76 | OUTPUT_DIRECTORY=$(WINDOWS_OUTPUT_DIRECTORY) all-configurations 77 | 78 | # Expects three variables to be defined: 79 | # CMAKE_OPTIONS: 80 | # First, the compiler: 81 | # - Empty for a "default" build. 82 | # - -G "Visual Studio XX YYYY" to select a specific Microsoft compiler. 83 | # - -DCMAKE_CXX_COMPILER=AAA to select a specific Unix compiler binary. 84 | # Configuration selection (e.g. -DCONFIGURATION=CXX98) also go here. 85 | # TITLE: 86 | # The build title (subdirectory). Some combination of compiler/configuration. 87 | # MAKE_COMMAND: 88 | # Either make or msbuild "Better Enums Testing.sln" 89 | # OUTPUT_DIRECTORY: 90 | # Path to generated binaries relative to build directory. Either "." or 91 | # "Debug". 92 | .PHONY : one-configuration 93 | one-configuration : $(CXXTEST_GENERATED) 94 | mkdir -p build/$(TITLE) 95 | cd build/$(TITLE) && cmake $(CMAKE_OPTIONS) ../.. && $(MAKE_COMMAND) 96 | rm -rf build/$(TITLE)/bin 97 | [ -f build/$(TITLE)/do-not-test ] || \ 98 | ( ln -s $(OUTPUT_DIRECTORY) build/$(TITLE)/bin && \ 99 | make BIN=build/$(TITLE)/bin run-tests ) 100 | 101 | .PHONY : run-tests 102 | run-tests : 103 | $(BIN)/cxxtest 104 | @for FILE in $(BIN)/example-*$(SUFFIX) ; \ 105 | do \ 106 | EXAMPLE=$$(basename $$FILE | sed s/\.exe$$// | sed s/^example-//) ; \ 107 | $$FILE | sed 's/\r$$//' | cmp - expect/$$EXAMPLE ; \ 108 | RESULT=$$? ; \ 109 | if [ $$RESULT -ne 0 ] ; \ 110 | then \ 111 | echo \'$$FILE\' produced bad output ; \ 112 | exit $$RESULT ; \ 113 | fi ; \ 114 | done 115 | @echo Example program output matches expected output 116 | 117 | .PHONY : all-configurations 118 | all-configurations : 119 | make TITLE=$(TITLE)-c++11 \ 120 | CMAKE_OPTIONS="$(CMAKE_OPTIONS) -DCONFIGURATION=CONSTEXPR" \ 121 | one-configuration 122 | make TITLE=$(TITLE)-full-constexpr \ 123 | CMAKE_OPTIONS="$(CMAKE_OPTIONS) -DCONFIGURATION=FULL_CONSTEXPR" \ 124 | one-configuration 125 | make TITLE=$(TITLE)-enum-class \ 126 | CMAKE_OPTIONS="$(CMAKE_OPTIONS) -DCONFIGURATION=STRICT_CONVERSION" \ 127 | one-configuration 128 | make TITLE=$(TITLE)-c++14 \ 129 | CMAKE_OPTIONS="$(CMAKE_OPTIONS) -DCONFIGURATION=CXX14" \ 130 | one-configuration 131 | make TITLE=$(TITLE)-c++98 \ 132 | CMAKE_OPTIONS="$(CMAKE_OPTIONS) -DCONFIGURATION=CXX98" \ 133 | one-configuration 134 | 135 | .PHONY : all-unix 136 | all-unix : examples 137 | make COMPILER=clang++39 unix 138 | make COMPILER=clang++38 unix 139 | make COMPILER=clang++37 unix 140 | make COMPILER=g++52 unix 141 | make COMPILER=g++46 unix 142 | make COMPILER=g++47 unix 143 | make COMPILER=g++43 unix 144 | make COMPILER=g++48 unix 145 | make COMPILER=g++49 unix 146 | make COMPILER=g++44 unix 147 | make COMPILER=g++45 unix 148 | make COMPILER=clang++33 unix 149 | make COMPILER=clang++34 unix 150 | make COMPILER=clang++35 unix 151 | make COMPILER=clang++36 unix 152 | 153 | .PHONY : all-ms 154 | all-ms : examples 155 | make TITLE=vc2015 COMPILER="Visual Studio 14 2015" ms 156 | make TITLE=vc2008 COMPILER="Visual Studio 9 2008" ms 157 | make TITLE=vc2010 COMPILER="Visual Studio 10 2010" ms 158 | make TITLE=vc2012 COMPILER="Visual Studio 11 2012" ms 159 | make TITLE=vc2013 COMPILER="Visual Studio 12 2013" ms 160 | 161 | $(CXXTEST_GENERATED) : cxxtest/*.h 162 | $(CXXTESTGEN) --error-printer -o $@ $^ 163 | $(call PATH_FIX,$@) 164 | 165 | .PHONY : clean 166 | clean : 167 | rm -rf build $(CXXTEST_GENERATED) 168 | -------------------------------------------------------------------------------- /test/cxxtest/stream.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | 7 | BETTER_ENUM(Compiler, int, GCC, Clang, MSVC) 8 | 9 | 10 | 11 | class StreamOperatorTests : public CxxTest::TestSuite { 12 | public: 13 | void test_output() 14 | { 15 | std::stringstream stream; 16 | 17 | stream << +Compiler::GCC; 18 | TS_ASSERT_EQUALS(strcmp(stream.str().c_str(), "GCC"), 0); 19 | } 20 | 21 | void test_input() 22 | { 23 | std::stringstream stream("Clang"); 24 | Compiler compiler = Compiler::GCC; 25 | 26 | stream >> compiler; 27 | TS_ASSERT_EQUALS(compiler, +Compiler::Clang); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /test/expect/1-hello-world: -------------------------------------------------------------------------------- 1 | Hello, World! 2 | -------------------------------------------------------------------------------- /test/expect/101-special-values: -------------------------------------------------------------------------------- 1 | Invalid 2 | Red 3 | -------------------------------------------------------------------------------- /test/expect/102-any-underlying: -------------------------------------------------------------------------------- 1 | Red component: c4 2 | Green component: 74 3 | Blue component: 51 4 | darksalmon 5 | -------------------------------------------------------------------------------- /test/expect/103-bitset: -------------------------------------------------------------------------------- 1 | 0000000000000010100001 2 | -------------------------------------------------------------------------------- /test/expect/104-quine: -------------------------------------------------------------------------------- 1 | BETTER_ENUM(Channel, int, Red = 0, Green = 1, Blue = 2) 2 | BETTER_ENUM(Depth, int, TrueColor = 1, HighColor = 0) 3 | -------------------------------------------------------------------------------- /test/expect/105-c++17-reflection: -------------------------------------------------------------------------------- 1 | Blue 2 | -------------------------------------------------------------------------------- /test/expect/2-conversions: -------------------------------------------------------------------------------- 1 | Cyan Magenta Yellow Cyan 1 Magenta Cyan 2 | -------------------------------------------------------------------------------- /test/expect/3-iterate: -------------------------------------------------------------------------------- 1 | 0 2 3 2 | Red Green Blue 3 | -------------------------------------------------------------------------------- /test/expect/4-switch: -------------------------------------------------------------------------------- 1 | 37 2 | -------------------------------------------------------------------------------- /test/expect/5-map: -------------------------------------------------------------------------------- 1 | the red channel 2 | the red channel 3 | Green 4 | Blue 5 | -------------------------------------------------------------------------------- /test/expect/6-iostreams: -------------------------------------------------------------------------------- 1 | Red 2 | -------------------------------------------------------------------------------- /test/expect/7-safety: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aantron/better-enums/520d8ee39037c9c94aa6e708a4fd6c0fa313ae80/test/expect/7-safety -------------------------------------------------------------------------------- /test/expect/8-representation: -------------------------------------------------------------------------------- 1 | Comment 2 | -------------------------------------------------------------------------------- /test/expect/9-constexpr: -------------------------------------------------------------------------------- 1 | 5 2 | -------------------------------------------------------------------------------- /test/linking/helper.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "shared.h" 3 | 4 | void print(Channel channel) 5 | { 6 | std::cout << Channel::_name() << "::" << channel._to_string() << std::endl; 7 | std::cout << Channel::_size() << std::endl; 8 | } 9 | -------------------------------------------------------------------------------- /test/linking/helper.h: -------------------------------------------------------------------------------- 1 | #ifndef HELPER_H 2 | #define HELPER_H 3 | 4 | #include "shared.h" 5 | 6 | void print(Channel channel); 7 | 8 | #endif // #ifndef HELPER_H 9 | -------------------------------------------------------------------------------- /test/linking/main.cc: -------------------------------------------------------------------------------- 1 | #include "shared.h" 2 | #include "helper.h" 3 | 4 | int main() 5 | { 6 | print(Channel::Red); 7 | 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /test/linking/shared.h: -------------------------------------------------------------------------------- 1 | #ifndef SHARED_H 2 | #define SHARED_H 3 | 4 | #include 5 | 6 | BETTER_ENUM(Channel, int, Red, Green, Blue) 7 | 8 | #endif // #ifndef SHARED_H 9 | -------------------------------------------------------------------------------- /test/performance/1-simple.cc: -------------------------------------------------------------------------------- 1 | int main() 2 | { 3 | return 0; 4 | } 5 | -------------------------------------------------------------------------------- /test/performance/2-include_empty.cc: -------------------------------------------------------------------------------- 1 | #include "empty.h" 2 | 3 | int main() 4 | { 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /test/performance/3-only_include_enum.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /test/performance/4-declare_enums.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | BETTER_ENUM(Channel, int, 4 | Red, Green, Blue, Cyan, Magenta, Yellow, Black, Hue, Saturation, 5 | Value) 6 | 7 | BETTER_ENUM(Direction, int, 8 | North, East, South, West, NorthEast, SouthEast, SouthWest, 9 | NorthWest, NorthNorthEast, EastNorthEast, EastSouthEast, 10 | SouthSouthEast, SouthSouthWest, WestSouthWest, WestNorthWest, 11 | NorthNorthWest) 12 | 13 | BETTER_ENUM(ASTNode, int, 14 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 15 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 16 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 17 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 18 | 19 | BETTER_ENUM(State, int, 20 | Attacking, Defending, Searching, Pursuing, Hungry, Fleeing, 21 | Confused, Healing, Stunned) 22 | 23 | BETTER_ENUM(APIMethod, int, 24 | ReadPost, WritePost, PollPost, ReadImage, WriteImage, PollImage, 25 | ReadKey, WriteKey, PollKey, ReadUser, WriteUser, PollUser, 26 | ReadOrganization, WriteOrganization, PollOrganization, ReadGroup, 27 | WriteGroup, PollGroup, ReadProject, WriteProject, PollProject, 28 | ReadComment, WriteComment, PollComment, ReadPermission, 29 | WritePermission, PollPermission, ReadOwner, WriteOwner, PollOwner, 30 | ReadProposal, WriteProposal, PollProposal, ReadHistory, 31 | WriteHistory, PollHistory) 32 | 33 | BETTER_ENUM(Lipsum, int, 34 | Lorem, ipsum, dolor, sit, amet, consectetur, adipiscing, elit, 35 | Vivamus, libero, massa, tincidunt, at, ex, nec, porta, malesuada, 36 | arcu, Nullam, lectus, nibh, dictum, eget, convallis, ac, feugiat, 37 | felis, Suspendisse, quis, purus, vel, lacus, cursus, tristique, 38 | Donec, augue, tortor, luctus, a, sed, mattis, in, quam, Cras, vitae, 39 | euismod, Cum, sociis, natoque, penatibus, et, magnis, dis, 40 | parturient) 41 | 42 | BETTER_ENUM(ASTNode0, int, 43 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 44 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 45 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 46 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 47 | 48 | BETTER_ENUM(ASTNode1, int, 49 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 50 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 51 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 52 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 53 | 54 | BETTER_ENUM(ASTNode2, int, 55 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 56 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 57 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 58 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 59 | 60 | BETTER_ENUM(ASTNode3, int, 61 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 62 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 63 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 64 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 65 | 66 | BETTER_ENUM(ASTNode4, int, 67 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 68 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 69 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 70 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 71 | 72 | BETTER_ENUM(ASTNode5, int, 73 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 74 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 75 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 76 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 77 | 78 | BETTER_ENUM(ASTNode6, int, 79 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 80 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 81 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 82 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 83 | 84 | BETTER_ENUM(ASTNode7, int, 85 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 86 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 87 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 88 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 89 | 90 | BETTER_ENUM(ASTNode8, int, 91 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 92 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 93 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 94 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 95 | 96 | BETTER_ENUM(ASTNode9, int, 97 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 98 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 99 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 100 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 101 | 102 | BETTER_ENUM(ASTNode10, int, 103 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 104 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 105 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 106 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 107 | 108 | BETTER_ENUM(ASTNode11, int, 109 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 110 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 111 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 112 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 113 | 114 | BETTER_ENUM(ASTNode12, int, 115 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 116 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 117 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 118 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 119 | 120 | BETTER_ENUM(ASTNode13, int, 121 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 122 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 123 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 124 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 125 | 126 | BETTER_ENUM(ASTNode14, int, 127 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 128 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 129 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 130 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 131 | 132 | BETTER_ENUM(ASTNode15, int, 133 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 134 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 135 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 136 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 137 | 138 | BETTER_ENUM(ASTNode16, int, 139 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 140 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 141 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 142 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 143 | 144 | BETTER_ENUM(ASTNode17, int, 145 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 146 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 147 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 148 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 149 | 150 | BETTER_ENUM(ASTNode18, int, 151 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 152 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 153 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 154 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 155 | 156 | BETTER_ENUM(ASTNode19, int, 157 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 158 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 159 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 160 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 161 | 162 | BETTER_ENUM(ASTNode20, int, 163 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 164 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 165 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 166 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 167 | 168 | BETTER_ENUM(ASTNode21, int, 169 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 170 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 171 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 172 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 173 | 174 | BETTER_ENUM(ASTNode22, int, 175 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 176 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 177 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 178 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 179 | 180 | BETTER_ENUM(ASTNode23, int, 181 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 182 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 183 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 184 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 185 | 186 | BETTER_ENUM(ASTNode24, int, 187 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 188 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 189 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 190 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 191 | 192 | BETTER_ENUM(ASTNode25, int, 193 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 194 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 195 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 196 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 197 | 198 | BETTER_ENUM(ASTNode26, int, 199 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 200 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 201 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 202 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 203 | 204 | BETTER_ENUM(ASTNode27, int, 205 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 206 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 207 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 208 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 209 | 210 | BETTER_ENUM(ASTNode28, int, 211 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 212 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 213 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 214 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 215 | 216 | BETTER_ENUM(ASTNode29, int, 217 | IntegerLiteral, StringLiteral, CharacterLiteral, Variable, 218 | UnaryOperation, BinaryOperation, ApplicationExpression, Abstraction, 219 | LetBinding, CaseExpression, Pattern, Signature, Module, Functor, 220 | TypeVariable, BasicType, ArrowType, VariantTypeConstant) 221 | 222 | int main() 223 | { 224 | return 0; 225 | } 226 | -------------------------------------------------------------------------------- /test/performance/5-iostream.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /test/performance/empty.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aantron/better-enums/520d8ee39037c9c94aa6e708a4fd6c0fa313ae80/test/performance/empty.h --------------------------------------------------------------------------------