This commit is contained in:
Mars 2024-06-08 14:10:59 -04:00
parent a743cdabe5
commit bd402f57f5
Signed by: pupbrained
GPG key ID: 874E22DF2F9DFCB5
276 changed files with 37936 additions and 22932 deletions

View file

@ -14,63 +14,82 @@
#include "../StringLiteral.hpp"
namespace rfl {
namespace internal {
namespace enums {
namespace internal {
namespace enums {
template <class EnumType, class LiteralType, size_t N, auto... _enums>
struct Names {
/// Contains a collection of enums as compile-time strings.
using Literal = LiteralType;
template <class EnumType, class LiteralType, size_t N, 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;
/// 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...};
/// 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.");
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, _new_enum>,
Names<EnumType, define_literal_t<LiteralType, NewLiteral>, N + 1,
_enums..., _new_enum>>;
};
template <class NewLiteral, auto _new_enum>
using AddOneType =
std::conditional_t<N == 0,
Names<EnumType, NewLiteral, 1, _new_enum>,
Names<EnumType,
define_literal_t<LiteralType, NewLiteral>,
N + 1,
_enums...,
_new_enum>>;
};
template <class EnumType, size_t N, StringLiteral... _names, auto... _enums>
auto names_to_enumerator_named_tuple(
Names<EnumType, Literal<_names...>, N, _enums...>) {
return make_named_tuple(Field<_names, EnumType>{_enums}...);
}
template <class EnumType,
size_t N,
StringLiteral... _names,
auto... _enums>
auto names_to_enumerator_named_tuple(
Names<EnumType, Literal<_names...>, N, _enums...>) {
return make_named_tuple(Field<_names, EnumType> {_enums}...);
}
template <class EnumType, size_t N, StringLiteral... _names, auto... _enums>
auto names_to_underlying_enumerator_named_tuple(
Names<EnumType, Literal<_names...>, N, _enums...>) {
return make_named_tuple(Field<_names, std::underlying_type_t<EnumType>>{
static_cast<std::underlying_type_t<EnumType>>(_enums)}...);
}
template <class EnumType,
size_t N,
StringLiteral... _names,
auto... _enums>
auto names_to_underlying_enumerator_named_tuple(
Names<EnumType, Literal<_names...>, N, _enums...>) {
return make_named_tuple(
Field<_names, std::underlying_type_t<EnumType>> {
static_cast<std::underlying_type_t<EnumType>>(_enums)}...);
}
template <class EnumType, size_t N, StringLiteral... _names, auto... _enums>
constexpr std::array<std::pair<std::string_view, EnumType>, N>
names_to_enumerator_array(Names<EnumType, Literal<_names...>, N, _enums...>) {
return {
std::make_pair(LiteralHelper<_names>::field_.string_view(), _enums)...};
}
template <class EnumType,
size_t N,
StringLiteral... _names,
auto... _enums>
constexpr std::array<std::pair<std::string_view, EnumType>, N>
names_to_enumerator_array(
Names<EnumType, Literal<_names...>, N, _enums...>) {
return {std::make_pair(LiteralHelper<_names>::field_.string_view(),
_enums)...};
}
template <class EnumType, size_t N, StringLiteral... _names, auto... _enums>
constexpr std::array<
std::pair<std::string_view, std::underlying_type_t<EnumType>>, N>
names_to_underlying_enumerator_array(
Names<EnumType, Literal<_names...>, N, _enums...>) {
return {
std::make_pair(LiteralHelper<_names>::field_.string_view(),
static_cast<std::underlying_type_t<EnumType>>(_enums))...};
}
template <class EnumType,
size_t N,
StringLiteral... _names,
auto... _enums>
constexpr std::array<
std::pair<std::string_view, std::underlying_type_t<EnumType>>,
N>
names_to_underlying_enumerator_array(
Names<EnumType, Literal<_names...>, N, _enums...>) {
return {std::make_pair(
LiteralHelper<_names>::field_.string_view(),
static_cast<std::underlying_type_t<EnumType>>(_enums))...};
}
} // namespace enums
} // namespace internal
} // namespace rfl
} // namespace enums
} // namespace internal
} // namespace rfl
#endif

