{"id":16073297,"url":"https://github.com/fix8mt/conjure_enum","last_synced_at":"2025-04-05T10:26:31.745Z","repository":{"id":229575977,"uuid":"777040206","full_name":"fix8mt/conjure_enum","owner":"fix8mt","description":"Lightweight header-only C++20 enum reflection","archived":false,"fork":false,"pushed_at":"2024-04-09T05:47:57.000Z","size":224,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-04-09T06:38:24.806Z","etag":null,"topics":["bitset","c-plus-plus","cpp","cpp20","cxx20","enum","enum-to-string","fix8","header-only","magic-enum","metaprogramming","no-dependencies","no-macros","reflection","source-location","string-to-enum"],"latest_commit_sha":null,"homepage":"https://github.com/fix8mt/conjure_enum","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fix8mt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2024-03-25T04:22:21.000Z","updated_at":"2024-04-15T06:05:27.072Z","dependencies_parsed_at":"2024-04-09T06:35:42.043Z","dependency_job_id":null,"html_url":"https://github.com/fix8mt/conjure_enum","commit_stats":null,"previous_names":["fix8mt/conjure_enum"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fix8mt%2Fconjure_enum","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fix8mt%2Fconjure_enum/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fix8mt%2Fconjure_enum/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fix8mt%2Fconjure_enum/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fix8mt","download_url":"https://codeload.github.com/fix8mt/conjure_enum/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247321350,"owners_count":20919971,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bitset","c-plus-plus","cpp","cpp20","cxx20","enum","enum-to-string","fix8","header-only","magic-enum","metaprogramming","no-dependencies","no-macros","reflection","source-location","string-to-enum"],"created_at":"2024-10-09T08:06:01.624Z","updated_at":"2025-04-05T10:26:31.724Z","avatar_url":"https://github.com/fix8mt.png","language":"C++","readme":"\u003c!-----------------------------------------------------------------------------------------\n// SPDX-License-Identifier: MIT\n// SPDX-FileCopyrightText: Copyright (C) 2024 Fix8 Market Technologies Pty Ltd\n// SPDX-FileType: DOCUMENTATION\n//\n// conjure_enum (header only)\n//   by David L. Dight\n// see https://github.com/fix8mt/conjure_enum\n//\n// Lightweight header-only C++20 enum and typename reflection\n//\n// Licensed under the MIT License \u003chttp://opensource.org/licenses/MIT\u003e.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is furnished\n// to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice (including the next paragraph)\n// shall be included in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\n// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\n// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n//----------------------------------------------------------------------------------------\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.fix8mt.com\"\u003e\u003cimg src=\"assets/conjure_enum_logo.png\" width=\"200\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003ch3 align=\"center\"\u003eLightweight header-only C++20 enum and typename reflection\u003c/h3\u003e\n\n---\n\n[![clang](https://github.com/fix8mt/conjure_enum/actions/workflows/Ubuntu-clang-latest.yml/badge.svg)](https://github.com/fix8mt/conjure_enum/actions/workflows/Ubuntu-clang-latest.yml)\n[![gcc](https://github.com/fix8mt/conjure_enum/actions/workflows/Ubuntu-gcc-latest.yml/badge.svg)](https://github.com/fix8mt/conjure_enum/actions/workflows/Ubuntu-gcc-latest.yml)\n\u003ca href=\"https://en.wikipedia.org/wiki/C%2B%2B20\"\u003e\u003cimg src=\"assets/badgecpprequired.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opensource.org/license/mit\"\u003e\u003cimg src=\"assets/badgemitlic.svg\"\u003e\u003c/a\u003e\n\n# 1. Quick links\n|1|[`conjure_enum`](#3-conjure_enum)| API and examples|\n|:--|:--|:--|\n|2|[`enum_bitset`](#4-enum_bitset)| Enhanced enum aware `std::bitset`|\n|3|[`conjure_type`](#5-conjure_type)| Any type string extractor|\n|4|[`fixed_string`](#6-fixed_string)| Statically stored null terminated fixed string|\n|5|[Building](#7-building)| How to build or include|\n|6|[vcpkg](https://vcpkg.io/en/package/conjure-enum)| For vcpkg package|\n|7|[Notes](#8-notes)| Notes on the implementation, limits, etc|\n|8|[Benchmarks](#9-benchmarks)| Benchmarking |\n|9|[Compilers](#10-compiler-support)| Supported compilers|\n|10|[Compiler issues](#11-compiler-issues)| Workarounds for various compiler issues|\n|11|[Results of `std::source_location`](reference/source_location.md)| For implementation specific `std::source_location` results|\n\u003e [!TIP]\n\u003e Use the built-in [table of contents](https://github.blog/changelog/2021-04-13-table-of-contents-support-in-markdown-files/) to navigate this guide.\n\u003e Even better in [full read view](./README.md) of this page.\n\u003e\n\u003e For the latest cutting edge changes, see the [dev branch](https://github.com/fix8mt/conjure_enum/tree/dev).\n\u003e You can view the changes (if any) [here](https://github.com/fix8mt/conjure_enum/compare/master..dev).\n\n---\n# 2. Introduction\n## a) Supercharge Your C++ Enums with This Lightweight Reflection Library!\n\nBased on the awesome work in [`magic_enum`](https://github.com/Neargye/magic_enum)[^2] and [`boost::describe`](https://github.com/boostorg/describe),\nthis library offers a streamlined and powerful way to add reflection capabilities to your C++ enums and other types. We've optimized the core functionality,\nfocusing on the main features developers usually want. We've also added general purpose typename reflection for any type.\n\n`conjure_enum`[^1] takes full advantage of recently added C++20 features. We've leveraged the convenience of [`std::source_location`](https://en.cppreference.com/w/cpp/utility/source_location) and\nunlocked the potential of [`constexpr` algorithms](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0202r3.html) and [concepts](https://en.cppreference.com/w/cpp/language/constraints).\n\n## b) Highlights\n\n- ***Single Header-Only***: No external dependencies, simplifying integration into your project\n- ***Modern C++20***: Entirely `constexpr` for compile-time safety, efficiency and performance; no macros\n- ***Broad Support***: Works with:\n  - scoped and unscoped enums\n  - enums with **aliases** and **gaps**\n  - anonymous and named namespaced enums and types\n  - custom [enum ranges](#ii-using-enum_range)\n- ***Easy to Use***: Class-based approach with intuitive syntax\n- ***Convenient***: `enum_bitset` provides an enhanced enum aware `std::bitset` (see 2 above)\n- ***Useful***: `conjure_type` gives you the type string of _any type!_ (see 3 above)\n- ***Wide Compiler Compatibility***: Support for: (see 9 above)\n  - GCC\n  - Clang\n  - MSVC\n  - XCode/Apple Clang\n- ***Confidence***: Includes comprehensive unit test suite for reliable functionality and profiling\n- ***Expanded***: Enhanced API:\n  - `add_scope`\n  - `remove_scope`\n  - `unscoped_string_to_enum`\n  - `for_each_n`\n  - `dispatch`\n  - iterators and more!\n- ***Transparency***: Compiler implementation variability fully documented, verifiable and reportable (see 11 above)\n\n---\n# 3. `conjure_enum`\nAll examples refer to the following enums:\n```c++\nenum class component { scheme, authority, userinfo, user, password, host, port, path=12, test=path, query, fragment };\nenum component1 { scheme, authority, userinfo, user, password, host, port, path=12, query, fragment };\nenum class numbers { zero, one, two, three, four, five, six, seven, eight, nine };\n```\n\n\u003e [!IMPORTANT]\n\u003e Your type _must_ be an enum, satisfying:\n\u003e ```C++\n\u003etemplate\u003ctypename T\u003e\n\u003econcept valid_enum = requires(T)\n\u003e{\n\u003e   requires std::same_as\u003cT, std::decay_t\u003cT\u003e\u003e;\n\u003e   requires std::is_enum_v\u003cT\u003e;\n\u003e};\n\u003e```\n\n## a) `enum_to_string`\n```c++\nstatic constexpr std::string_view enum_to_string(T value, bool noscope=false);\ntemplate\u003cT e\u003e\nstatic constexpr std::string_view enum_to_string();\n```\nReturns a `std::string_view` or empty if not found. Optionally passing `true` will remove scope in result if present.\n`noscope` option ![](assets/notminimalred.svg).\n```c++\nauto name { conjure_enum\u003ccomponent\u003e::enum_to_string(component::path) };\nauto name_trim { conjure_enum\u003ccomponent\u003e::enum_to_string(component::path, true) }; // optionally remove scope in result\nauto alias_name { conjure_enum\u003ccomponent\u003e::enum_to_string(component::test) }; // alias\nauto noscope_name { conjure_enum\u003ccomponent1\u003e::enum_to_string(path) };\nstd::cout \u003c\u003c name \u003c\u003c '\\n' \u003c\u003c name_trim \u003c\u003c '\\n' \u003c\u003c alias_name \u003c\u003c '\\n' \u003c\u003c noscope_name \u003c\u003c '\\n';\nstd::cout \u003c\u003c conjure_enum\u003cnumbers\u003e::enum_to_string\u003cnumbers::two\u003e() \u003c\u003c '\\n';\n```\n_output_\n```CSV\ncomponent::path\npath\ncomponent::path\npath\nnumbers::two\n```\n### Aliases\nBecause all methods in `conjure_enum` are defined _within_ a `class` instead of individual template functions in a `namespace`, you can reduce your\ntyping with standard aliases:\n```c++\nusing ec = FIX8::conjure_enum\u003ccomponent\u003e;\nstd::cout \u003c\u003c std::format(\"\\\"{}\\\"\\n\", ec::enum_to_string(component::authority));\nstd::cout \u003c\u003c std::format(\"\\\"{}\\\"\\n\", ec::enum_to_string(static_cast\u003ccomponent\u003e(100)));\n```\n_output_\n```CSV\n\"component::authority\"\n\"\"\n```\nAlso supplied is a template version of `enum_to_string`.\n```c++\nstd::cout \u003c\u003c std::format(\"\\\"{}\\\"\\n\", ec::enum_to_string\u003ccomponent::scheme\u003e());\nstd::cout \u003c\u003c std::format(\"\\\"{}\\\"\\n\", ec::enum_to_string\u003cscheme\u003e());\n```\n_output_\n```CSV\n\"component::scheme\"\n\"scheme\"\n```\n\n## b) `string_to_enum`\n```c++\nstatic constexpr std::optional\u003cT\u003e string_to_enum(std::string_view str);\n```\nReturns a `std::optional\u003cT\u003e`. Empty if string was not valid. Use `std::optional\u003cT\u003e::value_or()` to set an error value\nand avoid throwing an exception.\n```c++\nint value { static_cast\u003cint\u003e(conjure_enum\u003ccomponent\u003e::string_to_enum(\"component::path\").value()) };\nint noscope_value { static_cast\u003cint\u003e(conjure_enum\u003ccomponent1\u003e::string_to_enum(\"path\").value()) };\nint bad_value { static_cast\u003cint\u003e(conjure_enum\u003ccomponent\u003e::string_to_enum(\"bad_string\").value_or(component(100))) };\nstd::cout \u003c\u003c value \u003c\u003c '\\n' \u003c\u003c noscope_value \u003c\u003c '\\n' \u003c\u003c bad_value \u003c\u003c '\\n';\n```\n_output_\n```CSV\n12\n12\n100 \u003c-- invalid, error value\n```\n## c) `unscoped_string_to_enum` ![](assets/notminimalred.svg)\n```c++\nstatic constexpr std::optional\u003cT\u003e unscoped_string_to_enum(std::string_view str);\n```\nSame as `string_to_enum` except works with unscoped strings. Returns a `std::optional\u003cT\u003e`. Empty if string was not valid. Use `std::optional\u003cT\u003e::value_or()` to set an error value\nand avoid throwing an exception.\n```c++\nint value { static_cast\u003cint\u003e(conjure_enum\u003ccomponent\u003e::unscoped_string_to_enum(\"path\").value()) };\nint noscope_value { static_cast\u003cint\u003e(conjure_enum\u003ccomponent1\u003e::string_to_enum(\"path\").value()) };\nint bad_value { static_cast\u003cint\u003e(conjure_enum\u003ccomponent\u003e::string_to_enum(\"bad_string\").value_or(component(100))) };\nstd::cout \u003c\u003c value \u003c\u003c '\\n' \u003c\u003c noscope_value \u003c\u003c '\\n' \u003c\u003c bad_value \u003c\u003c '\\n';\n```\n_output_\n```CSV\n12\n12\n100 \u003c-- invalid, error value\n```\n## d) `int_to_enum`, `enum_cast`\n```c++\nstatic constexpr std::optional\u003cT\u003e int_to_enum(int value);\nstatic constexpr T enum_cast(int value);\n```\nReturns a `std::optional\u003cT\u003e`. Empty if value was not valid. Use `std::optional\u003cT\u003e::value_or()` to set an error value\nand avoid throwing an exception. `enum_cast` will cast to the enum type regardless of whether the value is a valid enum.\n```c++\nint value { static_cast\u003cint\u003e(conjure_enum\u003ccomponent\u003e::int_to_enum(12).value()) };\nint noscope_value { static_cast\u003cint\u003e(conjure_enum\u003ccomponent1\u003e::int_to_enum(12).value()) };\nint bad_value { static_cast\u003cint\u003e(conjure_enum\u003ccomponent\u003e::int_to_enum(100).value_or(component(100))) };\nstd::cout \u003c\u003c value \u003c\u003c '\\n' \u003c\u003c noscope_value \u003c\u003c '\\n' \u003c\u003c bad_value \u003c\u003c '\\n';\nstd::cout \u003c\u003c static_cast\u003cint\u003e(conjure_enum\u003ccomponent\u003e::enum_cast(150)) \u003c\u003c '\\n';\n```\n_output_\n```CSV\n12\n12\n100 \u003c-- invalid, error value\n150 \u003c-- invalid, but still casted\n```\n## e) `enum_to_int`, `enum_to_underlying`\n```c++\nstatic constexpr int enum_to_int(T value);\nstatic constexpr std::underlying_type_t\u003cT\u003e enum_to_underlying(T value);\n```\nReturns an `int` or the `underlying` value for the given enum value. These are added for completeness. For unscoped enums\nyou can always just use the value like an int, or for scoped enums just `static_cast\u003cint\u003e` it first.\n```c++\nstd::cout \u003c\u003c conjure_enum\u003ccomponent\u003e::enum_to_int(component::path) \u003c\u003c '\\n';\nstd::cout \u003c\u003c conjure_enum\u003ccomponent\u003e::enum_to_underlying(component::path) \u003c\u003c '\\n';\nstd::cout \u003c\u003c conjure_enum\u003ccomponent1\u003e::enum_to_int(path) \u003c\u003c '\\n';\nstd::cout \u003c\u003c conjure_enum\u003ccomponent1\u003e::enum_to_underlying(path) \u003c\u003c '\\n';\n```\n_output_\n```CSV\n12\n12\n12\n12\n```\n## f) `count`\n```c++\nstatic constexpr std::size_t count();\n```\nReturns number of enumerations.\n```c++\nstd::cout \u003c\u003c conjure_enum\u003ccomponent\u003e::count()  \u003c\u003c '\\n';\n```\n_output_\n```CSV\n10\n```\n## g) `names` ![](assets/notminimalred.svg)\n```c++\nstatic constexpr std::array\u003cstd::string_view, std::size_t\u003e names;\n```\nThis static member is generated for your type. It is a `std::array` of the `std::string_view` strings in enum order.\n```c++\nfor(const auto ev : conjure_enum\u003ccomponent\u003e::names) // scoped enum\n   std::cout \u003c\u003c ev \u003c\u003c '\\n';\nfor(const auto ev : conjure_enum\u003ccomponent1\u003e::names) // unscoped enum\n   std::cout \u003c\u003c ev \u003c\u003c '\\n';\n```\n_output_\n```CSV\ncomponent::scheme\ncomponent::authority\ncomponent::userinfo\ncomponent::user\ncomponent::password\ncomponent::host\ncomponent::port\ncomponent::path\ncomponent::query\ncomponent::fragment\nscheme\nauthority\nuserinfo\nuser\npassword\nhost\nport\npath\nquery\nfragment\n```\n## h) `unscoped_names` ![](assets/notminimalred.svg)\n```c++\nstatic constexpr std::array\u003cstd::string_view, std::size_t\u003e unscoped_names;\n```\nThis static member is generated for your type. It is a `std::array` of the `std::string_view` unscoped strings in enum order.\nFor unscoped enums is the same as `names` above.\n```c++\nfor(const auto ev : conjure_enum\u003ccomponent\u003e::unscoped_names) // scoped enum\n   std::cout \u003c\u003c ev \u003c\u003c '\\n';\nstd::cout \u003c\u003c '\\n';\nfor(const auto ev : conjure_enum\u003ccomponent1\u003e::unscoped_names) // unscoped enum\n   std::cout \u003c\u003c ev \u003c\u003c '\\n';\n```\n_output_\n```CSV\nscheme\nauthority\nuserinfo\nuser\npassword\nhost\nport\npath\nquery\nfragment\n\nscheme\nauthority\nuserinfo\nuser\npassword\nhost\nport\npath\nquery\nfragment\n```\n## i) `values`\n```c++\nstatic constexpr std::array\u003cT, std::size_t\u003e values;\n```\nThis static member is generated for your type. It is a `std::array` of the `T` values in enum order.\n```c++\nfor(const auto ev : conjure_enum\u003ccomponent\u003e::values) // scoped enum\n   std::cout \u003c\u003c static_cast\u003cint\u003e(ev) \u003c\u003c '\\n';\n```\n_output_\n```CSV\n0\n1\n2\n3\n4\n5\n6\n12\n13\n14\n```\n## j) `entries`, `sorted_entries`\n```c++\nstatic constexpr std::array\u003cstd::tuple\u003cT, std::string_view\u003e, std::size_t\u003e entries;\nstatic constexpr std::array\u003cstd::tuple\u003cT, std::string_view\u003e, std::size_t\u003e sorted_entries;\n```\nThese static members are generated for your type. They are `std::array` of tuples of `T` and `std::string_view`.\n`sorted_entries` is the same as `entries` except the array is sorted by the `std::string_view` name.\n```c++\nusing ec = conjure_enum\u003ccomponent\u003e;\nfor(const auto [value, str] : ec::entries) // scoped enum\n   std::cout \u003c\u003c std::format(\"{:\u003c2} {}\\n\", static_cast\u003cint\u003e(value), str);\nstd::cout \u003c\u003c '\\n';\nfor(const auto [value, str] : ec::sorted_entries) // scoped enum\n   std::cout \u003c\u003c std::format(\"{:\u003c2} {}\\n\", static_cast\u003cint\u003e(value), str);\n```\n_output_\n```CSV\n0  component::scheme\n1  component::authority\n2  component::userinfo\n3  component::user\n4  component::password\n5  component::host\n6  component::port\n12 component::path\n13 component::query\n14 component::fragment\n\n1  component::authority\n14 component::fragment\n5  component::host\n4  component::password\n12 component::path\n6  component::port\n13 component::query\n0  component::scheme\n3  component::user\n2  component::userinfo\n```\n## k) `scoped_entries`, `unscoped_entries` ![](assets/notminimalred.svg)\n```c++\nstatic constexpr std::array\u003cstd::tuple\u003cstd::string_view, std::string_view\u003e, std::size_t\u003e scoped_entries;\n```\nThis static member is generated for your type. It is a `std::array` of a tuple of `std::string_view` pairs in enum order.\nIt contains pairs of unscoped and their scoped string version. This array is sorted by unscoped name.\nFor unscoped enums, these are identical.\n\n`unscoped_entries` is the same except the pair is reversed.\n```c++\nfor(const auto [a, b] : conjure_enum\u003ccomponent\u003e::scoped_entries)\n   std::cout \u003c\u003c std::format(\"{:9} {}\\n\", a, b);\n```\n_output_\n```CSV\nauthority component::authority\nfragment  component::fragment\nhost      component::host\npassword  component::password\npath      component::path\nport      component::port\nquery     component::query\nscheme    component::scheme\nuser      component::user\nuserinfo  component::userinfo\n```\n## l) `rev_scoped_entries` ![](assets/notminimalred.svg)\n```c++\nstatic constexpr std::array\u003cstd::tuple\u003cstd::string_view, std::string_view\u003e, std::size_t\u003e rev_scoped_entries;\n```\nSame as `scoped_entries` except reversed, sorted by scoped name. Use to lookup unscoped name.\n## m) `index`\n```c++\nstatic constexpr std::optional\u003csize_t\u003e index(T value);\ntemplate\u003cT e\u003e\nstatic constexpr std::optional\u003csize_t\u003e index();\n```\nReturns the index (position in 0 based array of values) of the supplied enum value as an `std::optional\u003csize_t\u003e`.\nEmpty if value was not valid. Use `std::optional\u003cT\u003e::value_or()` to set an error value\nand avoid throwing an exception.\n```c++\nstd::cout \u003c\u003c conjure_enum\u003ccomponent\u003e::index(component::password).value() \u003c\u003c '\\n';\nstd::cout \u003c\u003c conjure_enum\u003ccomponent\u003e::index(component(100)).value_or(100) \u003c\u003c '\\n';\nstd::cout \u003c\u003c conjure_enum\u003ccomponent\u003e::index\u003ccomponent::password\u003e().value() \u003c\u003c '\\n';\nstd::cout \u003c\u003c conjure_enum\u003ccomponent\u003e::index\u003ccomponent(100)\u003e().value_or(100) \u003c\u003c '\\n';\n```\n_output_\n```CSV\n4\n100 \u003c-- invalid, error value\n4\n100 \u003c-- invalid, error value\n```\n## n) `contains`, `is_valid`\n```c++\nstatic constexpr bool contains(T value);\nstatic constexpr bool contains(std::string_view str);\ntemplate\u003cT e\u003e\nstatic constexpr bool contains();\ntemplate\u003cT e\u003e\nstatic constexpr bool is_valid();\n```\nReturns `true` if the enum contains the given value or string.\n```c++\nstd::cout \u003c\u003c std::format(\"{}\\n\", conjure_enum\u003ccomponent\u003e::contains(component::path));\nstd::cout \u003c\u003c std::format(\"{}\\n\", conjure_enum\u003ccomponent1\u003e::contains(\"nothing\"));\nstd::cout \u003c\u003c std::format(\"{}\\n\", conjure_enum\u003ccomponent\u003e::contains\u003ccomponent::path\u003e());\nstd::cout \u003c\u003c std::format(\"{}\\n\", conjure_enum\u003ccomponent\u003e::is_valid\u003ccomponent::path\u003e());\n```\n_output_\n```CSV\ntrue\nfalse\ntrue\ntrue\n```\n## o) `for_each`, `for_each_n` ![](assets/notminimalred.svg)\n```c++\ntemplate\u003ctypename Fn, typename... Args\u003e\nrequires std::invocable\u003cFn\u0026\u0026, T, Args...\u003e\n[[maybe_unused]] static constexpr auto for_each(Fn\u0026\u0026 func, Args\u0026\u0026... args);\n\ntemplate\u003ctypename Fn, typename C, typename... Args\u003e // specialisation for member function with object\nrequires std::invocable\u003cFn\u0026\u0026, C, T, Args...\u003e\n[[maybe_unused]] static constexpr auto for_each(Fn\u0026\u0026 func, C *obj, Args\u0026\u0026... args);\n\ntemplate\u003ctypename Fn, typename... Args\u003e\nrequires std::invocable\u003cFn\u0026\u0026, T, Args...\u003e\n[[maybe_unused]] static constexpr auto for_each_n(int n, Fn\u0026\u0026 func, Args\u0026\u0026... args);\n\ntemplate\u003ctypename Fn, typename C, typename... Args\u003e // specialisation for member function with object\nrequires std::invocable\u003cFn\u0026\u0026, C, T, Args...\u003e\n[[maybe_unused]] static constexpr auto for_each_n(int n, Fn\u0026\u0026 func, C *obj, Args\u0026\u0026... args);\n```\nCall supplied invocable for _each_ enum value. Similar to `std::for_each` except the first parameter of your invocable must accept an enum value (passed by `for_each`).\nOptionally provide any additional parameters. You can limit the number of calls to your invocable by using the `for_each_n` version with the first parameter\nbeing the maximum number to call. The second version of `for_each` and `for_each_n` is intended to be used\nwhen using a member function - the _second_ parameter passed by your call must be the `this` pointer of the object.\nIf you wish to pass a `reference` parameter, you must wrap it in `std::ref`.\n\nWorks with lambdas, member functions, functions etc, compatible with `std::invoke`.\n\nReturns\n```c++\nstd::bind(std::forward\u003cFn\u003e(func), std::placeholders::_1, std::forward\u003cArgs\u003e(args)...);\n// or\nstd::bind(std::forward\u003cFn\u003e(func), obj, std::placeholders::_1, std::forward\u003cArgs\u003e(args)...);\n```\nwhich can be stored or immediately invoked.\n\nSee `enum_bitset::for_each` to iterate through a bitset.\n```c++\nconjure_enum\u003ccomponent\u003e::for_each([](component val, int other)\n{\n   std::cout \u003c\u003c static_cast\u003cint\u003e(val) \u003c\u003c ' ' \u003c\u003c other \u003c\u003c '\\n';\n}, 10);\n```\n_output_\n```CSV\n0 10\n1 10\n2 10\n3 10\n4 10\n5 10\n6 10\n12 10\n13 10\n14 10\n```\nAbove example using `for_each_n`, limiting to 3:\n```c++\nconjure_enum\u003ccomponent\u003e::for_each_n(3, [](component val, int other)\n{\n   std::cout \u003c\u003c static_cast\u003cint\u003e(val) \u003c\u003c ' ' \u003c\u003c other \u003c\u003c '\\n';\n}, 10);\n```\n_output_\n```CSV\n0 10\n1 10\n2 10\n```\nExample using returned object and additional reference parameter:\n```c++\nint total{};\nauto myfunc { conjure_enum\u003ccomponent\u003e::for_each([](component val, int other, int\u0026 tot)\n{\n   std::cout \u003c\u003c static_cast\u003cint\u003e(val) \u003c\u003c ' ' \u003c\u003c other \u003c\u003c '\\n';\n   tot += static_cast\u003cint\u003e(val);\n}, 10, std::ref(total)) };\nmyfunc(component::fragment);\nstd::cout \u003c\u003c total \u003c\u003c '\\n';\n```\n_output_\n```CSV\n0 10\n1 10\n2 10\n3 10\n4 10\n5 10\n6 10\n12 10\n13 10\n14 10\n14 10 \u003c== invoked with returned object\n74\n```\nExample with pointer to member function with additional parameters:\n```c++\nstruct foo\n{\n   void process(component val, int offset, int\u0026 tot)\n   {\n      tot += offset + static_cast\u003cint\u003e(val);\n   }\n};\nint total{};\nfoo bar;\nconjure_enum\u003ccomponent\u003e::for_each(\u0026foo::process, \u0026bar, 10, std::ref(total));\nstd::cout \u003c\u003c total \u003c\u003c '\\n';\n```\n_output_\n```CSV\n160\n```\n## p) `dispatch` ![](assets/notminimalred.svg)\n```c++\ntemplate\u003ctypename Fn\u003e\nstatic constexpr bool tuple_comp(const std::tuple\u003cT, Fn\u003e\u0026 pl, const std::tuple\u003cT, Fn\u003e\u0026 pr);\n\ntemplate\u003cstd::size_t I, typename R, typename Fn, typename... Args\u003e // with not found value(nval) for return\nrequires std::invocable\u003cFn\u0026\u0026, T, Args...\u003e\n[[maybe_unused]] static constexpr R dispatch(T ev, R nval, const std::array\u003cstd::tuple\u003cT, Fn\u003e, I\u003e\u0026 disp, Args\u0026\u0026... args);\n\ntemplate\u003cstd::size_t I, typename R, typename Fn, typename C, typename... Args\u003e // specialisation for member function with not found value(nval) for return\nrequires std::invocable\u003cFn\u0026\u0026, C, T, Args...\u003e\n[[maybe_unused]] static constexpr R dispatch(T ev, R nval, const std::array\u003cstd::tuple\u003cT, Fn\u003e, I\u003e\u0026 disp, C *obj, Args\u0026\u0026... args);\n\ntemplate\u003cstd::size_t I, typename Fn, typename... Args\u003e // void func with not found call to last element\nrequires (std::invocable\u003cFn\u0026\u0026, T, Args...\u003e \u0026\u0026 I \u003e 0)\nstatic constexpr void dispatch(T ev, const std::array\u003cstd::tuple\u003cT, Fn\u003e, I\u003e\u0026 disp, Args\u0026\u0026... args);\n\ntemplate\u003cstd::size_t I, typename Fn, typename C, typename... Args\u003e // specialisation for void member function with not found call to last element\nrequires (std::invocable\u003cFn\u0026\u0026, C, T, Args...\u003e \u0026\u0026 I \u003e 0)\nstatic constexpr void dispatch(T ev, const std::array\u003cstd::tuple\u003cT, Fn\u003e, I\u003e\u0026 disp, C *obj, Args\u0026\u0026... args);\n```\nWith a given enum, search and call user supplied invocable. A typical use case would be where you want to demux a complex event, allowing you to easily declare predefined invocable actions\nfor different enum values.\n\n- Where invocable returns a value, return this value or a user supplied \"not found\" value.\n- Where invocable is void, call user supplied invocable or \"not found\" invocable (last in supplied array).\n\nThe first parameter of your invocable must accept an enum value (passed by `dispatch`).\nOptionally provide any additional parameters.\n\nWorks with lambdas, member functions, functions etc, compatible with `std::invoke`.\n\nThere are two versions of `dispatch` - the first takes an enum value, a 'not found' value, and a `std::array` of `std::tuple` of enum and invocable.\nThe second version takes an enum value, and a `std::array` of `std::tuple` of enum and invocable. The last element of the array is called if the enum is not found.\nThis version is intended for use with `void` return invocables.\n\nThe second version of each of the above is intended to be used when using a member function - the _first_ parameter passed after your array must be the `this` pointer of the object.\nYou can also use `std::bind` to bind the this pointer and any parameter placeholders when declaring your array.\nIf you wish to pass a `reference` parameter, you must wrap it in `std::ref`.\n\n\u003e [!TIP]\n\u003e If you wish to provide a `constexpr` array, you will need to use a simple function prototype, since `std::function` is not constexpr (see unit tests for examples).\n\n\u003e [!IMPORTANT]\n\u003e Your `std::array` of `std::tuple` should be sorted by enum.\n\u003e The `dispatch` method performs a binary search on the array. Complexity for a sorted array is at most\u0026ensp; $2log_2(N)+O(1)$ \u0026ensp;comparisons.\n\u003e If the array is _not_ sorted, complexity is linear.\n\nThe following example uses a `static constexpr` array of pointers to functions. For brevity they all point to the same function except the last which is\na lambda.\n```c++\nenum class directions { left, right, up, down, forward, backward, notfound=-1 };\nstatic constexpr auto prn([](directions ev) { std::cout \u003c\u003c conjure_enum\u003cdirections\u003e::enum_to_string(ev) \u003c\u003c '\\n'; });\nstatic constexpr auto tarr\n{\n   std::to_array\u003cstd::tuple\u003cdirections, void(*)(directions)\u003e\u003e\n   ({\n      { directions::left, prn },\n      { directions::right, prn },\n      { directions::up, prn },\n      { directions::down, prn },\n      { directions::backward, prn },\n      { directions::notfound, [](directions ev) { std::cout \u003c\u003c \"not found: \"; prn(ev); } }, // not found func\n   })\n};\nconjure_enum\u003cdirections\u003e::dispatch(directions::right, tarr);\nconjure_enum\u003cdirections\u003e::dispatch(directions::down, tarr);\nconjure_enum\u003cdirections\u003e::dispatch(directions::forward, tarr);\nstd::cout \u003c\u003c conjure_enum\u003cdirections\u003e::enum_to_int(directions::notfound) \u003c\u003c '\\n';\n```\n_output_\n```CSV\ndirections::right\ndirections::down\nnot found: directions::forward\n-1\n```\nThis example uses lambdas:\n```c++\nconst auto dd1\n{\n   std::to_array\u003cstd::tuple\u003ccomponent, std::function\u003cint(component, int)\u003e\u003e\u003e\n   ({\n      { component::scheme, [](component ev, int a) { return a * 100 + conjure_enum\u003ccomponent\u003e::enum_to_int(ev); } },\n      { component::port, [](component ev, int a) { return a * 200 + conjure_enum\u003ccomponent\u003e::enum_to_int(ev); } },\n      { component::fragment, [](component ev, int a) { return a * 300 + conjure_enum\u003ccomponent\u003e::enum_to_int(ev); } },\n   })\n};\nstd::cout \u003c\u003c conjure_enum\u003ccomponent\u003e::dispatch(component::port, -1, dd1, 10) \u003c\u003c '\\n';\n```\n_output_\n```CSV\n2006\n```\nThis example uses member functions:\n```c++\nstruct foo\n{\n   int process(component val, int aint)\n   {\n      return aint * static_cast\u003cint\u003e(val);\n   }\n   int process1(component val, int aint)\n   {\n      return aint + static_cast\u003cint\u003e(val);\n   }\n};\nconstexpr auto tarr1\n{\n   std::to_array\u003cstd::tuple\u003ccomponent, int (foo::*)(component, int)\u003e\u003e\n   ({\n      { component::scheme, \u0026foo::process },\n      { component::port, \u0026foo::process },\n      { component::fragment, \u0026foo::process1 },\n   })\n};\nfoo bar;\nfor (auto val : { component::scheme, component::path, component::port, component::fragment })\n   std::cout \u003c\u003c conjure_enum\u003ccomponent\u003e::dispatch(val, -1, tarr1, \u0026bar, 1000) \u003c\u003c '\\n';\n```\n_output_\n```CSV\n0\n-1\n6000\n1015\n```\n## q) `is_scoped`\n```c++\nstruct is_scoped : std::bool_constant\u003crequires\n   { requires !std::convertible_to\u003cT, std::underlying_type_t\u003cT\u003e\u003e; }\u003e{};\n```\nReturns `true` if the specified enum type is scoped.\n```c++\nstd::cout \u003c\u003c std::format(\"{}\\n\", conjure_enum\u003ccomponent\u003e::is_scoped());\nstd::cout \u003c\u003c std::format(\"{}\\n\", conjure_enum\u003ccomponent1\u003e::is_scoped());\n```\n_output_\n```CSV\ntrue\nfalse\n```\n## r) `is_continuous`\n```c++\nstatic constexpr bool is_continuous();\n```\nReturns `true` if enum range is continuous (no gaps).\n```c++\nstd::cout \u003c\u003c std::format(\"{}\\n\", conjure_enum\u003cnumbers\u003e::is_continuous());\nstd::cout \u003c\u003c std::format(\"{}\\n\", conjure_enum\u003ccomponent\u003e::is_continuous());\n```\n_output_\n```CSV\ntrue\nfalse\n```\n## s) `type_name` ![](assets/notminimalred.svg)\n```c++\nstatic constexpr std::string_view type_name();\n```\nReturns a `std::string_view` of `T`.\n```c++\nstd::cout \u003c\u003c conjure_enum\u003ccomponent\u003e::type_name() \u003c\u003c '\\n';\nstd::cout \u003c\u003c conjure_enum\u003ccomponent1\u003e::type_name() \u003c\u003c '\\n';\n```\n_output_\n```CSV\ncomponent\ncomponent1\n```\n## t) `remove_scope` ![](assets/notminimalred.svg)\n```c++\nstatic constexpr std::string_view remove_scope(std::string_view what);\n```\nReturns a `std::string_view` with scope removed; for unscoped returns unchanged\n```c++\nstd::cout \u003c\u003c conjure_enum\u003ccomponent\u003e::remove_scope(\"component::path\"sv) \u003c\u003c '\\n';\nstd::cout \u003c\u003c conjure_enum\u003ccomponent\u003e::remove_scope(\"path\"sv) \u003c\u003c '\\n';\n```\n_output_\n```CSV\npath\npath\n```\n## u) `add_scope` ![](assets/notminimalred.svg)\n```c++\nstatic constexpr std::string_view add_scope(std::string_view what);\n```\nReturns a `std::string_view` with scope added to the enum if the supplied enum string is valid but missing scope; for unscoped returns unchanged\n```c++\nstd::cout \u003c\u003c conjure_enum\u003ccomponent\u003e::add_scope(\"path\"sv) \u003c\u003c '\\n';\nstd::cout \u003c\u003c conjure_enum\u003ccomponent1\u003e::add_scope(\"path\"sv) \u003c\u003c '\\n';\n```\n_output_\n```CSV\ncomponent::path\npath\n```\n## v) `has_scope` ![](assets/notminimalred.svg)\n```c++\nstatic constexpr bool has_scope(std::string_view what);\n```\nReturns `true` if the supplied string representation is scoped (and is valid).\n```c++\nstd::cout \u003c\u003c std::format(\"{}\\n\", conjure_enum\u003ccomponent\u003e::has_scope(\"component::scheme\"));\nstd::cout \u003c\u003c std::format(\"{}\\n\", conjure_enum\u003ccomponent\u003e::has_scope(\"scheme\"));\nstd::cout \u003c\u003c std::format(\"{}\\n\", conjure_enum\u003ccomponent1\u003e::has_scope(\"scheme\"));\n```\n_output_\n```CSV\ntrue\nfalse\nfalse\n```\n## w) `iterators` ![](assets/notminimalred.svg)\n```c++\nstatic constexpr auto cbegin();\nstatic constexpr auto cend();\nstatic constexpr auto crbegin();\nstatic constexpr auto crend();\n```\nThese methods return `const_iterator` and `const_reverse_iterator` respectively all from `entries`\ndefined above.\n```c++\nusing en = conjure_enum\u003cnumbers\u003e;\nfor (auto itr{en::cbegin()}; itr != en::cend(); ++itr)\n   std::cout \u003c\u003c static_cast\u003cint\u003e(std::get\u003c0\u003e(*itr)) \u003c\u003c ' ' \u003c\u003c std::get\u003c1\u003e(*itr) \u003c\u003c '\\n';\n```\n_output_\n```CSV\n0 numbers::zero\n1 numbers::one\n2 numbers::two\n3 numbers::three\n4 numbers::four\n5 numbers::five\n6 numbers::six\n7 numbers::seven\n8 numbers::eight\n9 numbers::nine\n```\n## x) `iterator_adaptor` ![](assets/notminimalred.svg)\n```c++\ntemplate\u003cvalid_enum T\u003e\nstruct iterator_adaptor;\n```\nThis class wraps `conjure_enum\u003cT\u003e::entries` allowing it to be used in range based for loops:\n```c++\nfor (const auto pp : iterator_adaptor\u003cnumbers\u003e())\n   std::cout \u003c\u003c static_cast\u003cint\u003e(std::get\u003c0\u003e(pp)) \u003c\u003c '\\n';\n```\n_output_\n```CSV\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n```\n## y) `front, back` ![](assets/notminimalred.svg)\n```c++\nstatic constexpr auto front();\nstatic constexpr auto back();\n```\nThese methods return `*cbegin()` and `*std::prev(cend())` respectively all from `entries`\ndefined above.\n```c++\nfor (const auto\u0026 [ev,str] : {conjure_enum\u003cnumbers\u003e::front(), conjure_enum\u003cnumbers\u003e::back()})\n   std::cout \u003c\u003c static_cast\u003cint\u003e(ev) \u003c\u003c ' ' \u003c\u003c str \u003c\u003c '\\n';\n```\n_output_\n```CSV\n0 numbers::zero\n9 numbers::nine\n```\n## z) `ostream_enum_operator` ![](assets/notminimalred.svg)\n```c++\ntemplate\u003ctypename CharT, typename Traits=std::char_traits\u003cCharT\u003e, valid_enum T\u003e\nconstexpr std::basic_ostream\u003cCharT, Traits\u003e\u0026 operator\u003c\u003c(std::basic_ostream\u003cCharT, Traits\u003e\u0026 os, T value);\n```\nProvides `std::ostream` insertion for any enum. You just need to include\n```c++\nusing ostream_enum_operator::operator\u003c\u003c;\n```\nExamples\n```c++\nusing ostream_enum_operator::operator\u003c\u003c;\nstd::cout \u003c\u003c '\"' \u003c\u003c component::host \u003c\u003c '\"' \u003c\u003c '\\n';\nstd::cout \u003c\u003c '\"' \u003c\u003c component1::host \u003c\u003c '\"' \u003c\u003c '\\n';\nstd::cout \u003c\u003c '\"' \u003c\u003c static_cast\u003ccomponent\u003e(100) \u003c\u003c '\"' \u003c\u003c '\\n';\n```\n_output_\n```CSV\n\"component::host\"\n\"host\"\n\"100\"\n```\n## A) `epeek, tpeek`\n```c++\nstatic consteval const char *tpeek();\ntemplate\u003cT e\u003e\nstatic consteval const char *epeek();\n```\nThese functions return `std::source_location::current().function_name()` as `const char*` strings for the enum type or enum value.\nThe actual output is implementation dependent. See [Results of `source_location`](reference/source_location.md) for implementation specific `std::source_location` results.\n\nThe following code:\n```c++\nstd::cout \u003c\u003c conjure_enum\u003ccomponent\u003e::tpeek() \u003c\u003c '\\n';\nstd::cout \u003c\u003c conjure_enum\u003ccomponent\u003e::epeek\u003ccomponent::scheme\u003e() \u003c\u003c '\\n';\n```\nGenerates this output with gcc:\n```CSV\nstatic consteval const char* FIX8::conjure_enum\u003cT\u003e::tpeek() [with T = component]\nstatic consteval const char* FIX8::conjure_enum\u003cT\u003e::epeek() [with T e = component::path; T = component]\n```\n\n## B) `get_enum_min_value`, `get_enum_max_value`, `get_actual_enum_min_value` and `get_actual_enum_max_value`\n```c++\nstatic constexpr int get_enum_min_value();\nstatic constexpr int get_enum_max_value();\nstatic constexpr int get_actual_enum_min_value();\nstatic constexpr int get_actual_enum_max_value();\n```\nThe first two functions return the min and max enum range for the specified enum. If you have specialised `enum_range` then these values\nwill be reported (see below).\n\nThe second two functions return the actual min and max enum values as ints for the specified enum.\n```c++\nstd::cout \u003c\u003c conjure_enum\u003ccomponent\u003e::get_enum_min_value() \u003c\u003c '/' \u003c\u003c conjure_enum\u003ccomponent\u003e::get_enum_min_value() \u003c\u003c '\\n';\nstd::cout \u003c\u003c conjure_enum\u003ccomponent\u003e::get_actual_enum_min_value() \u003c\u003c '/' \u003c\u003c conjure_enum\u003ccomponent\u003e::get_actual_enum_min_value() \u003c\u003c '\\n';\n```\n_output_\n```CSV\n-128/127\n0/14\n```\n\n## C) `in_range`\n```c++\nstatic constexpr bool in_range(T value);\n```\nReturns `true` if the given value is within the minimum and maximum defined values for this enum type.\n```c++\nstd::cout \u003c\u003c std::format(\"{}\\n\", conjure_enum\u003ccomponent\u003e::in_range(static_cast\u003ccomponent\u003e(100)));\n```\n_output_\n```CSV\nfalse\n```\n\n---\n# 4. `enum_bitset`\n`enum_bitset` is a convenient way of creating bitsets based on `std::bitset`. It uses your enum (scoped or unscoped)\nfor the bit positions (and names).\n\u003e [!NOTE]\n\u003e - Your enum sequence _must_ be 0 based\n\u003e - Continuous\n\u003e - The last value must be less than the count of enumerations\n\u003e\n\u003e We decided on these restrictions for both simplicity and practicality - bitsets only really make sense when represented in this manner; also...\n\u003e\n\u003e - This implementation is limited to 64 bits (arbitrary length impl. soon).\n\n\u003e [!IMPORTANT]\n\u003e You must include\n\u003e ```C++\n\u003e #include \u003cfix8/conjure_enum.hpp\u003e\n\u003e #include \u003cfix8/conjure_enum_bitset.hpp\u003e\n\u003e ```\n\u003e Your enum _must_ satisfy the following:\n\u003e ```C++\n\u003etemplate\u003ctypename T\u003e\n\u003econcept valid_bitset_enum = valid_enum\u003cT\u003e and requires(T)\n\u003e{\n\u003e   requires conjure_enum\u003cT\u003e::is_continuous();\n\u003e   requires conjure_enum\u003cT\u003e::get_actual_enum_min_value() == 0;\n\u003e   requires conjure_enum\u003cT\u003e::get_actual_enum_max_value() \u003c conjure_enum\u003cT\u003e::count();\n\u003e};\n\u003e```\n\n## a) Creating an `enum_bitset`\n```c++\nconstexpr enum_bitset() = default;\nconstexpr enum_bitset(U bits);\nconstexpr enum_bitset(std::string_view from, bool anyscope=false,\n   char sep='|', bool ignore_errors=true);\nconstexpr enum_bitset(std::bitset\u003cN\u003e from);\n\ntemplate\u003cvalid_bitset_enum... E\u003e\nconstexpr enum_bitset(E... comp);\n\ntemplate\u003cstd::integral... I\u003e\nconstexpr enum_bitset(I... comp);\n```\nYou can use the enum values directly in your constructor. _No_ need to `|` them - this is assumed. Just supply them comma separated:\n```c++\nenum class numbers { zero, one, two, three, four, five, six, seven, eight, nine };\nenum_bitset\u003cnumbers\u003e b(numbers::zero, numbers::one, numbers::two, numbers::three);\nstd::cout \u003c\u003c b \u003c\u003c '\\n';\n```\n_output_\n```CSV\n0000001111\n```\nYou can use the underlying type as well:\n```c++\nenum_bitset\u003cnumbers\u003e b(0,1,2,3);\nstd::cout \u003c\u003c b \u003c\u003c '\\n';\n```\n_output_\n```CSV\n0000001111\n```\nYou can use an `int` initialiser too:\n```c++\nenum_bitset\u003cnumbers\u003e b(15);\nstd::cout \u003c\u003c b \u003c\u003c '\\n';\n```\n_output_\n```CSV\n0000001111\n```\nYou can even use a delimited string based on your enum names.\nOptionally omit the scope and even specify your own delimiter (default is `|`).\nSubstrings are trimmed of whitespace before lookup.\n```c++\nenum_bitset\u003cnumbers\u003e b(\"numbers::zero|numbers::one|numbers::two|numbers::three\");\nstd::cout \u003c\u003c b \u003c\u003c '\\n';\nenum_bitset\u003cnumbers\u003e b1(\"zero,one  ,two,  three\", true, ',');\nstd::cout \u003c\u003c b1 \u003c\u003c '\\n';\nenum_bitset\u003cnumbers\u003e b2(\"zero|one|two|three\", true);\nstd::cout \u003c\u003c b2 \u003c\u003c '\\n';\n```\n_output_\n```CSV\n0000001111\n0000001111\n0000001111\n```\nA typical use of the above is for parsing configuration bitsets. Here you can tell the constructor to throw an `std::invalid_argument`\nif a substring is invalid by specifying `false` for `ignore_errors`:\n```c++\ntry\n{\n   enum_bitset\u003cnumbers\u003e b(\"zero,twenty,two,three\", true, ',', false);\n   std::cout \u003c\u003c b \u003c\u003c '\\n';\n}\ncatch(const std::invalid_argument\u0026 e)\n{\n   std::cerr \u003c\u003c \"exception: \" \u003c\u003c e.what() \u003c\u003c '\\n';\n}\n```\n_output_\n```CSV\nexception: twenty\n```\nYou can also create an `enum_bitset` from a `std::bitset` of the same number of bits.\n```c++\nstd::bitset\u003c10\u003e bs{1 \u003c\u003c 1 | 1 \u003c\u003c 3 | 1 \u003c\u003c 6};\nenum_bitset\u003cnumbers\u003e ed(bs);\nstd::cout \u003c\u003c ed \u003c\u003c '\\n';\n```\n_output_\n```CSV\n0001001010\n```\n\n## b) Standard bit operators\nAll of the standard operators are supported. Assignment operators return a `enum_bitset\u0026`, non-assignment operators return a `enum_bitset`.\n\n| Operator | Description |\n| :--- | :--- |\n| `\u0026` | binary AND |\n| `\\|` | binary OR |\n| `^` | binary XOR |\n| `~` | binary NOT (ones' complement)|\n| `\u003c\u003c` | left shift |\n| `\u003e\u003e` | right shift |\n| `\u003c\u003c=` | left shift assign |\n| `\u003e\u003e=` | right shift assign |\n| `\u0026=` | AND assign |\n| `\\|=` | OR assign |\n| `^=` | XOR assign |\n\nOperators work with enum values or integers:\n```c++\nenum_bitset\u003cnumbers\u003e b(numbers::zero, numbers::one, numbers::two, numbers::three);\nstd::cout \u003c\u003c b \u003c\u003c '\\n';\nstd::cout \u003c\u003c (b \u0026 0b111) \u003c\u003c '\\n';\nb ^= numbers::two;\nstd::cout \u003c\u003c b \u003c\u003c '\\n';\n```\n_output_\n```\n0000001111\n0000000111\n0000001011\n```\n## c) Standard accessors and mutators\nAll of the standard accessors and mutators are supported.\n| Method | Description |\n| :--- | :--- |\n| `test` | test for bit(s)|\n| `set` | set all or 1 bit, optionally set to off|\n| `reset` | reset bits(s)|\n| `flip` | flip bits(s) (ones' complement)|\n| `to_ulong` | convert to `unsigned long` |\n| `to_ullong` | convert to `unsigned long long` |\n| `count` | count of bits on |\n| `size` | number of bits in bitset |\n| `operator[]` | set or test bit at position or enum value |\n| `any` | return `true` if any bit is on |\n| `all` | return `true` if all bits are on |\n| `none` | return `true` if no bits are on |\n\nAdditional methods\n| Method | Description |\n| :--- | :--- |\n| `set` | set all specified bits, templated |\n| `reset` | reset all specified bits, templated |\n| `rotl` | rotate left specified times|\n| `rotr` | rotate right specified times|\n| `countl_zero` | counts number of consecutive `0` bits, starting from the most significant bit |\n| `countl_one` | counts number of consecutive `1` bits, starting from the most significant bit |\n| `countr_zero` | counts number of consecutive `0` bits, starting from the least significant bit |\n| `countr_one` | counts number of consecutive `1` bits, starting from the least significant bit |\n| `any_of` | test for one or more bits, templated, function, types and underlyings |\n| `all_of` | test for all specified bits, templated, function, types and underlyings |\n| `none_of` | test for all specified bits set to off, templated, function, types and underlyings |\n| `not_count` | complement of count, count of off bits |\n| `has_single_bit` | return true if bitset is an integral power of two|\n\n\u003e [!NOTE]\n\u003e `rotl`, `rotl`, `countl*` and `countr*` operate on the _used_ bits of the underlying type.\n\nTake a look at the [implementation](include/fix8/conjure_enum_bitset.hpp) for more detail on the various API functions available.\nYou can also review the unit test cases for examples of use.\n\nAll accessors and mutators work with enum values or integers as with operators. They also work with multiple values, either as template parameters or\nas variadic arguments:\n```c++\nenum_bitset\u003cnumbers\u003e eb;\neb.set\u003cnumbers::zero,numbers::two,numbers::five,numbers::nine\u003e();\nstd::cout \u003c\u003c eb \u003c\u003c '\\n';\nstd::cout \u003c\u003c std::boolalpha \u003c\u003c eb.all_of\u003cnumbers::zero,numbers::two,numbers::five,numbers::nine\u003e() \u003c\u003c '\\n';\neb.reset\u003cnumbers::five,numbers::two\u003e();\nstd::cout \u003c\u003c std::boolalpha \u003c\u003c eb.all_of(0, 2, 5, 9) \u003c\u003c '\\n';\nstd::cout \u003c\u003c std::boolalpha \u003c\u003c eb.any_of(0, 2, 5, 9) \u003c\u003c '\\n';\nstd::cout \u003c\u003c std::boolalpha \u003c\u003c eb.all_of(numbers::zero,numbers::nine) \u003c\u003c '\\n';\nstd::cout \u003c\u003c eb \u003c\u003c '\\n';\neb.reset(numbers::nine)\nstd::cout \u003c\u003c ec \u003c\u003c '\\n';\neb.reset();\neb[2] = true;\neb[numbers::three] = true;\nstd::cout \u003c\u003c eb \u003c\u003c '\\n';\nstd::cout \u003c\u003c eb.rotr(1) \u003c\u003c '\\n';\n```\n_output_\n```\n1000100101\ntrue\nfalse\ntrue\ntrue\n1000000001\n0000000001\n0000001100\n0000011000\n```\n## d) Other functions\n### i. `operator bool`\n```c++\nconstexpr operator bool() const;\n```\nReturn true if _any_ bits are on.\n\n```c++\nif (enum_bitset\u003cnumbers\u003e ec(15); ec)\n   std::cout \u003c\u003c ec \u003c\u003c '\\n';\n```\n_output_\n```CSV\n0001001111\n```\n\n### ii. `operator std::bitset\u003cN\u003e()`\n```c++\nconstexpr operator std::bitset\u003cN\u003e() const;\n```\nCast an `enum_bitset` to a `std::bitset` with the same number of bits.\n\n```c++\nenum_bitset\u003cnumbers\u003e ec(numbers::one,numbers::three,numbers::six);\nstd::bitset\u003c10\u003e bs{ec};\nstd::cout \u003c\u003c bs \u003c\u003c '\\n';\n```\n_output_\n```CSV\n0001001010\n```\n\n### iii. `std::ostream\u0026 operator\u003c\u003c`, `to_string`, `to_hex_string`\n```c++\nfriend constexpr std::ostream\u0026 operator\u003c\u003c(std::ostream\u0026 os, const enum_bitset\u0026 what);\nconstexpr std::string to_string(char zero='0', char one='1') const;\n\ntemplate\u003cbool showbase=true, bool uppercase=false\u003e\nconstexpr std::string to_hex_string() const;\n\nconstexpr std::string to_hex_string() const;\n```\nInserts default string representation into `std::ostream`.\u003cbr\u003e\nReturns a `std::string` representation of the bitset. Optionally specify which characters to use for `0` and `1`.\u003cbr\u003e\nReturns a `std::string` representation of the bitset in hex format. Optionally specify `showbase` which will prefix\nthe string with `0x` or `0X`; optionally specify `uppercase` which will set the case of the hex digits.\n\n```c++\nenum_bitset\u003cnumbers\u003e ec(numbers::one,numbers::three,numbers::six);\nstd::cout \u003c\u003c ec \u003c\u003c '\\n';\nstd::cout \u003c\u003c ec.to_string('-', '+') \u003c\u003c '\\n';\nstd::cout \u003c\u003c ec.to_hex_string() \u003c\u003c '\\n';\nstd::cout \u003c\u003c ec.to_hex_string\u003ctrue, true\u003e() \u003c\u003c '\\n';\n```\n_output_\n```CSV\n0001001010\n---+--+-+-\n0x4a\n0X4A\n```\n### iv. `for_each`, `for_each_n`\n```c++\ntemplate\u003ctypename Fn, typename... Args\u003e\nrequires std::invocable\u003cFn\u0026\u0026, T, Args...\u003e\n[[maybe_unused]] constexpr auto for_each(Fn\u0026\u0026 func, Args\u0026\u0026... args);\n\ntemplate\u003ctypename C, typename Fn, typename... Args\u003e // specialisation for member function with object\nrequires std::invocable\u003cFn\u0026\u0026, C, T, Args...\u003e\n[[maybe_unused]] constexpr auto for_each(Fn\u0026\u0026 func, C *obj, Args\u0026\u0026... args);\n\ntemplate\u003ctypename Fn, typename... Args\u003e\nrequires std::invocable\u003cFn\u0026\u0026, T, Args...\u003e\n[[maybe_unused]] constexpr auto for_each_n(int n, Fn\u0026\u0026 func, Args\u0026\u0026... args);\n\ntemplate\u003ctypename C, typename Fn, typename... Args\u003e // specialisation for member function with object\nrequires std::invocable\u003cFn\u0026\u0026, C, T, Args...\u003e\n[[maybe_unused]] constexpr auto for_each_n(int n, Fn\u0026\u0026 func, C *obj, Args\u0026\u0026... args);\n```\nCall supplied invocable for _every bit that is on_. Similar to `std::for_each` except first parameter of your invocable must accept an enum value (passed by `for_each`).\nOptionally provide any additional parameters. Works with lambdas, member functions, functions etc. You can limit the number of calls to your\ninvocable by using the `for_each_n` version with the first parameter being the maximum number to call. The second version of `for_each` and `for_each_n` is intended to be used\nwhen using a member function - the _second_ parameter passed by your call must be the `this` pointer of the object.\nIf you wish to pass a `reference` parameter, you must wrap it in `std::ref`.\n\nReturns `std::bind(std::forward\u003cFn\u003e(func), std::placeholders::_1, std::forward\u003cArgs\u003e(args)...)` or\n`std::bind(std::forward\u003cFn\u003e(func), obj, std::placeholders::_1, std::forward\u003cArgs\u003e(args)...)` which can be stored or immediately invoked.\n\nTo iterate over every bit regardless of whether it is on or not, use `conjure_enum\u003cT\u003e::for_each`.\n\nExample using member function:\n```c++\nstruct foo\n{\n   void printer(numbers val, std::ostream\u0026 ostr) const\n   {\n      ostr \u003c\u003c conjure_enum\u003cnumbers\u003e::enum_to_string(val) \u003c\u003c '\\n';\n   }\n};\nenum_bitset\u003cnumbers\u003e ec(numbers::zero,numbers::two,numbers::five,numbers::nine);\nconst foo bar;\nec.for_each(\u0026foo::printer, \u0026bar, std::ref(std::cout));\n```\n_output_\n```CSV\nnumbers::zero\nnumbers::two\nnumbers::five\nnumbers::nine\n```\nAbove example using `for_each_n`, limiting to 3:\n```c++\nec.for_each_n(3, \u0026foo::printer, \u0026bar, std::ref(std::cout));\n```\n_output_\n```CSV\nnumbers::zero\nnumbers::two\nnumbers::five\n```\n\n### v. Using `conjure_enum::dispatch` with `enum_bitset`\nUsing an `enum_bitset` wth `conjure_enum::dispatch` can be a convenient way of iterating through a set of bits to call specific functions using `for_each`. The following demonstrates this:\n```c++\nconst auto dd3\n{\n   std::to_array\u003cstd::tuple\u003cnumbers, std::function\u003cvoid(numbers, int)\u003e\u003e\u003e\n   ({\n      { numbers::one, [](numbers ev, int a)\n         { std::cout \u003c\u003c 1000 + a + conjure_enum\u003cnumbers\u003e::enum_to_int(ev) \u003c\u003c '\\n'; } },\n      { numbers::two, [](numbers ev, int a)\n         { std::cout \u003c\u003c 2000 + a + conjure_enum\u003cnumbers\u003e::enum_to_int(ev) \u003c\u003c '\\n'; } },\n      { numbers::three, [](numbers ev, int a)\n         { std::cout \u003c\u003c 3000 + a + conjure_enum\u003cnumbers\u003e::enum_to_int(ev) \u003c\u003c '\\n'; } },\n      { static_cast\u003cnumbers\u003e(-1), [](numbers ev, [[maybe_unused]] int a)\n         { std::cout \u003c\u003c \"not found: \" \u003c\u003c conjure_enum\u003cnumbers\u003e::enum_to_int(ev) \u003c\u003c '\\n'; } }, // not found func\n   })\n};\nenum_bitset\u003cnumbers\u003e(1,2,3,5).for_each([](numbers val, const auto\u0026 arr, int num)\n{\n   conjure_enum\u003cnumbers\u003e::dispatch(val, arr, num);\n}, dd3, 100);\n```\n_output_\n```CSV\n1101\n2102\n3103\nnot found: 5\n```\n\n### vi. `get_underlying`\n```c++\nconstexpr U get_underlying() const;\n```\nReturns the underlying integral value.\n\n### vii. `get_underlying_bit_size`\n```c++\nconstexpr int get_underlying_bit_size() const\n```\nReturns the number of bits that the underlying integral contains. Will always be a power of 2 and an integral type. The number of bits may be larger\nthan the count of bits.\n\n### viii. `get_bit_mask`,`get_unused_bit_mask`\n```c++\nconstexpr U get_bit_mask() const;\nconstexpr U get_unused_bit_mask() const;\n```\nReturns a bit mask that would mask off the _unused_ bits of the underlying integral.\u003cbr\u003e\nReturns a bit mask that would mask off the _used_ bits of the underlying integral.\n\n### ix. `std::hash\u003cenum_bitset\u003cT\u003e\u003e`\n```c++\ntemplate\u003ctypename T\u003e\nstruct std::hash\u003cFIX8::enum_bitset\u003cT\u003e\u003e;\n```\nProvides a specialization of `std::hash` for `enum_bitset\u003cT\u003e`.\n\n---\n# 5. `conjure_type`\n`conjure_type` is a general purpose class allowing you to extract a string representation of any typename.\nThe string will be stored statically by the compiler, so you can use the statically generated value `name` to obtain your type.\n\u003e [!IMPORTANT]\n\u003e You must include\n\u003e ```C++\n\u003e #include \u003cfix8/conjure_enum.hpp\u003e\n\u003e #include \u003cfix8/conjure_type.hpp\u003e\n\u003e ```\n\n## a) `name`\nThis static member is generated for your type. It is a `fixed_string` but has a built-in `std::string_view` operator.\n```c++\ntemplate\u003ctypename T\u003e\nclass conjure_type;\nstatic constexpr fixed_string name;\n```\n\n```c++\nclass foo;\nstd::cout \u003c\u003c std::format(\"\\\"{}\\\"\\n\", conjure_type\u003cfoo\u003e::name);\n```\n_output_\n```CSV\n\"foo\"\n```\nWorks with aliases:\n```c++\nusing test = std::map\u003cstd::size_t, std::string_view\u003e;\nusing test1 = std::map\u003cstd::size_t, foo\u003e;\nstd::cout \u003c\u003c conjure_type\u003ctest\u003e::name \u003c\u003c '\\n';\nstd::cout \u003c\u003c conjure_type\u003ctest1\u003e::name \u003c\u003c '\\n';\nstd::cout \u003c\u003c conjure_type\u003cstd::underlying_type_t\u003cnumbers\u003e\u003e::name \u003c\u003c '\\n';\n```\n_output_\n```CSV\nstd::map\u003clong unsigned int, std::basic_string_view\u003cchar\u003e \u003e\nstd::map\u003clong unsigned int, foo\u003e\nint\n```\nWorks with its own types too:\n```c++\nstd::cout \u003c\u003c conjure_type\u003cconjure_type\u003cconjure_enum\u003cnumbers\u003e\u003e\u003e::name \u003c\u003c '\\n';\n```\n_output_\n```CSV\nFIX8::conjure_type\u003cFIX8::conjure_enum\u003cnumbers\u003e \u003e\n```\nIf you need to explicitly obtain a `std::string_view`, use the `get()` method on `name` (not windows sorry):\n```c++\nauto fstrv { conjure_type\u003ctest\u003e::name };\nauto strv { conjure_type\u003ctest\u003e::name.get() };\nstd::cout \u003c\u003c conjure_type\u003cdecltype(fstrv)\u003e::name \u003c\u003c '\\n';\nstd::cout \u003c\u003c conjure_type\u003cdecltype(strv)\u003e::name \u003c\u003c '\\n';\n```\n_output_\n```CSV\nfixed_string\u003c58\u003e\nstd::basic_string_view\u003cchar\u003e\n```\nAlternatively you can use the `as_string_view()` method:\n```c++\nauto fstrv { conjure_type\u003ctest\u003e::as_string_view() };\nstd::cout \u003c\u003c conjure_type\u003cdecltype(fstrv)\u003e::name \u003c\u003c '\\n';\n```\n_output_\n```CSV\nstd::basic_string_view\u003cchar\u003e\n```\n\n## b) `as_string_view`\nReturn the name as a `std::string_view`.\n```c++\nstatic constexpr std::string_view as_string_view();\n```\n\n## c) `tpeek`\n```c++\nstatic consteval const char *tpeek();\n```\n\nThese functions return `std::source_location::current().function_name()` as `const char*` strings for type.\nThe actual output is implementation dependent. See [Results of `source_location`](reference/source_location.md) for implementation specific `std::source_location` results.\n\nThe following code:\n```c++\nstd::cout \u003c\u003c conjure_type\u003ctest\u003e::tpeek() \u003c\u003c '\\n';\n```\nGenerates this output with gcc:\n```CSV\nstatic consteval const char* FIX8::conjure_type\u003cT\u003e::tpeek() [with T = test]\n```\n\n---\n# 6. `fixed_string`\n`fixed_string` is a specialisation of `std::array` that provides statics storage for an ASCII zero (asciiz) string. The purpose of this class is to allow the\ncreation of `constexpr` strings with specfic storage, adding a trailing `0`. It is used by `conjure_enum` to store all strings. API is described below.\n\n## a) Creating a `fixed_string`\n```c++\ntemplate\u003cstd::size_t N\u003e\nclass fixed_string;\nconstexpr fixed_string(std::string_view sv);\n```\nConstructs a `fixed_string` from a `std::string_view`. The source string is _copied_ and a null character is added to the end. Note the size of the _source_ string must be passed as a template parameter.\n```c++\nstd::string_view sv{\"The rain in Spain\"};\nconstexpr fixed_string\u003csv.size()\u003e fs{sv};\n```\n\n## b) `get`\n```c++\nconstexpr std::string_view get() const;\n```\nReturns the string as a `std::string_view`.\n\n## c) `c_str`\n```c++\nconstexpr const char *c_str() const;\n```\nReturns the string as a null terminated `const char *`.\n\n## d) `operator std::string_view`\n```c++\nconstexpr operator std::string_view() const;\n```\nProvides a `std::string_view` cast. Returns the string as a `std::string_view`.\n\n## e) `operator[]`\n```c++\nconstexpr char operator[](size_t idx) const;\n```\nReturns the character at the specifdined index. It is not range checked.\n\n## f) `size`\n```c++\nconstexpr std::size_t size() const;\n```\nReturns the size of the `fixed_string` including the null terminator.\n\n## g) `std::ostream\u0026 operator\u003c\u003c`\n```c++\nstd::ostream\u0026 operator\u003c\u003c(std::ostream\u0026 os, const fixed_string\u0026 what)\n```\nProvides an `ostream` insertor.\n\n---\n# 7. Building\nThis implementation is header only. Apart from standard C++20 includes there are no external dependencies needed in your application.\n[Catch2](https://github.com/catchorg/Catch2.git) is used for the built-in unit tests.\n\u003e [!TIP]\n\u003e The unit test source files [unittests.cpp](utests/unittests.cpp) and [edgetests.cpp](utests/edgetests.cpp) contain additional examples for all\n\u003e the APIs.\n\n## a) Obtaining the source, building the unittests and examples\n### i. Build platform\n#### \\*nix based environments\nTo clone and default build the test app, unit tests and the benchmark:\n```bash\n$ git clone https://github.com/fix8mt/conjure_enum.git\n$ cd conjure_enum\n$ mkdir build\n$ cd build\n$ cmake ..\n$ make -j4\n$ ctest (or make test)\n```\n#### Windows environments\nCreate a new console project. Add the repo `https://github.com/fix8mt/conjure_enum.git` and clone the source.\nMake sure you set the C++ language to C++20 in the project preferences. The project should build and run the unit tests\nby default.\n\nThe package is also available on [vckpg](https://vcpkg.io/en/package/conjure-enum).\n\n### ii. Default compiler warnings\nBy default all warnings are enabled. To prevent this, pass the following to cmake:\n```bash\n$ cmake -DBUILD_ALL_WARNINGS=false ..\n```\n### iii. Default unit tests\nBy default the unit tests are built (which will download Catch2). To prevent this, pass the following to cmake:\n```bash\n$ cmake -DBUILD_UNITTESTS=false ..\n```\n### iv. Default executable stripping\nTo disable stripping of the executables:\n```bash\n$ cmake -DBUILD_STRIP_EXE=false ..\n```\n### v. Clang compilation profiling\nTo enable clang compilation profiling:\n```bash\n$ cmake -DBUILD_CLANG_PROFILER=true ..\n```\n## b) Using in your application with cmake\nIn `CMakeLists.txt` set your include path to:\n```CMake\ninclude_directories([conjure_enum directory]/include)\n# e.g.\nset(cjedir /home/dd/prog/conjure_enum)\ninclude_directories(${cjedir}/include)\n```\nand just include:\n```c++\n#include \u003cfix8/conjure_enum.hpp\u003e\n```\nin your application. Everything in this class is within the namespace `FIX8`, so you can add:\n```c++\nusing namespace FIX8;\n```\n\n## c) Integrating `conjure_enum` in your project with cmake FetchContent\nYou can use cmake [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) to integrate `conjure_enum` with your project.\nIf your project was called `myproj` with the sourcefile `myproj.cpp` then...\n```CMake\nproject(myproj)\nadd_executable (myproj myproj.cpp)\nset_target_properties(myproj PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED true)\nmessage(STATUS \"Downloading conjure_enum...\")\ninclude(FetchContent)\nFetchContent_Declare(conjure_enum GIT_REPOSITORY https://github.com/fix8mt/conjure_enum.git)\nFetchContent_MakeAvailable(conjure_enum)\ntarget_include_directories(myproj PRIVATE ${conjure_enum_SOURCE_DIR}/include)\n```\n\n## d) Reporting issues\nRaise an [issue](https://github.com/fix8mt/conjure_enum/issues) on the github page.\nThe executable `srcloctest` should be built when you build the package by default. This application\ndoes not use any of the `conjure_enum` library and is designed to report how your compiler handles `std::source_location`.\nThe actual output is implementation dependent. See [Results of `source_location`](reference/source_location.md) for implementation specific `std::source_location` results.\nYou should attach the output of this application with your issue.\n\u003e [!TIP]\n\u003e Use the `-m` switch with `srcloctest` to generate github markdown which you can paste directly into the issue.\n\n```C++\n$ ./srcloctest\nCompiler: Clang: Ubuntu Clang 16.0.6 (23ubuntu4)\n1. scoped enum\nstatic const char *conjure_type\u003cNamespace_Enum_Type\u003e::tpeek() [T = Namespace_Enum_Type]\nstatic const char *conjure_enum\u003cNamespace_Enum_Type\u003e::epeek() [T = Namespace_Enum_Type, e = Namespace_Enum_Type::Value]\nstatic const char *conjure_enum\u003cNamespace_Enum_Type\u003e::epeek() [T = Namespace_Enum_Type, e = (Namespace_Enum_Type)100]\n\n2. unscoped enum\nstatic const char *conjure_type\u003cNamespace_Enum_Type1\u003e::tpeek() [T = Namespace_Enum_Type1]\nstatic const char *conjure_enum\u003cNamespace_Enum_Type1\u003e::epeek() [T = Namespace_Enum_Type1, e = Value]\nstatic const char *conjure_enum\u003cNamespace_Enum_Type1\u003e::epeek() [T = Namespace_Enum_Type1, e = (Namespace_Enum_Type1)100]\n\n3. scoped enum in anonymous namespace\nstatic const char *conjure_type\u003c(anonymous namespace)::Anon_Enum_Type\u003e::tpeek() [T = (anonymous namespace)::Anon_Enum_Type]\nstatic const char *conjure_enum\u003c(anonymous namespace)::Anon_Enum_Type\u003e::epeek() [T = (anonymous namespace)::Anon_Enum_Type, e = (anonymous namespace)::Anon_Enum_Type::Value]\nstatic const char *conjure_enum\u003c(anonymous namespace)::Anon_Enum_Type\u003e::epeek() [T = (anonymous namespace)::Anon_Enum_Type, e = ((anonymous namespace)::Anon_Enum_Type)100]\n\n4. unscoped enum in anonymous namespace\nstatic const char *conjure_type\u003c(anonymous namespace)::Anon_Enum_Type1\u003e::tpeek() [T = (anonymous namespace)::Anon_Enum_Type1]\nstatic const char *conjure_enum\u003c(anonymous namespace)::Anon_Enum_Type1\u003e::epeek() [T = (anonymous namespace)::Anon_Enum_Type1, e = (anonymous namespace)::Value]\nstatic const char *conjure_enum\u003c(anonymous namespace)::Anon_Enum_Type1\u003e::epeek() [T = (anonymous namespace)::Anon_Enum_Type1, e = ((anonymous namespace)::Anon_Enum_Type1)100]\n\n5. scoped enum in namespace\nstatic const char *conjure_type\u003cNamespace::Namespace_Enum_Type\u003e::tpeek() [T = Namespace::Namespace_Enum_Type]\nstatic const char *conjure_enum\u003cNamespace::Namespace_Enum_Type\u003e::epeek() [T = Namespace::Namespace_Enum_Type, e = Namespace::Namespace_Enum_Type::Value]\nstatic const char *conjure_enum\u003cNamespace::Namespace_Enum_Type\u003e::epeek() [T = Namespace::Namespace_Enum_Type, e = (Namespace::Namespace_Enum_Type)100]\n\n6. unscoped enum in namespace\nstatic const char *conjure_type\u003cNamespace::Namespace_Enum_Type1\u003e::tpeek() [T = Namespace::Namespace_Enum_Type1]\nstatic const char *conjure_enum\u003cNamespace::Namespace_Enum_Type1\u003e::epeek() [T = Namespace::Namespace_Enum_Type1, e = Namespace::Value]\nstatic const char *conjure_enum\u003cNamespace::Namespace_Enum_Type1\u003e::epeek() [T = Namespace::Namespace_Enum_Type1, e = (Namespace::Namespace_Enum_Type1)100]\n\n7. types in named and anonymous namespaces\nstatic const char *conjure_type\u003cFoo\u003e::tpeek() [T = Foo]\nstatic const char *conjure_type\u003cNamespace::Namespace_Foo\u003e::tpeek() [T = Namespace::Namespace_Foo]\nstatic const char *conjure_type\u003c(anonymous namespace)::Anon_Foo\u003e::tpeek() [T = (anonymous namespace)::Anon_Foo]\n\n8. other types\nstatic const char *conjure_type\u003cint\u003e::tpeek() [T = int]\nstatic const char *conjure_type\u003cstd::basic_string_view\u003cchar\u003e\u003e::tpeek() [T = std::basic_string_view\u003cchar\u003e]\nstatic const char *conjure_type\u003cstd::vector\u003cstd::tuple\u003cint, char, std::basic_string_view\u003cchar\u003e\u003e\u003e\u003e::tpeek() [T = std::vector\u003cstd::tuple\u003cint, char, std::basic_string_view\u003cchar\u003e\u003e\u003e]\n\n9. edge enum types\nstatic const char *conjure_type\u003c(anonymous namespace)::NineEnums\u003e::tpeek() [T = (anonymous namespace)::NineEnums]\nstatic const char *conjure_type\u003c(anonymous namespace)::NineEnums1\u003e::tpeek() [T = (anonymous namespace)::NineEnums1]\nstatic const char *conjure_type\u003cTEST::NineEnums\u003e::tpeek() [T = TEST::NineEnums]\nstatic const char *conjure_type\u003cTEST::NineEnums1\u003e::tpeek() [T = TEST::NineEnums1]\nstatic const char *conjure_type\u003c(anonymous namespace)::TEST1::NineEnums\u003e::tpeek() [T = (anonymous namespace)::TEST1::NineEnums]\nstatic const char *conjure_type\u003c(anonymous namespace)::TEST1::NineEnums1\u003e::tpeek() [T = (anonymous namespace)::TEST1::NineEnums1]\nstatic const char *conjure_type\u003cTEST::TEST1::NineEnums\u003e::tpeek() [T = TEST::TEST1::NineEnums]\nstatic const char *conjure_type\u003cTEST::TEST1::NineEnums1\u003e::tpeek() [T = TEST::TEST1::NineEnums1]\n$\n```\n\n## e) Contributing\nContributions are welcome. Make your changes in [your fork on the dev branch](https://github.com/fix8mt/conjure_enum/tree/dev) and open a pull request from there. PRs to\nmaster will not be considered.\n\n---\n# 8. Notes\n## a) enum limits\nCompilation times increase with the number of enums that use `conjure_enum` in any compilation unit.\n1. For a simple project with few enums, there is probably no need to set any limits;\n1. Where the enum is defined elsewhere (say if you are using `std::errc`) then use `enum_range` or one of the convenience macros;\n1. Where the enum is unscoped then use `enum_range` or one of the convenience macros;\n1. Where you have defined the enum yourself and it is a scoped enum, use `T::ce_first` and `T::ce_last`, or 2.\n\n### i. `FIX8_CONJURE_ENUM_MIN_VALUE`, `FIX8_CONJURE_ENUM_MAX_VALUE`\nThese are set by default unless you override them by defining them in your application. They are the global range default for all enums using `conjure_enum`.\n\u003e [!IMPORTANT]\n\u003e If you want to define these values they must appear _before_ you include `conjure_enum.hpp`.\n\nThe following are the default settings:\n```c++\n#if not defined FIX8_CONJURE_ENUM_MIN_VALUE\n# define FIX8_CONJURE_ENUM_MIN_VALUE -128\n#endif\n#if not defined FIX8_CONJURE_ENUM_MAX_VALUE\n# define FIX8_CONJURE_ENUM_MAX_VALUE 127\n#endif\n```\nThese definitions set the minimum and maximum enum values that are supported. You can adjust them to suit your requirements but for most use cases the defaults are sufficient.\n\u003e [!TIP]\n\u003e You can reduce compile times in some circumstances by narrowing the range of `FIX8_CONJURE_ENUM_MIN_VALUE` and `FIX8_CONJURE_ENUM_MAX_VALUE`. For example\n\u003e if your enums are only within the range of say `0-16` you can set `FIX8_CONJURE_ENUM_MIN_VALUE` and `FIX8_CONJURE_ENUM_MAX_VALUE` to `0` and `16` respectively. If the range is _too_ narrow\n\u003e `conjure_enum` will **ignore enum values outside your range**.\n\n\u003e [!TIP]\n\u003e If you wish to set ranges on a per enum basis, use `enum_range` (see below).\n\n### ii. using `enum_range`\nYou can specialise this class to override the defaults and set your own range on a per enum basis.\n```c++\ntemplate\u003cvalid_enum T\u003e\nstruct enum_range\n{\n   static constexpr int min{FIX8_CONJURE_ENUM_MIN_VALUE}, max{FIX8_CONJURE_ENUM_MAX_VALUE};\n};\n```\nThe `min` and `max` values are used to set the range of enum values for enums in `conjure_enum`. As shown above, the default values will be\n`FIX8_CONJURE_ENUM_MIN_VALUE` and `FIX8_CONJURE_ENUM_MAX_VALUE`.\n\n```c++\nenum class range_test { first, second, third, fourth, fifth, sixth, seventh, eighth };\ntemplate\u003c\u003e\nstruct FIX8::enum_range\u003crange_test\u003e\n{\n   static constexpr int min{0}, max{7};\n};\nstatic_assert(conjure_enum\u003crange_test\u003e::get_enum_min_value() == 0);\nstatic_assert(conjure_enum\u003crange_test\u003e::get_enum_max_value() == 7);\n```\n#### ii.a `FIX8_CONJURE_ENUM_SET_RANGE_INTS`, `FIX8_CONJURE_ENUM_SET_RANGE`\nFor convenience, two macros are provided to make it easier to set custom ranges using `enum_range`.\n```c++\n#define FIX8_CONJURE_ENUM_SET_RANGE_INTS(ec,min_int,max_int)...\n#define FIX8_CONJURE_ENUM_SET_RANGE(min_enum,max_enum)...\n```\nThe first macro takes an enum typename followed by a lower and upper int range value.\n\nThe second macro takes a lower and upper enum value. For example:\n```c++\nFIX8_CONJURE_ENUM_SET_RANGE_INTS(numbers, 0, 7)\n```\n```c++\nFIX8_CONJURE_ENUM_SET_RANGE(numbers::first, numbers::eighth)\n```\n\n### iii. using `T::ce_first` and `T::ce_last`\nAnother approach to setting a custom range for an enum is to alias the first and last enum values in your enum definition\nusing `ce_first` and `ce_last`. `conjure_enum` will use these values to set the enum range.\nYou can set either, both or neither. A range value not set will default to the `FIX8_CONJURE_ENUM_MIN_VALUE` or `FIX8_CONJURE_ENUM_MAX_VALUE`.\n\n\u003e [!WARNING]\n\u003e With _unscoped_ enums, there can only be one enum type with `ce_first` and `ce_last` defined in any translation unit (ODR).\n\u003e It is therefore recommended to only use this feature with scoped enums.\n\nFor example:\n```c++\nenum class range_test\n{\n   first, second, third, fourth, fifth, sixth, seventh, eighth,\n   ce_first=first, ce_last=eighth\n};\nusing rt = conjure_enum\u003crange_test\u003e;\nstd::cout \u003c\u003c rt::get_enum_min_value() \u003c\u003c '/' \u003c\u003c rt::get_enum_max_value() \u003c\u003c '\\n';\nstd::cout \u003c\u003c rt::get_actual_enum_min_value() \u003c\u003c '/' \u003c\u003c rt::get_actual_enum_max_value() \u003c\u003c '\\n';\n```\n_output_\n```CSV\n0/7\n0/7\n```\n\n## b) Choosing the minimal build\n```c++\n#define FIX8_CONJURE_ENUM_MINIMAL\n```\nYou can select a minimal version of `conjure_enum` by defining `FIX8_CONJURE_ENUM_MINIMAL` _before_ you include `conjure_enum.hpp`\n\nThis limits the API to a more basic set of functionality. This will reduce compile times.\nStatic structures and API calls that will be excluded are:\n```c++\nscoped_entries\nunscoped_entries\nrev_scoped_entries\nnames\nunscoped_names\nremove_scope\nadd_scope\nunscoped_string_to_enum\nfor_each,for_each_n\ntype_name\ndispatch\niterators\nenum_to_string //noscope option not available\n```\nThese are marked ![](assets/notminimalred.svg) in the API documentation above.\n\n## c) Continuous enum optimization\n```c++\n#define FIX8_CONJURE_ENUM_IS_CONTINUOUS\n```\nIf your enum(s) are continuous (no gaps) you can enable this compiler optimization\nby defining `FIX8_CONJURE_ENUM_IS_CONTINUOUS` _before_ you include `conjure_enum.hpp`.\nOur testing shows a reduction in overall compile times. All enums using `conjure_enum.hpp` in the current compilation unit must be continuous.\n\n## d) Anonymous enum optimization\n```c++\n#define FIX8_CONJURE_ENUM_NO_ANON\n```\nIf your enum(s) are not within any anonymous namespaces (rarely used for this purpose), you can enable this compiler optimization\nby defining `FIX8_CONJURE_ENUM_NO_ANON` _before_ you include `conjure_enum.hpp`.\nOur testing shows a reduction in overall compile times. All enums using `conjure_enum.hpp` in the current compilation unit must be continuous.\n\n## e) Enable all optimizations\n```c++\n#define FIX8_CONJURE_ENUM_ALL_OPTIMIZATIONS\n```\nYou can enable all optimizations described above by defining `FIX8_CONJURE_ENUM_ALL_OPTIMIZATIONS` _before_ you include `conjure_enum.hpp`.\nThis is the equivalent of defining:\n```c++\n#define FIX8_CONJURE_ENUM_MINIMAL\n#define FIX8_CONJURE_ENUM_IS_CONTINUOUS\n#define FIX8_CONJURE_ENUM_NO_ANON\n```\n\n## f) Class `conjure_enum` is not constructible\nAll methods in this class are _static_. You cannot instantiate an object of this type. The same goes for `conjure_type`.\n\n## g) It's not _real_ reflection\nThis library provides a workaround (hack :smirk:) to current limitations of C++. There are proposals out there for future versions of the language that will provide proper reflection.\nSee [Reflection TS](https://en.cppreference.com/w/cpp/experimental/reflect) and [Reflection for C++26](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2996r0.html)\nfor examples of some of these.\n\n## h) Use of `std::string_view`\nAll of the generated static strings and generated static tables obtained by `std::source_location` use the library defined `fixed_string`. No string copying is done at runtime, resulting in\na single static string in your application. All `conjure_enum` methods that return strings _only_ return `std::string_view`.\nTo demonstrate this, lets look at the supplied test application `statictest`:\n```c++\n#include \u003ciostream\u003e\n#define FIX8_CONJURE_ENUM_MINIMAL\n#include \u003cfix8/conjure_enum.hpp\u003e\nenum class component : int { scheme, authority, userinfo, user, password, host, port, path, query, fragment };\nFIX8_CONJURE_ENUM_SET_RANGE(component::scheme, component::fragment)\nint main(void)\n{\n   for(const auto\u0026 [a, b] : conjure_enum\u003ccomponent\u003e::entries)\n      std::cout \u003c\u003c conjure_enum\u003ccomponent\u003e::enum_to_int(a) \u003c\u003c ' ' \u003c\u003c b \u003c\u003c '\\n';\n   std::cout \u003c\u003c static_cast\u003cint\u003e(conjure_enum\u003ccomponent\u003e::string_to_enum(\"component::path\").value()) \u003c\u003c '\\n';\n   std::cout \u003c\u003c conjure_enum\u003ccomponent\u003e::get_enum_min_value() \u003c\u003c '/' \u003c\u003c conjure_enum\u003ccomponent\u003e::get_enum_max_value() \u003c\u003c '\\n';\n   return 0;\n}\n```\n_output_\n```CSV\n$ ./statictest\n0 component::scheme\n1 component::authority\n2 component::userinfo\n3 component::user\n4 component::password\n5 component::host\n6 component::port\n7 component::path\n8 component::query\n9 component::fragment\ncomponent::scheme\ncomponent::authority\ncomponent::userinfo\ncomponent::user\ncomponent::password\ncomponent::host\ncomponent::port\ncomponent::path\ncomponent::query\ncomponent::fragment\n7\n0/9\n$\n```\n\nThe default build of `statictest` performs a [strip](https://en.wikipedia.org/wiki/Strip_(Unix)) on the executable.\nThen we run [strings](https://en.wikipedia.org/wiki/Strings_(Unix)) on the executable.\n\u003cdetails\u003e\u003csummary\u003e\u003ci\u003eshell output\u003c/i\u003e\u003c/summary\u003e\n\u003cp\u003e\n\u003cpre\u003e$ strings statictest\n/lib64/ld-linux-x86-64.so.2\n__gmon_start__\n_ITM_deregisterTMCloneTable\n_ITM_registerTMCloneTable\n_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c\n_ZNSolsEi\n_ZSt21ios_base_library_initv\n_ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l\n_ZNSo3putEc\n_ZSt4cout\n__stack_chk_fail\n__libc_start_main\n__cxa_finalize\nlibstdc++.so.6\nlibc.so.6\nGLIBC_2.4\nGLIBC_2.34\nGLIBC_2.2.5\nGLIBCXX_3.4.32\nGLIBCXX_3.4.9\nGLIBCXX_3.4\nATUH\n[]A\\A]A^\nPTE1\nu+UH\n\u003ci\u003e\u003cb\u003ecomponent::fragment\u003c/b\u003e\u003c/i\u003e\n\u003ci\u003e\u003cb\u003ecomponent::query\u003c/b\u003e\u003c/i\u003e\n\u003ci\u003e\u003cb\u003ecomponent::path\u003c/b\u003e\u003c/i\u003e\n\u003ci\u003e\u003cb\u003ecomponent::port\u003c/b\u003e\u003c/i\u003e\n\u003ci\u003e\u003cb\u003ecomponent::host\u003c/b\u003e\u003c/i\u003e\n\u003ci\u003e\u003cb\u003ecomponent::password\u003c/b\u003e\u003c/i\u003e\n\u003ci\u003e\u003cb\u003ecomponent::user\u003c/b\u003e\u003c/i\u003e\n\u003ci\u003e\u003cb\u003ecomponent::userinfo\u003c/b\u003e\u003c/i\u003e\n\u003ci\u003e\u003cb\u003ecomponent::authority\u003c/b\u003e\u003c/i\u003e\n\u003ci\u003e\u003cb\u003ecomponent::scheme\u003c/b\u003e\u003c/i\u003e\n9*3$\"\nGCC: (Ubuntu 13.2.0-23ubuntu4) 13.2.0\n.shstrtab\n.interp\n.note.gnu.property\n.note.gnu.build-id\n.note.ABI-tag\n.gnu.hash\n.dynsym\n.dynstr\n.gnu.version\n.gnu.version_r\n.rela.dyn\n.rela.plt\n.init\n.plt.got\n.plt.sec\n.text\n.fini\n.rodata\n.eh_frame_hdr\n.eh_frame\n.init_array\n.fini_array\n.data.rel.ro\n.dynamic\n.data\n.bss\n.comment\n$\n\u003c/pre\u003e\u003c/p\u003e\u003c/details\u003e\n\nIt can be observed that there is only _one_ copy of the scoped enum value string in the executable.\n\n## i) Compilation profiling (Clang)\nYou can profile the compile time for Clang (other compilers TBA). Firstly install [ClangBuildAnalyzer](https://github.com/aras-p/ClangBuildAnalyzer).\nThen configure `conjure_enum` with:\n```CMake\ncmake -DBUILD_CLANG_PROFILER=true ..\n```\nYou can follow the instructions given on the `ClangBuildAnalyzer` page to run. Alternatively after you build the included program `cbenchmark`,\nrun the included script [cbenchmark.sh](examples/cbenchmark.sh). The script expects the following environment variables:\n\n| Variable | Description |\n| :--- | :--- |\n| `ClangBuildAnalyzerLoc` | directory where ClangBuildAnalyzer can be found|\n| `ArtifactLoc` | directory where `conjure_enum` is built|\n\nFor example, if `ClangBuildAnalyzer` was built in `~/prog/ClangBuildAnalyzer/build` and your `conjure_enum` build was in `./build_clang`, then you\nwould run the script from the `conjure_enum` directory as follows:\n```bash\nClangBuildAnalyzerLoc=~/prog/ClangBuildAnalyzer/build ArtifactLoc=build_clang examples/cbenchmark.sh\n```\nThe results will be printed to the screen. For example:\n\u003cdetails\u003e\u003csummary\u003e\u003ci\u003eoutput\u003c/i\u003e\u003c/summary\u003e\n\u003cp\u003e\n\n```CSV\nProcessing all files and saving to 'cbenchmark.dat'...\n  done in 0.0s. Run 'ClangBuildAnalyzer --analyze cbenchmark.dat' to analyze it.\nAnalyzing build trace from 'cbenchmark.dat'...\n**** Time summary:\nCompilation (2 times):\n  Parsing (frontend):            0.4 s\n  Codegen \u0026 opts (backend):      0.0 s\n\n**** Files that took longest to parse (compiler frontend):\n   423 ms: build_clang/CMakeFiles/cbenchmark.dir/examples/cbenchmark.cpp.o\n\n**** Templates that took longest to instantiate:\n   187 ms: FIX8::conjure_enum\u003cstd::errc\u003e (1 times, avg 187 ms)\n   119 ms: FIX8::conjure_enum\u003cstd::errc\u003e::_entries\u003c0UL, 1UL, 2UL, 3UL, 4UL, 5UL... (1 times, avg 119 ms)\n     8 ms: FIX8::conjure_enum\u003cstd::errc\u003e::_sorted_entries (1 times, avg 8 ms)\n     6 ms: std::sort\u003cstd::tuple\u003cstd::errc, std::basic_string_view\u003cchar\u003e\u003e *, boo... (1 times, avg 6 ms)\n     6 ms: std::__sort\u003cstd::tuple\u003cstd::errc, std::basic_string_view\u003cchar\u003e\u003e *, _... (1 times, avg 6 ms)\n     5 ms: std::__introsort_loop\u003cstd::tuple\u003cstd::errc, std::basic_string_view\u003cc... (1 times, avg 5 ms)\n     3 ms: std::array\u003cstd::tuple\u003cstd::errc, std::basic_string_view\u003cchar\u003e\u003e, 72\u003e (1 times, avg 3 ms)\n     2 ms: std::tuple\u003cstd::basic_string_view\u003cchar\u003e, char, std::basic_string_vie... (1 times, avg 2 ms)\n     2 ms: std::__unguarded_partition_pivot\u003cstd::tuple\u003cstd::errc, std::basic_st... (1 times, avg 2 ms)\n     2 ms: std::__move_median_to_first\u003cstd::tuple\u003cstd::errc, std::basic_string_... (1 times, avg 2 ms)\n     2 ms: std::iter_swap\u003cstd::tuple\u003cstd::errc, std::basic_string_view\u003cchar\u003e\u003e *... (1 times, avg 2 ms)\n     2 ms: std::__partial_sort\u003cstd::tuple\u003cstd::errc, std::basic_string_view\u003ccha... (1 times, avg 2 ms)\n     2 ms: std::tuple\u003cstd::errc, std::basic_string_view\u003cchar\u003e\u003e (1 times, avg 2 ms)\n     2 ms: std::__heap_select\u003cstd::tuple\u003cstd::errc, std::basic_string_view\u003cchar... (1 times, avg 2 ms)\n     2 ms: std::__make_heap\u003cstd::tuple\u003cstd::errc, std::basic_string_view\u003cchar\u003e\u003e... (1 times, avg 2 ms)\n     1 ms: std::optional\u003cunsigned long\u003e (1 times, avg 1 ms)\n     1 ms: std::to_array\u003cstd::tuple\u003cstd::basic_string_view\u003cchar\u003e, char, std::ba... (1 times, avg 1 ms)\n     1 ms: std::basic_string\u003cchar32_t\u003e (1 times, avg 1 ms)\n     1 ms: std::basic_string\u003cchar8_t\u003e (1 times, avg 1 ms)\n     1 ms: std::__adjust_heap\u003cstd::tuple\u003cstd::errc, std::basic_string_view\u003cchar... (1 times, avg 1 ms)\n     1 ms: std::basic_string\u003cchar16_t\u003e (1 times, avg 1 ms)\n     1 ms: std::basic_string\u003cwchar_t\u003e (1 times, avg 1 ms)\n     1 ms: std::basic_string\u003cchar\u003e (1 times, avg 1 ms)\n     1 ms: std::__and_\u003cstd::__is_swappable\u003cstd::errc\u003e, std::__is_swappable\u003cstd:... (1 times, avg 1 ms)\n     1 ms: std::_Tuple_impl\u003c0, std::basic_string_view\u003cchar\u003e, char, std::basic_s... (1 times, avg 1 ms)\n     1 ms: std::__final_insertion_sort\u003cstd::tuple\u003cstd::errc, std::basic_string_... (1 times, avg 1 ms)\n     1 ms: std::basic_string\u003cchar32_t\u003e::_M_construct\u003cconst char32_t *\u003e (1 times, avg 1 ms)\n     1 ms: std::basic_string\u003cchar8_t\u003e::_M_construct\u003cconst char8_t *\u003e (1 times, avg 1 ms)\n     1 ms: std::basic_string\u003cchar16_t\u003e::_M_construct\u003cconst char16_t *\u003e (1 times, avg 1 ms)\n     1 ms: std::operator+\u003cchar, std::char_traits\u003cchar\u003e, std::allocator\u003cchar\u003e\u003e (1 times, avg 1 ms)\n\n**** Template sets that took longest to instantiate:\n   187 ms: FIX8::conjure_enum\u003c$\u003e (1 times, avg 187 ms)\n   119 ms: FIX8::conjure_enum\u003c$\u003e::_entries\u003c$\u003e (1 times, avg 119 ms)\n    49 ms: FIX8::conjure_enum\u003c$\u003e::_get_name\u003c$\u003e (72 times, avg 0 ms)\n    12 ms: std::tuple\u003c$\u003e::tuple\u003c$\u003e (21 times, avg 0 ms)\n     8 ms: FIX8::conjure_enum\u003c$\u003e::_sorted_entries (1 times, avg 8 ms)\n     7 ms: std::basic_string\u003c$\u003e (5 times, avg 1 ms)\n     6 ms: std::sort\u003c$\u003e (1 times, avg 6 ms)\n     6 ms: std::__sort\u003c$\u003e (1 times, avg 6 ms)\n     5 ms: std::basic_string\u003c$\u003e::_M_construct\u003c$\u003e (5 times, avg 1 ms)\n     5 ms: std::tuple\u003c$\u003e (2 times, avg 2 ms)\n     5 ms: std::__introsort_loop\u003c$\u003e (1 times, avg 5 ms)\n     4 ms: std::array\u003c$\u003e (2 times, avg 2 ms)\n     2 ms: std::__unguarded_partition_pivot\u003c$\u003e (1 times, avg 2 ms)\n     2 ms: std::__and_\u003c$\u003e (3 times, avg 0 ms)\n     2 ms: std::__move_median_to_first\u003c$\u003e (1 times, avg 2 ms)\n     2 ms: std::iter_swap\u003c$\u003e (1 times, avg 2 ms)\n     2 ms: std::__partial_sort\u003c$\u003e (1 times, avg 2 ms)\n     2 ms: std::__heap_select\u003c$\u003e (1 times, avg 2 ms)\n     2 ms: std::__make_heap\u003c$\u003e (1 times, avg 2 ms)\n     1 ms: std::_Tuple_impl\u003c$\u003e (2 times, avg 0 ms)\n     1 ms: std::optional\u003c$\u003e (1 times, avg 1 ms)\n     1 ms: std::to_array\u003c$\u003e (1 times, avg 1 ms)\n     1 ms: std::__adjust_heap\u003c$\u003e (1 times, avg 1 ms)\n     1 ms: std::basic_string\u003c$\u003e::basic_string (2 times, avg 0 ms)\n     1 ms: std::__final_insertion_sort\u003c$\u003e (1 times, avg 1 ms)\n     1 ms: std::operator+\u003cchar, std::char_traits\u003cchar\u003e, std::allocator\u003cchar\u003e\u003e (1 times, avg 1 ms)\n     1 ms: std::__insertion_sort\u003c$\u003e (1 times, avg 1 ms)\n     0 ms: FIX8::conjure_enum\u003c$\u003e::_tuple_comp_rev (1 times, avg 0 ms)\n     0 ms: std::tuple\u003cstd::errc, std::basic_string_view\u003cchar\u003e\u003e::operator= (1 times, avg 0 ms)\n     0 ms: __gnu_cxx::__to_xstring\u003c$\u003e (1 times, avg 0 ms)\n\n**** Functions that took longest to compile:\n     0 3s: test_conjure_enum(std::errc) (/home/davidd/prog/conjure_enum_tclass/examples/cbenchmark.cpp)\n\n**** Function sets that took longest to compile / optimize:\n\n**** Expensive headers:\n166 ms: /usr/include/c++/14/system_error (included 1 times, avg 166 ms), included via:\n  1x: \u003cdirect include\u003e\n\n54 ms: /home/davidd/prog/conjure_enum_tclass/include/fix8/conjure_enum.hpp (included 1 times, avg 54 ms), included via:\n  1x: \u003cdirect include\u003e\n\n  done in 0.0s.\n```\n\n\u003c/p\u003e\u003c/details\u003e\n\n---\n# 9. Benchmarks\nWe have benchmarked compilation times for `conjure_enum` and `magic_enum`.\nFor `magic_enum` we created a separate repo (see [here](https://github.com/fix8mt/magic_enum_benchmark)).\n\n| Compiler | `conjure_enum` (secs) | `magic_enum` (secs)| Notes |\n| :--- | :--- | :--- |:--- |\n| **MSVC** | 0.376 | 0.343 | using cl from command prompt|\n|_command_ | `cl /nologo /MD /std:c++latest /Bt+ /I ..\\include  ..\\examples\\cbenchmark.cpp\\|find \"c1xx.dll\"` | `cl /nologo /MD /std:c++latest /Bt+ -I build\\_deps\\magic_enum-src\\include cbenchmark.cpp\\|find \"c1xx.dll\"`||\n| **clang** | 0.3 | 0.3 | using ClangBuildAnalyzer|\n|_command_|`make`; `ClangBuildAnalyzerLoc=~/prog/ClangBuildAnalyzer/build ArtifactLoc=build_clang examples/cbenchmark.sh`|`make`; `ClangBuildAnalyzerLoc=~/prog/ClangBuildAnalyzer/build ArtifactLoc=build_clang ./cbenchmark.sh`||\n\n## Notes\n- Benchmark run 10 times, best result shown\n- Both benchmarks are using [cbenchmark.sh](examples/cbenchmark.sh) and [cbenchmark.cpp](examples/cbenchmark.cpp)\n- MSVC: Windows 11 ThinkCentre 16x 13th Gen Intel i7-13700, 32Gb; MSVC 2022 / 17.11.0.\n- Clang: Ubuntu 24.04 12th Gen Intel i9-12900T, 32Gb; Clang 18.1.3\n- `magic_enum`: single header only\n- `conjure_enum`: minimal build\n\n## Discussion\nFor MSVC, `magic_enum` compilation times a slighly better than `conjure_enum` (around %9). For clang the results are identical.\nFrom a compilation performance perspective, `conjure_enum` roughly matches the performance of `magic_enum`.\n\n---\n# 10. Compiler support\n| Compiler | Version(s) | Notes | Unsupported |\n| :--- | :--- | :--- | ---: |\n| [gcc](https://gcc.gnu.org/projects/cxx-status.html) | `11`, `12`, `13`, `14`| `std::format` not complete in `11`, `12` | `\u003c= 10` |\n| [clang](https://clang.llvm.org/cxx_status.html) | `15`, `16`, `17`, `18`| Catch2 needs `cxx_std_20` in `15` | `\u003c= 14` |\n| [msvc](https://learn.microsoft.com/en-us/cpp/overview/visual-cpp-language-conformance) | `16`, `17` | Visual Studio 2019,2022, latest `17.11.3`| `\u003c= 16.9`|\n| [xcode](https://developer.apple.com/support/xcode/) | `15` | Apple Xcode Clang 15.0.0 (LLVM 16), some issues with `constexpr`, workarounds| `\u003c= 14`|\n\n# 11. Compiler issues\n| Compiler | Version(s) | Issues | Workaround |\n| :--- | :--- | :--- | ---: |\n| clang | `16`, `17`, `18`| Compiler reports integers outside valid range [x,y]| specify underlying type when declaring enum eg. `enum class foo : int` |\n\n[^1]: \u0026copy; 2024 Fix8 Market Technologies Pty Ltd, David L. Dight.\n  Logo by [Adrian An](mailto:adrian.an[at]mac.com).\n[^2]: \u0026copy; 2019 - 2024 Daniil Goncharov\n\n#\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.fix8mt.com\"\u003e\u003cimg src=\"assets/fix8mt_Master_Logo_Green_Trans.png\" width=\"120\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffix8mt%2Fconjure_enum","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffix8mt%2Fconjure_enum","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffix8mt%2Fconjure_enum/lists"}