Skip to content

Commit 5185bf4

Browse files
committed
reflection-based backend prototype
1 parent 591b643 commit 5185bf4

28 files changed

Lines changed: 1651 additions & 719 deletions

.github/workflows/ubuntu.yml

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,28 @@ jobs:
1010
fail-fast: false
1111
matrix:
1212
compiler:
13-
- { cc: "gcc-11", cxx: "g++-11", os: "ubuntu-22.04", nonascii: "TRUE", modules: "FALSE", import_std: "FALSE" }
14-
- { cc: "gcc-11", cxx: "g++-11", os: "ubuntu-22.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" }
15-
- { cc: "gcc-12", cxx: "g++-12", os: "ubuntu-22.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" }
16-
- { cc: "gcc-13", cxx: "g++-13", os: "ubuntu-24.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" }
17-
- { cc: "gcc-14", cxx: "g++-14", os: "ubuntu-24.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" }
18-
- { cc: "gcc-15", cxx: "g++-15", os: "ubuntu-24.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" }
19-
- { cc: "gcc-16", cxx: "g++-16", os: "ubuntu-26.04", nonascii: "FALSE", modules: "TRUE", import_std: "TRUE" }
20-
- { cc: "clang-13", cxx: "clang++-13", os: "ubuntu-22.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" }
21-
- { cc: "clang-14", cxx: "clang++-14", os: "ubuntu-22.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" }
22-
- { cc: "clang-15", cxx: "clang++-15", os: "ubuntu-22.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" }
23-
- { cc: "clang-16", cxx: "clang++-16", os: "ubuntu-22.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" }
24-
- { cc: "clang-17", cxx: "clang++-17", os: "ubuntu-24.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" }
25-
- { cc: "clang-18", cxx: "clang++-18", os: "ubuntu-24.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" }
26-
- { cc: "clang-19", cxx: "clang++-19", os: "ubuntu-24.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" }
27-
- { cc: "clang-19", cxx: "clang++-19", os: "ubuntu-24.04", nonascii: "FALSE", modules: "TRUE", import_std: "TRUE" }
28-
- { cc: "clang-20", cxx: "clang++-20", os: "ubuntu-24.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" }
29-
- { cc: "clang-20", cxx: "clang++-20", os: "ubuntu-24.04", nonascii: "FALSE", modules: "TRUE", import_std: "FALSE" }
30-
- { cc: "clang-21", cxx: "clang++-21", os: "ubuntu-24.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" }
31-
- { cc: "clang-21", cxx: "clang++-21", os: "ubuntu-24.04", nonascii: "FALSE", modules: "TRUE", import_std: "FALSE" }
32-
- { cc: "clang-22", cxx: "clang++-22", os: "ubuntu-24.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" }
33-
- { cc: "clang-22", cxx: "clang++-22", os: "ubuntu-24.04", nonascii: "FALSE", modules: "TRUE", import_std: "FALSE" }
34-
35-
name: "${{ format('{0} NONASCII={1} MODULES={2} IMPORT_STD={3}', matrix.compiler.cc, matrix.compiler.nonascii, matrix.compiler.modules, matrix.compiler.import_std) }}"
13+
- { cc: "gcc-11", cxx: "g++-11", os: "ubuntu-22.04", modules: "FALSE", import_std: "FALSE" }
14+
- { cc: "gcc-12", cxx: "g++-12", os: "ubuntu-22.04", modules: "FALSE", import_std: "FALSE" }
15+
- { cc: "gcc-13", cxx: "g++-13", os: "ubuntu-24.04", modules: "FALSE", import_std: "FALSE" }
16+
- { cc: "gcc-14", cxx: "g++-14", os: "ubuntu-24.04", modules: "FALSE", import_std: "FALSE" }
17+
- { cc: "gcc-15", cxx: "g++-15", os: "ubuntu-24.04", modules: "FALSE", import_std: "FALSE" }
18+
- { cc: "gcc-16", cxx: "g++-16", os: "ubuntu-26.04", modules: "TRUE", import_std: "TRUE" }
19+
- { cc: "clang-13", cxx: "clang++-13", os: "ubuntu-22.04", modules: "FALSE", import_std: "FALSE" }
20+
- { cc: "clang-14", cxx: "clang++-14", os: "ubuntu-22.04", modules: "FALSE", import_std: "FALSE" }
21+
- { cc: "clang-15", cxx: "clang++-15", os: "ubuntu-22.04", modules: "FALSE", import_std: "FALSE" }
22+
- { cc: "clang-16", cxx: "clang++-16", os: "ubuntu-22.04", modules: "FALSE", import_std: "FALSE" }
23+
- { cc: "clang-17", cxx: "clang++-17", os: "ubuntu-24.04", modules: "FALSE", import_std: "FALSE" }
24+
- { cc: "clang-18", cxx: "clang++-18", os: "ubuntu-24.04", modules: "FALSE", import_std: "FALSE" }
25+
- { cc: "clang-19", cxx: "clang++-19", os: "ubuntu-24.04", modules: "FALSE", import_std: "FALSE" }
26+
- { cc: "clang-19", cxx: "clang++-19", os: "ubuntu-24.04", modules: "TRUE", import_std: "TRUE" }
27+
- { cc: "clang-20", cxx: "clang++-20", os: "ubuntu-24.04", modules: "FALSE", import_std: "FALSE" }
28+
- { cc: "clang-20", cxx: "clang++-20", os: "ubuntu-24.04", modules: "TRUE", import_std: "FALSE" }
29+
- { cc: "clang-21", cxx: "clang++-21", os: "ubuntu-24.04", modules: "FALSE", import_std: "FALSE" }
30+
- { cc: "clang-21", cxx: "clang++-21", os: "ubuntu-24.04", modules: "TRUE", import_std: "FALSE" }
31+
- { cc: "clang-22", cxx: "clang++-22", os: "ubuntu-24.04", modules: "FALSE", import_std: "FALSE" }
32+
- { cc: "clang-22", cxx: "clang++-22", os: "ubuntu-24.04", modules: "TRUE", import_std: "FALSE" }
33+
34+
name: "${{ format('{0} MODULES={1} IMPORT_STD={2}', matrix.compiler.cc, matrix.compiler.modules, matrix.compiler.import_std) }}"
3635
runs-on: ${{ matrix.compiler.os }}
3736
env:
3837
CC: ${{ matrix.compiler.cc }}
@@ -72,6 +71,14 @@ jobs:
7271
sudo apt update
7372
sudo apt install -y ${{ matrix.compiler.cxx }}
7473
74+
- name: Test Unicode identifiers
75+
if: ${{ matrix.compiler.cc == 'gcc-16' }}
76+
run: |
77+
"${CXX}" -std=c++17 -Wall -Wextra -Wshadow -pedantic-errors -Werror -Iinclude test/test_nonascii.cpp -o test_nonascii
78+
./test_nonascii
79+
"${CXX}" -std=c++26 -freflection -Wall -Wextra -Wshadow -pedantic-errors -Werror -Iinclude test/test_nonascii.cpp -o test_nonascii_reflection
80+
./test_nonascii_reflection
81+
7582
- name: Install pinned CMake
7683
if: ${{ matrix.compiler.modules == 'TRUE' }}
7784
run: |
@@ -83,11 +90,15 @@ jobs:
8390
8491
- name: Configure Release
8592
run: |
93+
standard_options=()
94+
if [[ "${{ matrix.compiler.cc }}" == "gcc-16" && "${{ matrix.compiler.modules }}" == "TRUE" ]]; then
95+
standard_options=(-DCMAKE_CXX_STANDARD=26 -DCMAKE_CXX_STANDARD_REQUIRED=ON)
96+
fi
8697
cmake -S . -B build-release \
8798
-DCMAKE_BUILD_TYPE=Release \
88-
-DMAGIC_ENUM_OPT_ENABLE_NONASCII:BOOL=${{ matrix.compiler.nonascii }} \
8999
-DMAGIC_ENUM_USE_MODULES:BOOL=${{ matrix.compiler.modules }} \
90-
${{ matrix.compiler.modules == 'TRUE' && '-G Ninja' || '' }}
100+
${{ matrix.compiler.modules == 'TRUE' && '-G Ninja' || '' }} \
101+
"${standard_options[@]}"
91102
92103
- name: Build Release
93104
run: cmake --build build-release --parallel --config Release
@@ -97,11 +108,15 @@ jobs:
97108