View file

@ -15,118 +15,121 @@
#include "is_flag_enum.hpp"
namespace rfl {
namespace internal {
namespace enums {
namespace internal {
namespace enums {
template <class EnumType>
class StringConverter {
public:
static constexpr bool is_flag_enum_ = is_flag_enum<EnumType>;
template <class EnumType>
class StringConverter {
public:
static constexpr bool is_flag_enum_ = is_flag_enum<EnumType>;
static constexpr auto names_ = get_enum_names<EnumType, is_flag_enum_>();
static constexpr auto names_ =
get_enum_names<EnumType, is_flag_enum_>();
using NamesLiteral = typename decltype(names_)::Literal;
using NamesLiteral = typename decltype(names_)::Literal;
public:
/// Transform an enum to a matching string.
static std::string enum_to_string(const EnumType _enum) {
if constexpr (is_flag_enum_) {
return flag_enum_to_string(_enum);
} else {
return enum_to_single_string(_enum);
}
}
public:
/// Transform an enum to a matching string.
static std::string enum_to_string(const EnumType _enum) {
if constexpr (is_flag_enum_) {
return flag_enum_to_string(_enum);
} else {
return enum_to_single_string(_enum);
}
}
/// Transforms a string to the matching enum.
static Result<EnumType> string_to_enum(const std::string& _str) {
static_assert(names_.size != 0,
"No enum could be identified. Please choose enum values "
"between 0 to 127 or for flag enums choose 1,2,4,8,16,...");
if constexpr (is_flag_enum_) {
return string_to_flag_enum(_str);
} else {
return single_string_to_enum(_str);
}
}
/// Transforms a string to the matching enum.
static Result<EnumType> string_to_enum(const std::string& _str) {
static_assert(
names_.size != 0,
"No enum could be identified. Please choose enum values "
"between 0 to 127 or for flag enums choose 1,2,4,8,16,...");
if constexpr (is_flag_enum_) {
return string_to_flag_enum(_str);
} else {
return single_string_to_enum(_str);
}
}
private:
/// Iterates through the enum bit by bit and matches it against the flags.
static std::string flag_enum_to_string(const EnumType _e) {
using T = std::underlying_type_t<EnumType>;
auto val = static_cast<T>(_e);
int i = 0;
std::vector<std::string> flags;
while (val != 0) {
const auto bit = val & static_cast<T>(1);
if (bit == 1) {
auto str = enum_to_single_string(
static_cast<EnumType>(static_cast<T>(1) << i));
flags.emplace_back(std::move(str));
}
++i;
val >>= 1;
}
return strings::join("|", flags);
}
private:
/// Iterates through the enum bit by bit and matches it against the
/// flags.
static std::string flag_enum_to_string(const EnumType _e) {
using T = std::underlying_type_t<EnumType>;
auto val = static_cast<T>(_e);
int i = 0;
std::vector<std::string> flags;
while (val != 0) {
const auto bit = val & static_cast<T>(1);
if (bit == 1) {
auto str = enum_to_single_string(
static_cast<EnumType>(static_cast<T>(1) << i));
flags.emplace_back(std::move(str));
}
++i;
val >>= 1;
}
return strings::join("|", flags);
}
/// This assumes that _enum can be exactly matched to one of the names and
/// does not have to be combined using |.
static std::string enum_to_single_string(const EnumType _enum) {
const auto to_str = [](const auto _l) { return _l.str(); };
/// This assumes that _enum can be exactly matched to one of the names
/// and does not have to be combined using |.
static std::string enum_to_single_string(const EnumType _enum) {
const auto to_str = [](const auto _l) { return _l.str(); };
for (size_t i = 0; i < names_.size; ++i) {
if (names_.enums_[i] == _enum) {
return NamesLiteral::from_value(
static_cast<typename NamesLiteral::ValueType>(i))
.transform(to_str)
.value();
}
}
for (size_t i = 0; i < names_.size; ++i) {
if (names_.enums_[i] == _enum) {
return NamesLiteral::from_value(
static_cast<typename NamesLiteral::ValueType>(i))
.transform(to_str)
.value();
}
}
return std::to_string(static_cast<std::underlying_type_t<EnumType>>(_enum));
}
return std::to_string(
static_cast<std::underlying_type_t<EnumType>>(_enum));
}
/// Finds the enum matching the literal.
static EnumType literal_to_enum(const NamesLiteral _lit) noexcept {
return names_.enums_[_lit.value()];
}
/// Finds the enum matching the literal.
static EnumType literal_to_enum(const NamesLiteral _lit) noexcept {
return names_.enums_[_lit.value()];
}
/// This assumes that _enum can be exactly matched to one of the names and
/// does not have to be combined using |.
static Result<EnumType> single_string_to_enum(const std::string& _str) {
const auto r = NamesLiteral::from_string(_str).transform(literal_to_enum);
if (r) {
return r;
} else {
try {
return static_cast<EnumType>(std::stoi(_str));
} catch (std::exception& exp) {
return Error(exp.what());
}
}
}
/// This assumes that _enum can be exactly matched to one of the names
/// and does not have to be combined using |.
static Result<EnumType> single_string_to_enum(const std::string& _str) {
const auto r =
NamesLiteral::from_string(_str).transform(literal_to_enum);
if (r) {
return r;
} else {
try {
return static_cast<EnumType>(std::stoi(_str));
} catch (std::exception& exp) { return Error(exp.what()); }
}
}
/// Only relevant if this is a flag enum - combines the different matches
/// using |.
static Result<EnumType> string_to_flag_enum(
const std::string& _str) noexcept {
using T = std::underlying_type_t<EnumType>;
const auto split = strings::split(_str, "|");
auto res = static_cast<T>(0);
for (const auto& s : split) {
const auto r = single_string_to_enum(s);
if (r) {
res |= static_cast<T>(*r);
} else {
return r;
}
}
return static_cast<EnumType>(res);
}
};
/// Only relevant if this is a flag enum - combines the different
/// matches using |.
static Result<EnumType> string_to_flag_enum(
const std::string& _str) noexcept {
using T = std::underlying_type_t<EnumType>;
const auto split = strings::split(_str, "|");
auto res = static_cast<T>(0);
for (const auto& s : split) {
const auto r = single_string_to_enum(s);
if (r) {
res |= static_cast<T>(*r);
} else {
return r;
}
}
return static_cast<EnumType>(res);
}
};
} // namespace enums
} // namespace internal
} // namespace rfl
} // namespace enums
} // namespace internal
} // namespace rfl
#endif

