Skip to content

Commit 19172ec

Browse files
authored
Merge pull request #23 from fix8mt/dev
Dev 1.2.0
2 parents 0198333 + 5e3a3bb commit 19172ec

File tree

15 files changed

+581
-141
lines changed

15 files changed

+581
-141
lines changed

CMakeLists.txt

Lines changed: 29 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -----------------------------------------------------------------------------------------
22
# SPDX-License-Identifier: MIT
3-
# SPDX-FileCopyrightText: Copyright (C) 2024 Fix8 Market Technologies Pty Ltd
3+
# SPDX-FileCopyrightText: Copyright (C) 2024-25 Fix8 Market Technologies Pty Ltd
44
# SPDX-FileType: SOURCE
55
#
66
# conjure_enum (header only)
@@ -28,79 +28,50 @@
2828
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
2929
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3030
# ----------------------------------------------------------------------------------------
31-
cmake_minimum_required (VERSION 3.20)
31+
cmake_minimum_required (VERSION 3.21+)
32+
message(STATUS "CMake version ${CMAKE_VERSION}")
33+
include(cmake/buildutils.cmake)
3234
project (conjure_enum
3335
LANGUAGES CXX
34-
HOMEPAGE_URL https://github.com/fix8mt/conjure_enum
36+
HOMEPAGE_URL https://github.com/fix8mt/${PROJECT_NAME}
3537
DESCRIPTION "Lightweight C++20 enum and typename reflection"
36-
VERSION 1.1.0)
38+
VERSION 1.2.0)
3739

38-
message("${CMAKE_PROJECT_NAME} version ${CMAKE_PROJECT_VERSION}")
39-
40-
# to disable strip:
41-
# cmake -DBUILD_STRIP_EXE=false ..
42-
option(BUILD_STRIP_EXE "enable stripping of non-unittest executables" true)
43-
message("-- Build: strip exes ${BUILD_STRIP_EXE}")
44-
45-
# to disable warnings:
46-
# cmake -DBUILD_ALL_WARNINGS=false ..
47-
option(BUILD_ALL_WARNINGS "enable building with all warnings" true)
48-
message("-- Build: with all warnings ${BUILD_ALL_WARNINGS}")
49-
50-
# to disable building unit tests:
51-
# cmake -DBUILD_UNITTESTS=false ..
52-
option(BUILD_UNITTESTS "enable building unit tests" true)
53-
message("-- Build: unit tests ${BUILD_UNITTESTS}")
54-
55-
# to enable clang build profiler:
56-
# cmake -DBUILD_CLANG_PROFILER=true ..
57-
# see examples/cbenchmark.sh
58-
option(BUILD_CLANG_PROFILER "enable clang build profiler" false)
59-
message("-- Build: clang profiler ${BUILD_CLANG_PROFILER}")
60-
if(BUILD_CLANG_PROFILER)
61-
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
62-
add_compile_options(-ftime-trace)
63-
else()
64-
message(WARNING "BUILD_CLANG_PROFILER only available with Clang")
65-
endif()
40+
if(NOT MSVC)
41+
fix8_setbuildtype(CONJURE_ENUM Release)
6642
endif()
43+
fix8_addoption("BUILD_UNITTESTS|enable building unit tests|true")
44+
fix8_addoption("BUILD_STRIP_EXE|enable stripping of non-unittest executables|true")
45+
fix8_addoption("BUILD_ALL_WARNINGS|enable building with all warnings|true")
46+
fix8_addoption("BUILD_CLANG_PROFILER|enable clang build profiler|false")
6747

68-
function(build loc x)
69-
add_executable(${x} ${loc}/${x}.cpp)
70-
if(NOT ${x} STREQUAL srcloctest)
71-
add_dependencies(${x} srcloctest) # srcloctest should be built even if errors with conjure_enum
72-
endif()
73-
set_target_properties(${x} PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED true)
74-
target_include_directories(${x} PRIVATE include)
75-
if(BUILD_ALL_WARNINGS)
76-
target_compile_options(${x} PRIVATE
77-
$<$<CXX_COMPILER_ID:MSVC>:/W4>
78-
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Wpedantic>)
79-
endif()
80-
get_target_property(cppstd ${x} CXX_STANDARD)
81-
message("-- adding ${x}.cpp CXX_STANDARD: C++${cppstd} (${CMAKE_CXX_COMPILER_ID})")
82-
endfunction()
83-
84-
foreach(x srcloctest example statictest cbenchmark)
85-
build(examples ${x})
48+
fix8_build(examples srcloctest 20)
49+
foreach(x example statictest cbenchmark)
50+
fix8_build(examples ${x} 20)
51+
add_dependencies(${x} srcloctest) # srcloctest should be built even if errors with conjure_enum
8652
if(BUILD_STRIP_EXE)
8753
add_custom_command(TARGET ${x} POST_BUILD COMMAND ${CMAKE_STRIP} ${x})
8854
endif()
8955
endforeach()
9056