98109
- name: Configure Debug
99110
run: |
111+
standard_options=()
112+
if [[ "${{ matrix.compiler.cc }}" == "gcc-16" && "${{ matrix.compiler.modules }}" == "TRUE" ]]; then
113+
standard_options=(-DCMAKE_CXX_STANDARD=26 -DCMAKE_CXX_STANDARD_REQUIRED=ON)
114+
fi
100115
cmake -S . -B build-debug \
101116
-DCMAKE_BUILD_TYPE=Debug \
102-
-DMAGIC_ENUM_OPT_ENABLE_NONASCII:BOOL=${{ matrix.compiler.nonascii }} \
103117
-DMAGIC_ENUM_USE_MODULES:BOOL=${{ matrix.compiler.modules }} \
104-
${{ matrix.compiler.modules == 'TRUE' && '-G Ninja' || '' }}
118+
${{ matrix.compiler.modules == 'TRUE' && '-G Ninja' || '' }} \
119+
"${standard_options[@]}"
105120
106121
- name: Build Debug
107122
run: cmake --build build-debug --parallel --config Debug

CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ endif()
5050
option(MAGIC_ENUM_OPT_TEST_INSTALLED_VERSION "Test against an installed magic_enum CMake package" OFF)
5151
option(MAGIC_ENUM_OPT_TEST_INSTALLED_VERSION_PKGCONFIG "Test against an installed magic_enum pkg-config package" OFF)
5252