View file

@ -36,116 +36,127 @@
// defined.
namespace rfl {
namespace internal {
namespace enums {
namespace internal {
namespace enums {
template <auto e>
consteval auto get_enum_name_str_view() {
// Unfortunately, we cannot avoid the use of a compiler-specific macro for
// Clang on Windows. For all other compilers, function_name works as intended.
template <auto e>
consteval auto get_enum_name_str_view() {
// Unfortunately, we cannot avoid the use of a compiler-specific macro
// for Clang on Windows. For all other compilers, function_name works as
// intended.
#if defined(__clang__) && defined(_MSC_VER)
const auto func_name = std::string_view{__PRETTY_FUNCTION__};
const auto func_name = std::string_view {__PRETTY_FUNCTION__};
#else
const auto func_name =
std::string_view{std::source_location::current().function_name()};
const auto func_name =
std::string_view {std::source_location::current().function_name()};
#endif
#if defined(__clang__)
const auto split = func_name.substr(0, func_name.size() - 1);
return split.substr(split.find("e = ") + 4);
const auto split = func_name.substr(0, func_name.size() - 1);
return split.substr(split.find("e = ") + 4);
#elif defined(__GNUC__)
const auto split = func_name.substr(0, func_name.size() - 1);
return split.substr(split.find("e = ") + 4);
const auto split = func_name.substr(0, func_name.size() - 1);
return split.substr(split.find("e = ") + 4);
#elif defined(_MSC_VER)
const auto split = func_name.substr(0, func_name.size() - 7);
return split.substr(split.find("get_enum_name_str_view<") + 23);
const auto split = func_name.substr(0, func_name.size() - 7);
return split.substr(split.find("get_enum_name_str_view<") + 23);
#else
static_assert(false,
"You are using an unsupported compiler. Please use GCC, Clang "
"or MSVC or use rfl::Literal.");
static_assert(
false,
"You are using an unsupported compiler. Please use GCC, Clang "
"or MSVC or use rfl::Literal.");
#endif
}
}
template <auto e>
consteval auto get_enum_name() {
constexpr auto name = get_enum_name_str_view<e>();
const auto to_str_lit = [&]<auto... Ns>(std::index_sequence<Ns...>) {
return StringLiteral<sizeof...(Ns) + 1>{name[Ns]...};
};
return to_str_lit(std::make_index_sequence<name.size()>{});
}
template <auto e>
consteval auto get_enum_name() {
constexpr auto name = get_enum_name_str_view<e>();
const auto to_str_lit = [&]<auto... Ns>(std::index_sequence<Ns...>) {
return StringLiteral<sizeof...(Ns) + 1> {name[Ns]...};
};
return to_str_lit(std::make_index_sequence<name.size()> {});
}
template <class T>
consteval T calc_greatest_power_of_two() {
if constexpr (std::is_signed_v<T>) {
return static_cast<T>(1) << (sizeof(T) * 8 - 2);
} else {
return static_cast<T>(1) << (sizeof(T) * 8 - 1);
}
}
template <class T>
consteval T calc_greatest_power_of_two() {
if constexpr (std::is_signed_v<T>) {
return static_cast<T>(1) << (sizeof(T) * 8 - 2);
} else {
return static_cast<T>(1) << (sizeof(T) * 8 - 1);
}
}
template <class T, bool _is_flag>
consteval T get_max() {
if constexpr (_is_flag) {
return calc_greatest_power_of_two<T>();
} else {
return std::numeric_limits<T>::max() > 127 ? static_cast<T>(127)
: std::numeric_limits<T>::max();
}
}
template <class T, bool _is_flag>
consteval T get_max() {
if constexpr (_is_flag) {
return calc_greatest_power_of_two<T>();
} else {
return std::numeric_limits<T>::max() > 127
? static_cast<T>(127)
: std::numeric_limits<T>::max();
}
}
template <class T, bool _is_flag, int _i>
consteval T calc_j() {
if constexpr (_is_flag) {
return static_cast<T>(1) << _i;
} else {
return static_cast<T>(_i);
}
}
template <class T, bool _is_flag, int _i>
consteval T calc_j() {
if constexpr (_is_flag) {
return static_cast<T>(1) << _i;
} else {
return static_cast<T>(_i);
}
}
template <class EnumType, class NamesType, auto _max, bool _is_flag, int _i>
consteval auto get_enum_names_impl() {
using T = std::underlying_type_t<EnumType>;
template <class EnumType,
class NamesType,
auto _max,
bool _is_flag,
int _i>
consteval auto get_enum_names_impl() {
using T = std::underlying_type_t<EnumType>;
constexpr T j = calc_j<T, _is_flag, _i>();
constexpr T j = calc_j<T, _is_flag, _i>();
constexpr auto name = get_enum_name<static_cast<EnumType>(j)>();
constexpr auto name = get_enum_name<static_cast<EnumType>(j)>();
if constexpr (std::get<0>(name.arr_) == '(') {
if constexpr (j == _max) {
return NamesType{};
} else {
return get_enum_names_impl<EnumType, NamesType, _max, _is_flag, _i + 1>();
}
} else {
using NewNames = typename NamesType::template AddOneType<
Literal<remove_namespaces<name>()>, static_cast<EnumType>(j)>;
if constexpr (std::get<0>(name.arr_) == '(') {
if constexpr (j == _max) {
return NamesType {};
} else {
return get_enum_names_impl<EnumType, NamesType, _max, _is_flag,
_i + 1>();
}
} else {
using NewNames = typename NamesType::template AddOneType<
Literal<remove_namespaces<name>()>, static_cast<EnumType>(j)>;
if constexpr (j == _max) {
return NewNames{};
} else {
return get_enum_names_impl<EnumType, NewNames, _max, _is_flag, _i + 1>();
}
}
}
if constexpr (j == _max) {
return NewNames {};
} else {
return get_enum_names_impl<EnumType, NewNames, _max, _is_flag,
_i + 1>();
}
}
}
template <class EnumType, bool _is_flag>
consteval auto get_enum_names() {
static_assert(is_scoped_enum<EnumType>,
"You must use scoped enums (using class or struct) for the "
"parsing to work!");
template <class EnumType, bool _is_flag>
consteval auto get_enum_names() {
static_assert(
is_scoped_enum<EnumType>,
"You must use scoped enums (using class or struct) for the "
"parsing to work!");
static_assert(std::is_integral_v<std::underlying_type_t<EnumType>>,
"The underlying type of any Enum must be integral!");
static_assert(std::is_integral_v<std::underlying_type_t<EnumType>>,
"The underlying type of any Enum must be integral!");
constexpr auto max = get_max<std::underlying_type_t<EnumType>, _is_flag>();
constexpr auto max =
get_max<std::underlying_type_t<EnumType>, _is_flag>();
using EmptyNames = Names<EnumType, rfl::Literal<"">, 0>;
using EmptyNames = Names<EnumType, rfl::Literal<"">, 0>;
return get_enum_names_impl<EnumType, EmptyNames, max, _is_flag, 0>();
}
return get_enum_names_impl<EnumType, EmptyNames, max, _is_flag, 0>();
}
} // namespace enums
} // namespace internal
} // namespace rfl
} // namespace enums
} // namespace internal
} // namespace rfl
#endif