57+
# make include visible to inheriting projects
58+
add_library(${PROJECT_NAME} INTERFACE)
59+
file(GLOB HEADER_FILES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/fix8/*.hpp")
60+
target_sources(${PROJECT_NAME} INTERFACE FILE_SET HEADERS FILES ${HEADER_FILES})
61+
install(TARGETS ${PROJECT_NAME} FILE_SET HEADERS DESTINATION .)
62+
target_include_directories(${PROJECT_NAME} INTERFACE
63+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
64+
$<INSTALL_INTERFACE:include>)
65+
9166
if(BUILD_UNITTESTS)
92-
include(FetchContent)
93-
FetchContent_Declare(Catch2
94-
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
95-
GIT_SHALLOW ON
96-
GIT_TAG devel)
97-
FetchContent_MakeAvailable(Catch2)
67+
fix8_fetch(Catch2 catchorg/Catch2 devel)
68+
target_compile_features(Catch2 PRIVATE cxx_std_17)
69+
add_dependencies(Catch2 srcloctest) # srcloctest should be built before building Catch2
9870
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
99-
target_compile_features(Catch2 PRIVATE cxx_std_20)
10071
include(Catch)
10172
enable_testing()
10273
foreach(x unittests edgetests)
103-
build(utests ${x})
74+
fix8_build(utests ${x} 20)
10475
target_link_libraries(${x} PRIVATE Catch2::Catch2WithMain)
10576
catch_discover_tests(${x})
10677
endforeach()

README.md

Lines changed: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<!-----------------------------------------------------------------------------------------
22
// SPDX-License-Identifier: MIT
3-
// SPDX-FileCopyrightText: Copyright (C) 2024 Fix8 Market Technologies Pty Ltd
3+
// SPDX-FileCopyrightText: Copyright (C) 2024-25 Fix8 Market Technologies Pty Ltd
44
// SPDX-FileType: DOCUMENTATION
55
//
66
// conjure_enum (header only)
@@ -65,7 +65,7 @@
6565
# 2. Introduction
6666
## a) Supercharge Your C++ Enums with This Lightweight Reflection Library!
6767

68-
Based on the awesome work in [`magic_enum`](https://github.com/Neargye/magic_enum)[^2] and [`boost::describe`](https://github.com/boostorg/describe),
68+
Based on some of the awesome work in [`magic_enum`](https://github.com/Neargye/magic_enum)[^2] and [`boost::describe`](https://github.com/boostorg/describe),
6969
this library offers a streamlined and powerful way to add reflection capabilities to your C++ enums and other types. We've optimized the core functionality,
7070
focusing on the main features developers usually want. We've also added general purpose typename reflection for any type.
7171

@@ -126,7 +126,7 @@ template<T e>
126126
static constexpr std::string_view enum_to_string();
127127
```
128128
Returns a `std::string_view` or empty if not found. Optionally passing `true` will remove scope in result if present.
129-
`noscope` option ![](assets/notminimalred.svg).
129+
![](assets/notminimalred.svg).
130130
```c++
131131
auto name { conjure_enum<component>::enum_to_string(component::path) };
132132
auto name_trim { conjure_enum<component>::enum_to_string(component::path, true) }; // optionally remove scope in result
@@ -143,6 +143,11 @@ component::path
143143
path
144144
numbers::two
145145
```
146+
147+
> [!TIP]
148+
> For scoped enums, this method will return the fully qualified enum name (with the class prefix). If you want
149+
> to return the unscoped enum specify `true`
150+
146151
### Aliases
147152
Because all methods in `conjure_enum` are defined _within_ a `class` instead of individual template functions in a `namespace`, you can reduce your
148153
typing with standard aliases:
@@ -472,20 +477,20 @@ requires std::invocable<Fn&&, T, Args...>
472477

473478
template<typename Fn, typename C, typename... Args> // specialisation for member function with object
474479
requires std::invocable<Fn&&, C, T, Args...>
475-
[[maybe_unused]] static constexpr auto for_each(Fn&& func, C *obj, Args&&... args);
480+
[[maybe_unused]] static constexpr auto for_each(Fn&& func, C&& obj, Args&&... args);
476481

477482
template<typename Fn, typename... Args>
478483
requires std::invocable<Fn&&, T, Args...>
479484
[[maybe_unused]] static constexpr auto for_each_n(int n, Fn&& func, Args&&... args);
480485

481486
template<typename Fn, typename C, typename... Args> // specialisation for member function with object
482487
requires std::invocable<Fn&&, C, T, Args...>
483-
[[maybe_unused]] static constexpr auto for_each_n(int n, Fn&& func, C *obj, Args&&... args);
488+
[[maybe_unused]] static constexpr auto for_each_n(int n, Fn&& func, C&& obj, Args&&... args);
484489
```
485490
Call 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`).
486491
Optionally 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
487492
being the maximum number to call. The second version of `for_each` and `for_each_n` is intended to be used
488-
when using a member function - the _second_ parameter passed by your call must be the `this` pointer of the object.
493+
when using a member function - the _second_ parameter passed by your call must be a pointer or reference to the object.
489494
If you wish to pass a `reference` parameter, you must wrap it in `std::ref`.
490495
491496
Works with lambdas, member functions, functions etc, compatible with `std::invoke`.
@@ -586,18 +591,18 @@ requires std::invocable<Fn&&, T, Args...>
586591

587592
template<std::size_t I, typename R, typename Fn, typename C, typename... Args> // specialisation for member function with not found value(nval) for return
588593
requires std::invocable<Fn&&, C, T, Args...>
589-
[[maybe_unused]] static constexpr R dispatch(T ev, R nval, const std::array<std::tuple<T, Fn>, I>& disp, C *obj, Args&&... args);
594+
[[maybe_unused]] static constexpr R dispatch(T ev, R nval, const std::array<std::tuple<T, Fn>, I>& disp, C&& obj, Args&&... args);
590595

591596
template<std::size_t I, typename Fn, typename... Args> // void func with not found call to last element
592597
requires (std::invocable<Fn&&, T, Args...> && I > 0)
593598
static constexpr void dispatch(T ev, const std::array<std::tuple<T, Fn>, I>& disp, Args&&... args);
594599

595600
template<std::size_t I, typename Fn, typename C, typename... Args> // specialisation for void member function with not found call to last element
596601
requires (std::invocable<Fn&&, C, T, Args...> && I > 0)
597-
static constexpr void dispatch(T ev, const std::array<std::tuple<T, Fn>, I>& disp, C *obj, Args&&... args);
602+
static constexpr void dispatch(T ev, const std::array<std::tuple<T, Fn>, I>& disp, C&& obj, Args&&... args);
598603
```
599-
With 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
600-
for different enum values.
604+
With a given enum, search and call user supplied invocable. So for example, if you want to demux a complex event predicated on an enum, you can easily declare predefined invocable actions
605+
for different enum values and then use `dispatch` to test and execute those actions.
601606
602607
- Where invocable returns a value, return this value or a user supplied "not found" value.
603608
- Where invocable is void, call user supplied invocable or "not found" invocable (last in supplied array).
@@ -611,7 +616,7 @@ There are two versions of `dispatch` - the first takes an enum value, a 'not fou
611616
The 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.
612617
This version is intended for use with `void` return invocables.
613618
614-
The 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.
619+
The 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 a pointer or reference to the object.
615620
You can also use `std::bind` to bind the this pointer and any parameter placeholders when declaring your array.
616621
If you wish to pass a `reference` parameter, you must wrap it in `std::ref`.
617622
@@ -620,7 +625,7 @@ If you wish to pass a `reference` parameter, you must wrap it in `std::ref`.
620625
621626
> [!IMPORTANT]
622627
> Your `std::array` of `std::tuple` should be sorted by enum.
623-
> The `dispatch` method performs a binary search on the array. Complexity for a sorted array is at most&ensp; $2log_2(N)+O(1)$ &ensp;comparisons.
628+
> The `dispatch` method performs a [binary search](https://en.cppreference.com/w/cpp/algorithm/binary_search) on the array. Complexity for a sorted array is at most&ensp; $2log_2(N)+O(1)$ &ensp;comparisons.
624629
> If the array is _not_ sorted, complexity is linear.
625630
626631
The 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
@@ -985,16 +990,16 @@ _output_
985990
```CSV
986991
0000001111
987992
```
988-
You can use the underlying type as well:
993+
You can use the underlying type in an initialiser_list as well:
989994
```c++
990-
enum_bitset<numbers> b(0,1,2,3);
995+
enum_bitset<numbers> b({0,1,2,3});
991996
std::cout << b << '\n';
992997
```
993998
_output_
994999
```CSV
9951000
0000001111
9961001
```
997-
You can use an `int` initialiser too:
1002+
You can use an `unsigned integer` initialiser too (interpreted as an underlying type):
9981003
```c++
9991004
enum_bitset<numbers> b(15);
10001005
std::cout << b << '\n';
@@ -1220,20 +1225,20 @@ requires std::invocable<Fn&&, T, Args...>
12201225
12211226
template<typename C, typename Fn, typename... Args> // specialisation for member function with object
12221227
requires std::invocable<Fn&&, C, T, Args...>
1223-
[[maybe_unused]] constexpr auto for_each(Fn&& func, C *obj, Args&&... args);
1228+
[[maybe_unused]] constexpr auto for_each(Fn&& func, C&& obj, Args&&... args);
12241229
12251230
template<typename Fn, typename... Args>
12261231
requires std::invocable<Fn&&, T, Args...>
12271232
[[maybe_unused]] constexpr auto for_each_n(int n, Fn&& func, Args&&... args);
12281233
12291234
template<typename C, typename Fn, typename... Args> // specialisation for member function with object
12301235
requires std::invocable<Fn&&, C, T, Args...>
1231-
[[maybe_unused]] constexpr auto for_each_n(int n, Fn&& func, C *obj, Args&&... args);
1236+
[[maybe_unused]] constexpr auto for_each_n(int n, Fn&& func, C&& obj, Args&&... args);
12321237
```
12331238
Call 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`).
12341239
Optionally provide any additional parameters. Works with lambdas, member functions, functions etc. You can limit the number of calls to your
12351240
invocable 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
1236-
when using a member function - the _second_ parameter passed by your call must be the `this` pointer of the object.
1241+
when using a member function - the _second_ parameter passed by your call must be a pointer or reference to the object.
12371242
If you wish to pass a `reference` parameter, you must wrap it in `std::ref`.
12381243
12391244
Returns `std::bind(std::forward<Fn>(func), std::placeholders::_1, std::forward<Args>(args)...)` or
@@ -1426,8 +1431,8 @@ static consteval const char* FIX8::conjure_type<T>::tpeek() [with T = test]
14261431
14271432
---
14281433
# 6. `fixed_string`
1429-
`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
1430-
creation of `constexpr` strings with specfic storage, adding a trailing `0`. It is used by `conjure_enum` to store all strings. API is described below.
1434+
`fixed_string` is a specialisation of `std::array` that provides static storage for an ASCII zero (asciiz) string. The purpose of this class is to allow the
1435+
creation of `constexpr` strings with specfic storage whilst adding a trailing `0`. It is used by `conjure_enum` to store all strings. API is described below.
14311436
14321437
## a) Creating a `fixed_string`
14331438
```c++
@@ -1505,22 +1510,34 @@ by default.
15051510
15061511
The package is also available on [vckpg](https://vcpkg.io/en/package/conjure-enum).
15071512
1508-
### ii. Default compiler warnings
1513+
### ii. Installing
1514+
To install the headers in your target environment, you can either specify the installation prefix when you first run cmake or set the
1515+
environment variable `CMAKE_INSTALL_PREFIX`. Omitting the prefix will install to the default target directory for your platform.
1516+
```bash
1517+
$ cmake -DCMAKE_INSTALL_PREFIX=<target directory> ..
1518+
```
1519+
1520+
Alternatively you can install after building using cmake:
1521+
```bash
1522+
$ cmake --install . --prefix <target directory>
1523+
```
1524+
1525+
### iii. Default compiler warnings
15091526
By default all warnings are enabled. To prevent this, pass the following to cmake:
15101527
```bash
15111528
$ cmake -DBUILD_ALL_WARNINGS=false ..
15121529
```
1513-
### iii. Default unit tests
1530+
### iv. Default unit tests
15141531
By default the unit tests are built (which will download Catch2). To prevent this, pass the following to cmake:
15151532
```bash
15161533
$ cmake -DBUILD_UNITTESTS=false ..
15171534
```
1518-
### iv. Default executable stripping
1535+
### v. Default executable stripping
15191536
To disable stripping of the executables:
15201537
```bash
15211538
$ cmake -DBUILD_STRIP_EXE=false ..
15221539
```
1523-
### v. Clang compilation profiling
1540+
### vi. Clang compilation profiling
15241541
To enable clang compilation profiling:
15251542
```bash
15261543
$ cmake -DBUILD_CLANG_PROFILER=true ..
@@ -1553,7 +1570,7 @@ message(STATUS "Downloading conjure_enum...")
15531570
include(FetchContent)
15541571
FetchContent_Declare(conjure_enum GIT_REPOSITORY https://github.com/fix8mt/conjure_enum.git)
15551572
FetchContent_MakeAvailable(conjure_enum)
1556-
target_include_directories(myproj PRIVATE ${conjure_enum_SOURCE_DIR}/include)
1573+
target_link_libraries(myproj PRIVATE conjure_enum)
15571574
```
15581575
15591576
## d) Reporting issues
@@ -1650,12 +1667,14 @@ The following are the default settings:
16501667
These 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.
16511668
> [!TIP]
16521669
> 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
1653-
> 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
1654-
> `conjure_enum` will **ignore enum values outside your range**.
1670+
> 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.
16551671
16561672
> [!TIP]
16571673
> If you wish to set ranges on a per enum basis, use `enum_range` (see below).
16581674
1675+
> [!WARNING]
1676+
> If the range is _too_ narrow `conjure_enum` will **ignore enum values outside your specified range**.
1677+
16591678
### ii. using `enum_range`
16601679
You can specialise this class to override the defaults and set your own range on a per enum basis.
16611680
```c++
@@ -2047,16 +2066,18 @@ From a compilation performance perspective, `conjure_enum` roughly matches the p
20472066
| Compiler | Version(s) | Notes | Unsupported |
20482067
| :--- | :--- | :--- | ---: |
20492068
| [gcc](https://gcc.gnu.org/projects/cxx-status.html) | `11`, `12`, `13`, `14`| `std::format` not complete in `11`, `12` | `<= 10` |
2050-
| [clang](https://clang.llvm.org/cxx_status.html) | `15`, `16`, `17`, `18`| Catch2 needs `cxx_std_20` in `15` | `<= 14` |
2051-
| [msvc](https://learn.microsoft.com/en-us/cpp/overview/visual-cpp-language-conformance) | `16`, `17` | Visual Studio 2019,2022, latest `17.11.3`| `<= 16.9`|
2052-
| [xcode](https://developer.apple.com/support/xcode/) | `15` | Apple Xcode Clang 15.0.0 (LLVM 16), some issues with `constexpr`, workarounds| `<= 14`|
2069+
| [clang](https://clang.llvm.org/cxx_status.html) | `15`, `16`, `17`, `18`, `19`, `20`| Catch2 needs `cxx_std_20` in `15` | `<= 14` |
2070+
| [msvc](https://learn.microsoft.com/en-us/cpp/overview/visual-cpp-language-conformance) | `16`, `17` | Visual Studio 2019,2022, latest `17.14.3`| `<= 16.9`|
2071+
| [xcode](https://developer.apple.com/support/xcode/) | `15`, `16` | Apple Xcode Clang `15.0.0`, `16.3.0` (LLVM `16`, `17`), some issues with `constexpr`, workarounds| `<= 14`|
20532072
20542073
# 11. Compiler issues
20552074
| Compiler | Version(s) | Issues | Workaround |
20562075
| :--- | :--- | :--- | ---: |
20572076
| clang | `16`, `17`, `18`| Compiler reports integers outside valid range [x,y]| specify underlying type when declaring enum eg. `enum class foo : int` |
2077+
| xcode clang | `17` | Compiler reports `constexpr evaluation hit maximum step limit; possible infinite loop?` error with large enum ranges | use compiler option `-fconstexpr-steps=4194304`|
2078+
| gcc | `12`, `13`, `14`| Compiler reports warnings with catch2 with `CATCH2_INTERNAL_TEST_*` `is partly outside array bounds of ‘CATCH2_INTERNAL_TEST` warnings | can be ignored |
20582079
2059-
[^1]: &copy; 2024 Fix8 Market Technologies Pty Ltd, David L. Dight.
2080+
[^1]: &copy; 2024-25 Fix8 Market Technologies Pty Ltd, David L. Dight.
20602081
Logo by [Adrian An](mailto:adrian.an[at]mac.com).
20612082
[^2]: &copy; 2019 - 2024 Daniil Goncharov
20622083

0 commit comments

Comments
 (0)