53+
if(MAGIC_ENUM_OPT_TEST_INSTALLED_VERSION AND MAGIC_ENUM_OPT_TEST_INSTALLED_VERSION_PKGCONFIG)
54+
message(FATAL_ERROR "MAGIC_ENUM_OPT_TEST_INSTALLED_VERSION and MAGIC_ENUM_OPT_TEST_INSTALLED_VERSION_PKGCONFIG are mutually exclusive")
55+
endif()
56+
if(MAGIC_ENUM_OPT_TEST_INSTALLED_VERSION_PKGCONFIG AND MAGIC_ENUM_USE_MODULES)
57+
message(FATAL_ERROR "MAGIC_ENUM_OPT_TEST_INSTALLED_VERSION_PKGCONFIG does not support MAGIC_ENUM_USE_MODULES")
58+
endif()
59+
5360
set(MAGIC_ENUM_MODULE_CXX_STANDARD 20)
5461
if(DEFINED CMAKE_CXX_STANDARD AND
5562
CMAKE_CXX_STANDARD GREATER MAGIC_ENUM_MODULE_CXX_STANDARD)

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,10 @@ Header-only C++17 library provides static reflection for enums, work with any en
167167
magic_enum::enum_contains<Color>("fda"); // -> false
168168
```
169169
170-
* Check whether value is in reflection range
170+
* Check whether value can be reflected
171171
172172
```cpp
173-
magic_enum::enum_reflected<Color>(123); // -> true
174-
magic_enum::enum_contains<Color>(123); // -> false
175-
magic_enum::enum_reflected<Color>(128); // -> false
173+
magic_enum::enum_reflected(Color::GREEN); // -> true
176174
```
177175

178176
* Enum index in sequence
@@ -372,4 +370,6 @@ Header-only C++17 library provides static reflection for enums, work with any en
372370
* Xcode >= 10
373371
* GCC >= 9
374372
373+
C++26 reflection is selected automatically when available; see [limitations](doc/limitations.md#c26-standard-reflection).
374+
375375
## Licensed under the [MIT License](LICENSE)

doc/limitations.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# Limitations
22

3-
* This library uses a compiler-specific hack based on `__PRETTY_FUNCTION__` / `__FUNCSIG__`.
3+
* Without standard reflection, this library uses a compiler-specific hack based on `__PRETTY_FUNCTION__` / `__FUNCSIG__`.
4+
5+
## C++26 Standard Reflection
6+
7+
Standard reflection is selected automatically when available. Otherwise the compiler-specific implementation is used. Keep reflection settings consistent across all translation units and module BMIs.
8+
9+
* `customize::enum_name(E)` cannot add undeclared values.
10+
11+
Define `MAGIC_ENUM_FORCE_COMPILER_SPECIFIC_REFLECTION` before including `magic_enum.hpp` to force the compiler-specific implementation and its `enum_range` scan.
412

513
* Use `MAGIC_ENUM_SUPPORTED` or `magic_enum::is_magic_enum_supported` to check compiler support. Unsupported compilers cause compilation errors unless `MAGIC_ENUM_NO_CHECK_SUPPORT` is defined.
614

@@ -17,13 +25,13 @@
1725
};
1826
```
1927
20-
* `MAGIC_ENUM_RANGE_MIN` / `MAGIC_ENUM_RANGE_MAX` do not control flag reflection. Flag reflection scans bit positions available in `E`'s underlying type.
28+
* `MAGIC_ENUM_RANGE_MIN` / `MAGIC_ENUM_RANGE_MAX` do not control flag reflection.
2129
2230
* Zero is not reflected for flag enums.
2331
2432
## Enum Range
2533
26-
* For non-flag enums, range-based reflection only considers values in `[MAGIC_ENUM_RANGE_MIN, MAGIC_ENUM_RANGE_MAX]`.
34+
* `MAGIC_ENUM_RANGE_MIN` / `MAGIC_ENUM_RANGE_MAX` limit only compiler-specific reflection; standard reflection ignores them.
2735
2836
* By default, `MAGIC_ENUM_RANGE_MIN = -128`, `MAGIC_ENUM_RANGE_MAX = 127`.
2937
@@ -68,7 +76,7 @@
6876

