Skip to content

Commit 750e67d

Browse files
Add enchantum as an enum reflection backend (#441)
--------- Co-authored-by: Dr. Patrick Urbanke <[email protected]>
1 parent 3b6a3bb commit 750e67d

36 files changed

+1950
-432
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,3 +584,5 @@ reflect-cpp is released under the MIT License. Refer to the LICENSE file for det
584584
reflect-cpp includes [YYJSON](https://github.com/ibireme/yyjson), the fastest JSON library currently in existence. YYJSON is written by YaoYuan and also released under the MIT License.
585585

586586
reflect-cpp includes [compile-time-regular-expressions](https://github.com/hanickadot/compile-time-regular-expressions). CTRE is written by Hana Dusíková and released under the Apache-2.0 License with LLVM exceptions.
587+
588+
reflect-cpp includes [enchantum](https://github.com/ZXShady/enchantum/tree/main). enchantum is written by ZXShady and also released under the MIT License.

docs/enums.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Enums
22

3-
reflect-cpp supports scoped enumerations. They can either come in the form of normal enumerations or flag enums.
3+
reflect-cpp supports both scoped and unscoped enumerations as long as they are named. They can either come in the form of normal enumerations or flag enums.
44

55
## Normal enumerations
66

@@ -27,21 +27,24 @@ This results in the following JSON string:
2727

2828
However, some limitations apply:
2929

30-
1. They must be scoped enumerations.
30+
1. They cannot be unnamed enumerations.
3131

3232
```cpp
33-
/// OK - scoped enumeration
33+
// OK - scoped enumeration
3434
enum class Color1 { red, green, blue, yellow };
3535

36-
/// OK - scoped enumeration
36+
// OK - scoped enumeration
3737
enum struct Color2 { red, green, blue, yellow };
3838

39-
/// unsupported - unscoped enumerations
39+
// OK - unscoped enumeration
4040
enum Color3 { red, green, blue, yellow };
41+
42+
/// Unsupported: Anonymous enumeration
43+
enum { red, green, blue, yellow };
4144
```
4245

4346
2. Enum values must be in the range `[RFL_ENUM_RANGE_MIN, RFL_ENUM_RANGE_MAX]`. If the range is not specified, the
44-
default range is `[0, 127]`.
47+
default range is `[-256, 256]`.
4548

4649
- You can specify a custom range for the all enum values by defining `RFL_ENUM_RANGE_MIN` and `RFL_ENUM_RANGE_MAX`
4750
before including the reflect-cpp header:

include/rfl/config.hpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#ifndef RFL_CONFIG_HPP_
2+
#define RFL_CONFIG_HPP_
3+
4+
namespace rfl::config {
5+
6+
// To specify a different range for a particular enum type, specialize the
7+
// enum_range template for that enum type.
8+
template <typename T>
9+
struct enum_range {
10+
// In your template specialization, uncomment these two lines and replace them
11+
// with the values of your choice.
12+
// static constexpr int min = ...;
13+
// static constexpr int max = ...;
14+
};
15+
16+
} // namespace rfl::config
17+
18+
#endif
19+

include/rfl/enums.hpp

Lines changed: 76 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,69 +4,113 @@
44
#include <string>
55

66
#include "Result.hpp"
7-
#include "internal/enums/StringConverter.hpp"
87
#include "internal/enums/get_enum_names.hpp"
9-
#include "internal/enums/is_flag_enum.hpp"
10-
#include "internal/enums/is_scoped_enum.hpp"
8+
#include "internal/strings/strings.hpp"
9+
#include "thirdparty/enchantum/enchantum.hpp"
10+
#include "thirdparty/enchantum/bitflags.hpp"
1111

1212
namespace rfl {
1313

14-
// Converts an enum value to a string.
15-
template <internal::enums::is_scoped_enum EnumType>
16-
std::string enum_to_string(EnumType _enum) {
17-
return rfl::internal::enums::StringConverter<EnumType>::enum_to_string(_enum);
14+
template <enchantum::Enum EnumType>
15+
std::string enum_to_string(const EnumType _enum) {
16+
const auto to_string_or_number = [](const EnumType e) {
17+
const auto s = enchantum::to_string(e);
18+
return s.empty() ? std::to_string(
19+
static_cast<std::underlying_type_t<EnumType>>(e))
20+
: std::string(s);
21+
};
22+
23+
if constexpr (enchantum::is_bitflag<EnumType>) {
24+
// Iterates through the enum bit by bit and matches it against the flags.
25+
using T = std::underlying_type_t<EnumType>;
26+
auto val = static_cast<T>(_enum);
27+
int i = 0;
28+
std::vector<std::string> flags;
29+
while (val != 0) {
30+
const auto bit = val & static_cast<T>(1);
31+
if (bit == 1) {
32+
auto str =
33+
to_string_or_number(static_cast<EnumType>(static_cast<T>(1) << i));
34+
flags.emplace_back(std::move(str));
35+
}
36+
++i;
37+
val >>= 1;
38+
}
39+
return internal::strings::join("|", flags);
40+
} else {
41+
return to_string_or_number(_enum);
42+
}
1843
}
1944

2045
// Converts a string to a value of the given enum type.
21-
template <internal::enums::is_scoped_enum EnumType>
22-
rfl::Result<EnumType> string_to_enum(const std::string& _str) {
23-
return rfl::internal::enums::StringConverter<EnumType>::string_to_enum(_str);
46+
template <enchantum::Enum EnumType>
47+
Result<EnumType> string_to_enum(const std::string& _str) {
48+
const auto cast_numbers_or_names =
49+
[](const std::string& name) -> Result<EnumType> {
50+
const auto r = enchantum::cast<EnumType>(name);
51+
if (r) return *r;
52+
try {
53+
return static_cast<EnumType>(std::stoi(name));
54+
} catch (std::exception& exp) {
55+
return error(exp.what());
56+
}
57+
};
58+
59+
if constexpr (enchantum::is_bitflag<EnumType>) {
60+
using T = std::underlying_type_t<EnumType>;
61+
const auto split = internal::strings::split(_str, "|");
62+
auto res = static_cast<T>(0);
63+
for (const auto& s : split) {
64+
const auto r = cast_numbers_or_names(s);
65+
if (r) {
66+
res |= static_cast<T>(*r);
67+
} else {
68+
return r;
69+
}
70+
}
71+
return static_cast<EnumType>(res);
72+
} else {
73+
return cast_numbers_or_names(_str);
74+
}
2475
}
2576

2677
// Returns a named tuple mapping names of enumerators of the given enum type to
2778
// their values.
28-
template <internal::enums::is_scoped_enum EnumType>
79+
template <enchantum::Enum EnumType>
2980
auto get_enumerators() {
30-
constexpr auto names = internal::enums::get_enum_names<
31-
EnumType, internal::enums::is_flag_enum<EnumType>>();
32-
return internal::enums::names_to_enumerator_named_tuple(names);
81+
return internal::enums::names_to_enumerator_named_tuple(
82+
internal::enums::get_enum_names<EnumType>());
3383
}
3484

3585
// Returns a named tuple mapping names of enumerators of the given enum type to
3686
// their underlying values.
37-
template <internal::enums::is_scoped_enum EnumType>
87+
template <enchantum::Enum EnumType>
3888
auto get_underlying_enumerators() {
39-
constexpr auto names = internal::enums::get_enum_names<
40-
EnumType, internal::enums::is_flag_enum<EnumType>>();
41-
return internal::enums::names_to_underlying_enumerator_named_tuple(names);
89+
return internal::enums::names_to_underlying_enumerator_named_tuple(
90+
internal::enums::get_enum_names<EnumType>());
4291
}
4392

4493
// Returns an std::array containing pairs of enumerator names (as
4594
// std::string_view) and values.
46-
template <internal::enums::is_scoped_enum EnumType>
95+
template <enchantum::Enum EnumType>
4796
constexpr auto get_enumerator_array() {
48-
constexpr auto names = internal::enums::get_enum_names<
49-
EnumType, internal::enums::is_flag_enum<EnumType>>();
50-
return internal::enums::names_to_enumerator_array(names);
97+
return internal::enums::names_to_enumerator_array(
98+
internal::enums::get_enum_names<EnumType>());
5199
}
52100

53101
// Returns an std::array containing pairs of enumerator names (as
54102
// std::string_view) and underlying values.
55-
template <internal::enums::is_scoped_enum EnumType>
103+
template <enchantum::Enum EnumType>
56104
constexpr auto get_underlying_enumerator_array() {
57-
constexpr auto names = internal::enums::get_enum_names<
58-
EnumType, internal::enums::is_flag_enum<EnumType>>();
59-
return internal::enums::names_to_underlying_enumerator_array(names);
105+
return internal::enums::names_to_underlying_enumerator_array(
106+
internal::enums::get_enum_names<EnumType>());
60107
}
61108

62109
// Returns the range of the given enum type as a pair of the minimum and maximum
63-
template <internal::enums::is_scoped_enum EnumType>
110+
template <enchantum::Enum EnumType>
64111
constexpr auto get_enum_range() {
65-
return std::make_pair(
66-
internal::enums::get_range_min<EnumType,
67-
internal::enums::is_flag_enum<EnumType>>(),
68-
internal::enums::get_range_max<
69-
EnumType, internal::enums::is_flag_enum<EnumType>>());
112+
return std::make_pair(enchantum::enum_traits<EnumType>::min,
113+
enchantum::enum_traits<EnumType>::max);
70114
}
71115

72116
} // namespace rfl

include/rfl/internal/enums/Names.hpp

Lines changed: 8 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -9,94 +9,27 @@
99
#include <utility>
1010

1111
#include "../../Literal.hpp"
12+
#include "../../config.hpp"
1213
#include "../../define_literal.hpp"
1314
#include "../../make_named_tuple.hpp"
15+
#include "../../thirdparty/enchantum/enchantum.hpp"
1416
#include "../StringLiteral.hpp"
1517

16-
namespace rfl {
17-
18-
// Enum values must be greater than or equal to RFL_ENUM_RANGE_MIN.
19-
// By default, RFL_ENUM_RANGE_MIN is set to 0.
20-
// To change the default minimum range for all enum types, redefine the macro
21-
// RFL_ENUM_RANGE_MIN.
22-
#if !defined(RFL_ENUM_RANGE_MIN)
23-
#define RFL_ENUM_RANGE_MIN 0
24-
#endif
25-
26-
// Enum values must be less than or equal to RFL_ENUM_RANGE_MAX.
27-
// By default, RFL_ENUM_RANGE_MAX is set to 127.
28-
// To change the default maximum range for all enum types, redefine the macro
29-
// RFL_ENUM_RANGE_MAX.
30-
#if !defined(RFL_ENUM_RANGE_MAX)
31-
#define RFL_ENUM_RANGE_MAX 127
32-
#endif
33-
34-
namespace config {
35-
36-
// To specify a different range for a particular enum type, specialize the
37-
// enum_range template for that enum type.
38-
template <typename T>
39-
struct enum_range {
40-
static constexpr int min = RFL_ENUM_RANGE_MIN;
41-
static constexpr int max = RFL_ENUM_RANGE_MAX;
42-
};
43-
} // namespace config
44-
45-
namespace internal {
46-
namespace enums {
47-
48-
static_assert(RFL_ENUM_RANGE_MAX > RFL_ENUM_RANGE_MIN,
49-
"RFL_ENUM_RANGE_MAX must be greater than RFL_ENUM_RANGE_MIN.");
50-
51-
template <typename T, typename = void>
52-
struct range_min : std::integral_constant<int, RFL_ENUM_RANGE_MIN> {};
53-
54-
template <typename T>
55-
struct range_min<T, std::void_t<decltype(config::enum_range<T>::min)>>
56-
: std::integral_constant<decltype(config::enum_range<T>::min),
57-
config::enum_range<T>::min> {};
58-
59-
template <typename T, typename = void>
60-
struct range_max : std::integral_constant<int, RFL_ENUM_RANGE_MAX> {};
61-
62-
template <typename T>
63-
struct range_max<T, std::void_t<decltype(config::enum_range<T>::max)>>
64-
: std::integral_constant<decltype(config::enum_range<T>::max),
65-
config::enum_range<T>::max> {};
18+
namespace rfl::internal::enums {
6619

6720
template <class EnumType, class LiteralType, size_t N, bool _is_flag,
68-
auto... _enums>struct Names {
21+
auto... _enums>
22+
struct Names {
6923
/// Contains a collection of enums as compile-time strings.
7024
using Literal = LiteralType;
7125

7226
/// The number of possible values
7327
constexpr static size_t size = N;
7428

7529
/// A list of all the possible enums
76-
constexpr static std::array<EnumType, N> enums_ =
77-
std::array<EnumType, N>{_enums...};
78-
79-
static_assert(N == 0 || LiteralType::size() == N,
80-
"Size of literal and enum do not match.");
81-
82-
template <class NewLiteral, auto _new_enum>
83-
using AddOneType = std::conditional_t<
84-
N == 0, Names<EnumType, NewLiteral, 1, _is_flag, _new_enum>,
85-
Names<EnumType, define_literal_t<LiteralType, NewLiteral>, N + 1,
86-
_is_flag, _enums..., _new_enum>>;
30+
constexpr static auto enums_ = std::array<EnumType, N>{_enums...};
8731
};
8832

89-
90-
template <class EnumType, class LiteralType1, size_t N1, bool _is_flag1,
91-
auto... _enums1, class LiteralType2, size_t N2, auto... _enums2>
92-
consteval auto operator|(
93-
Names<EnumType, LiteralType1, N1, _is_flag1, _enums1...>,
94-
Names<EnumType, LiteralType2, N2, _is_flag1, _enums2...>) {
95-
using CombinedLiteral = define_literal_t<LiteralType1, LiteralType2>;
96-
return Names<EnumType, CombinedLiteral, N1 + N2, _is_flag1,
97-
_enums1..., _enums2...>{};
98-
}
99-
10033
template <class EnumType, size_t N, bool _is_flag, StringLiteral... _names,
10134
auto... _enums>
10235
auto names_to_enumerator_named_tuple(
@@ -132,8 +65,7 @@ names_to_underlying_enumerator_array(
13265
static_cast<std::underlying_type_t<EnumType>>(_enums))...};
13366
}
13467

135-
} // namespace enums
136-
} // namespace internal
137-
} // namespace rfl
68+
} // namespace rfl::internal::enums
13869

13970
#endif
71+

0 commit comments

Comments
 (0)