View file

@ -6,17 +6,17 @@
#include "is_scoped_enum.hpp"
namespace rfl {
namespace internal {
namespace enums {
namespace internal {
namespace enums {
template <class EnumType>
concept is_flag_enum = is_scoped_enum<EnumType> &&
requires(EnumType e1, EnumType e2) {
{ e1 | e2 } -> std::same_as<EnumType>;
};
template <class EnumType>
concept is_flag_enum =
is_scoped_enum<EnumType> && requires(EnumType e1, EnumType e2) {
{ e1 | e2 } -> std::same_as<EnumType>;
};
} // namespace enums
} // namespace internal
} // namespace rfl
} // namespace enums
} // namespace internal
} // namespace rfl
#endif

View file

@ -5,15 +5,16 @@
#include <type_traits>
namespace rfl {
namespace internal {
namespace enums {
namespace internal {
namespace enums {
template <class EnumType>
concept is_scoped_enum = std::is_enum_v<EnumType> &&
!std::is_convertible_v<EnumType, std::underlying_type_t<EnumType>>;
template <class EnumType>
concept is_scoped_enum =
std::is_enum_v<EnumType> &&
!std::is_convertible_v<EnumType, std::underlying_type_t<EnumType>>;
} // namespace enums
} // namespace internal
} // namespace rfl
} // namespace enums
} // namespace internal
} // namespace rfl
#endif