6977
## Aliasing
7078

71-
magic_enum [cannot reliably distinguish aliased enumerators](https://github.com/Neargye/magic_enum/issues/68). Its behavior with aliases is compiler-dependent.
79+
Aliased enumerators share one reflected name. Compiler-specific reflection [depends on the compiler](https://github.com/Neargye/magic_enum/issues/68); standard reflection uses the first declaration.
7280

7381
```cpp
7482
enum ShapeKind {

doc/reference.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
* [`customize::enum_range` customizes enum reflection.](#customizeenum_range)
1212
* [`enum_index` returns index from enum value.](#enum_index)
1313
* [`enum_contains` checks whether enum contains value or name.](#enum_contains)
14-
* [`enum_reflected` checks whether enum value is in reflection range.](#enum_reflected)
14+
* [`enum_reflected` checks whether enum value can be reflected.](#enum_reflected)
1515
* [`enum_type_name` returns enum type name.](#enum_type_name)
1616
* [`enum_fuse` combines enum values for switch/case statements.](#enum_fuse)
1717
* [`enum_switch` transforms runtime enum value into constexpr constant.](#enum_switch)
@@ -37,6 +37,8 @@
3737

3838
* Use `MAGIC_ENUM_SUPPORTED` or `magic_enum::is_magic_enum_supported` to check compiler support. Unsupported compilers cause compilation errors unless `MAGIC_ENUM_NO_CHECK_SUPPORT` is defined.
3939

40+
* C++26 reflection is selected automatically when available. See [limitations](limitations.md#c26-standard-reflection).
41+
4042
* `Enum<T>` constrains C++17 function parameters to enum types.
4143

4244
* To add custom enum or type names, see [example](../example/example_custom_name.cpp).
@@ -52,6 +54,8 @@
5254
#include <magic_enum/magic_enum.hpp>
5355
```
5456
57+
The string aliases must have matching `value_type`s. `MAGIC_ENUM_USING_ALIAS_STRING` must name a default- and move-constructible owning string supporting `empty()`, `size()`, `data()`, `reserve()`, `append(count, character)`, and `append(data, size)`; return values are ignored.
58+
5559
* To keep configuration in separate header, define `MAGIC_ENUM_CONFIG_FILE`:
5660
5761
```cpp
@@ -61,6 +65,8 @@
6165

6266
Configuration header can contain these aliases and range macros.
6367

68+
* Define all configuration macros consistently before the first magic_enum include in every translation unit and module BMI.
69+
6470
## `enum_cast`
6571

6672
```cpp
@@ -295,7 +301,7 @@ struct enum_range {
295301
296302
* `prefix_length` sets number of characters removed from start of each reflected enumerator name. If omitted, defaults to `0`.
297303
298-
* `min` and `max` are optional for non-flag enums and default to `MAGIC_ENUM_RANGE_MIN` / `MAGIC_ENUM_RANGE_MAX`. They are ignored for enum flags.
304+
* `min` and `max` set compiler-specific scan bounds for non-flag enums. Standard reflection and enum flags ignore them.
299305
300306
* `as_flags<>` and `as_common<>` force subtype for individual API calls without changing `enum_range`.
301307
@@ -340,6 +346,8 @@ struct enum_range {
340346
}
341347
```
342348
349+
If both an explicit `customize::enum_range<E>` specialization and an ADL `magic_enum_define_range_adl(E)` function are present, the explicit specialization is used and the ADL customization is ignored.
350+
343351
For flag enums, add `.flag<true>()`; `.minmax<...>()` is ignored.
344352
345353
## `enum_index`
@@ -411,9 +419,11 @@ constexpr bool enum_reflected(underlying_type_t<E> value) noexcept;
411419
412420
* Defined in header `<magic_enum/magic_enum.hpp>`
413421
414-
* Returns `true` if enum value is in reflection range.
422+
* With standard reflection, returns `true` for a declared enumerator.
423+
424+
* With compiler-specific reflection, returns `true` for a value in the configured range.
415425
416-
* For enum flags, returns `true` only when value is non-zero, single-bit, and its bit position can be reflected.
426+
* For flags, the value must be non-zero and single-bit.
417427
418428
## `enum_type_name`
419429
@@ -593,7 +603,7 @@ constexpr bool enum_flags_test_any(E lhs, E rhs) noexcept;
593603
};
594604
```
595605

596-
* `MAGIC_ENUM_RANGE_MIN` / `MAGIC_ENUM_RANGE_MAX` do not control flag reflection. Flag reflection scans bit positions available in `E`'s underlying type.
606+
* `MAGIC_ENUM_RANGE_MIN` / `MAGIC_ENUM_RANGE_MAX` do not control flag reflection.
597607

598608
* Zero is not reflected for flag enums.
599609

example/CMakeLists.txt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,6 @@ set(MAGIC_ENUM_EXAMPLES
2828
example_custom_name
2929
example_switch
3030
)
31-
if(MAGIC_ENUM_OPT_ENABLE_NONASCII)
32-
list(APPEND MAGIC_ENUM_EXAMPLES example_nonascii_name)
33-
endif()
3431

3532
foreach(MAGIC_ENUM_EXAMPLE IN LISTS MAGIC_ENUM_EXAMPLES)
3633
magic_enum_add_example(${MAGIC_ENUM_EXAMPLE} magic_enum::magic_enum 17)

example/example_nonascii_name.cpp

Lines changed: 0 additions & 45 deletions
This file was deleted.

0 commit comments

Comments
 (0)