Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -584,3 +584,5 @@ reflect-cpp is released under the MIT License. Refer to the LICENSE file for det
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.

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.

reflect-cpp includes [enchantum](https://github.com/ZXShady/enchantum/tree/main). enchantum is written by ZXShady and also released under the MIT License.
15 changes: 9 additions & 6 deletions docs/enums.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Enums

reflect-cpp supports scoped enumerations. They can either come in the form of normal enumerations or flag enums.
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.

## Normal enumerations

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

However, some limitations apply:

1. They must be scoped enumerations.
1. They cannot be unnamed enumerations.

```cpp
/// OK - scoped enumeration
// OK - scoped enumeration
enum class Color1 { red, green, blue, yellow };

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

/// unsupported - unscoped enumerations
// OK - unscoped enumeration
enum Color3 { red, green, blue, yellow };

/// Unsupported: Anonymous enumeration
enum { red, green, blue, yellow };
```

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

- You can specify a custom range for the all enum values by defining `RFL_ENUM_RANGE_MIN` and `RFL_ENUM_RANGE_MAX`
before including the reflect-cpp header:
Expand Down
19 changes: 19 additions & 0 deletions include/rfl/config.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#ifndef RFL_CONFIG_HPP_
#define RFL_CONFIG_HPP_

namespace rfl::config {

// To specify a different range for a particular enum type, specialize the
// enum_range template for that enum type.
template <typename T>
struct enum_range {
// In your template specialization, uncomment these two lines and replace them
// with the values of your choice.
// static constexpr int min = ...;
// static constexpr int max = ...;
};

} // namespace rfl::config

#endif

108 changes: 76 additions & 32 deletions include/rfl/enums.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,69 +4,113 @@
#include <string>

#include "Result.hpp"
#include "internal/enums/StringConverter.hpp"
#include "internal/enums/get_enum_names.hpp"
#include "internal/enums/is_flag_enum.hpp"
#include "internal/enums/is_scoped_enum.hpp"
#include "internal/strings/strings.hpp"
#include "thirdparty/enchantum/enchantum.hpp"
#include "thirdparty/enchantum/bitflags.hpp"

namespace rfl {

// Converts an enum value to a string.
template <internal::enums::is_scoped_enum EnumType>
std::string enum_to_string(EnumType _enum) {
return rfl::internal::enums::StringConverter<EnumType>::enum_to_string(_enum);
template <enchantum::Enum EnumType>
std::string enum_to_string(const EnumType _enum) {
const auto to_string_or_number = [](const EnumType e) {
const auto s = enchantum::to_string(e);
return s.empty() ? std::to_string(
static_cast<std::underlying_type_t<EnumType>>(e))
: std::string(s);
};

if constexpr (enchantum::is_bitflag<EnumType>) {
// Iterates through the enum bit by bit and matches it against the flags.
using T = std::underlying_type_t<EnumType>;
auto val = static_cast<T>(_enum);
int i = 0;
std::vector<std::string> flags;
while (val != 0) {
const auto bit = val & static_cast<T>(1);
if (bit == 1) {
auto str =
to_string_or_number(static_cast<EnumType>(static_cast<T>(1) << i));
flags.emplace_back(std::move(str));
}
++i;
val >>= 1;
}
return internal::strings::join("|", flags);
} else {
return to_string_or_number(_enum);
}
}

// Converts a string to a value of the given enum type.
template <internal::enums::is_scoped_enum EnumType>
rfl::Result<EnumType> string_to_enum(const std::string& _str) {
return rfl::internal::enums::StringConverter<EnumType>::string_to_enum(_str);
template <enchantum::Enum EnumType>
Result<EnumType> string_to_enum(const std::string& _str) {
const auto cast_numbers_or_names =
[](const std::string& name) -> Result<EnumType> {
const auto r = enchantum::cast<EnumType>(name);
if (r) return *r;
try {
return static_cast<EnumType>(std::stoi(name));
} catch (std::exception& exp) {
return error(exp.what());
}
};

if constexpr (enchantum::is_bitflag<EnumType>) {
using T = std::underlying_type_t<EnumType>;
const auto split = internal::strings::split(_str, "|");
auto res = static_cast<T>(0);
for (const auto& s : split) {
const auto r = cast_numbers_or_names(s);
if (r) {
res |= static_cast<T>(*r);
} else {
return r;
}
}
return static_cast<EnumType>(res);
} else {
return cast_numbers_or_names(_str);
}
}

// Returns a named tuple mapping names of enumerators of the given enum type to
// their values.
template <internal::enums::is_scoped_enum EnumType>
template <enchantum::Enum EnumType>
auto get_enumerators() {
constexpr auto names = internal::enums::get_enum_names<
EnumType, internal::enums::is_flag_enum<EnumType>>();
return internal::enums::names_to_enumerator_named_tuple(names);
return internal::enums::names_to_enumerator_named_tuple(
internal::enums::get_enum_names<EnumType>());
}

// Returns a named tuple mapping names of enumerators of the given enum type to
// their underlying values.
template <internal::enums::is_scoped_enum EnumType>
template <enchantum::Enum EnumType>
auto get_underlying_enumerators() {
constexpr auto names = internal::enums::get_enum_names<
EnumType, internal::enums::is_flag_enum<EnumType>>();
return internal::enums::names_to_underlying_enumerator_named_tuple(names);
return internal::enums::names_to_underlying_enumerator_named_tuple(
internal::enums::get_enum_names<EnumType>());
}

// Returns an std::array containing pairs of enumerator names (as
// std::string_view) and values.
template <internal::enums::is_scoped_enum EnumType>
template <enchantum::Enum EnumType>
constexpr auto get_enumerator_array() {
constexpr auto names = internal::enums::get_enum_names<
EnumType, internal::enums::is_flag_enum<EnumType>>();
return internal::enums::names_to_enumerator_array(names);
return internal::enums::names_to_enumerator_array(
internal::enums::get_enum_names<EnumType>());
}

// Returns an std::array containing pairs of enumerator names (as
// std::string_view) and underlying values.
template <internal::enums::is_scoped_enum EnumType>
template <enchantum::Enum EnumType>
constexpr auto get_underlying_enumerator_array() {
constexpr auto names = internal::enums::get_enum_names<
EnumType, internal::enums::is_flag_enum<EnumType>>();
return internal::enums::names_to_underlying_enumerator_array(names);
return internal::enums::names_to_underlying_enumerator_array(
internal::enums::get_enum_names<EnumType>());
}

// Returns the range of the given enum type as a pair of the minimum and maximum
template <internal::enums::is_scoped_enum EnumType>
template <enchantum::Enum EnumType>
constexpr auto get_enum_range() {
return std::make_pair(
internal::enums::get_range_min<EnumType,
internal::enums::is_flag_enum<EnumType>>(),
internal::enums::get_range_max<
EnumType, internal::enums::is_flag_enum<EnumType>>());
return std::make_pair(enchantum::enum_traits<EnumType>::min,
enchantum::enum_traits<EnumType>::max);
}

} // namespace rfl
Expand Down
84 changes: 8 additions & 76 deletions include/rfl/internal/enums/Names.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,94 +9,27 @@
#include <utility>

#include "../../Literal.hpp"
#include "../../config.hpp"
#include "../../define_literal.hpp"
#include "../../make_named_tuple.hpp"
#include "../../thirdparty/enchantum/enchantum.hpp"
#include "../StringLiteral.hpp"

namespace rfl {

// Enum values must be greater than or equal to RFL_ENUM_RANGE_MIN.
// By default, RFL_ENUM_RANGE_MIN is set to 0.
// To change the default minimum range for all enum types, redefine the macro
// RFL_ENUM_RANGE_MIN.
#if !defined(RFL_ENUM_RANGE_MIN)
#define RFL_ENUM_RANGE_MIN 0
#endif

// Enum values must be less than or equal to RFL_ENUM_RANGE_MAX.
// By default, RFL_ENUM_RANGE_MAX is set to 127.
// To change the default maximum range for all enum types, redefine the macro
// RFL_ENUM_RANGE_MAX.
#if !defined(RFL_ENUM_RANGE_MAX)
#define RFL_ENUM_RANGE_MAX 127
#endif

namespace config {

// To specify a different range for a particular enum type, specialize the
// enum_range template for that enum type.
template <typename T>
struct enum_range {
static constexpr int min = RFL_ENUM_RANGE_MIN;
static constexpr int max = RFL_ENUM_RANGE_MAX;
};
} // namespace config

namespace internal {
namespace enums {

static_assert(RFL_ENUM_RANGE_MAX > RFL_ENUM_RANGE_MIN,
"RFL_ENUM_RANGE_MAX must be greater than RFL_ENUM_RANGE_MIN.");

template <typename T, typename = void>
struct range_min : std::integral_constant<int, RFL_ENUM_RANGE_MIN> {};

template <typename T>
struct range_min<T, std::void_t<decltype(config::enum_range<T>::min)>>
: std::integral_constant<decltype(config::enum_range<T>::min),
config::enum_range<T>::min> {};

template <typename T, typename = void>
struct range_max : std::integral_constant<int, RFL_ENUM_RANGE_MAX> {};

template <typename T>
struct range_max<T, std::void_t<decltype(config::enum_range<T>::max)>>
: std::integral_constant<decltype(config::enum_range<T>::max),
config::enum_range<T>::max> {};
namespace rfl::internal::enums {

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

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

/// A list of all the possible enums
constexpr static std::array<EnumType, N> enums_ =
std::array<EnumType, N>{_enums...};

static_assert(N == 0 || LiteralType::size() == N,
"Size of literal and enum do not match.");

template <class NewLiteral, auto _new_enum>
using AddOneType = std::conditional_t<
N == 0, Names<EnumType, NewLiteral, 1, _is_flag, _new_enum>,
Names<EnumType, define_literal_t<LiteralType, NewLiteral>, N + 1,
_is_flag, _enums..., _new_enum>>;
constexpr static auto enums_ = std::array<EnumType, N>{_enums...};
};


template <class EnumType, class LiteralType1, size_t N1, bool _is_flag1,
auto... _enums1, class LiteralType2, size_t N2, auto... _enums2>
consteval auto operator|(
Names<EnumType, LiteralType1, N1, _is_flag1, _enums1...>,
Names<EnumType, LiteralType2, N2, _is_flag1, _enums2...>) {
using CombinedLiteral = define_literal_t<LiteralType1, LiteralType2>;
return Names<EnumType, CombinedLiteral, N1 + N2, _is_flag1,
_enums1..., _enums2...>{};
}

template <class EnumType, size_t N, bool _is_flag, StringLiteral... _names,
auto... _enums>
auto names_to_enumerator_named_tuple(
Expand Down Expand Up @@ -132,8 +65,7 @@ names_to_underlying_enumerator_array(
static_cast<std::underlying_type_t<EnumType>>(_enums))...};
}

} // namespace enums
} // namespace internal
} // namespace rfl
} // namespace rfl::internal::enums

#endif

Loading
Loading