Files
libconfig/build-config/reflect-cpp/include/rfl/Literal.hpp
Emily Boudreaux ec13264050 feat(reflect-cpp): Switched from glaze -> reflect cpp
A bug was discovered in glaze which prevented valid toml output. We have
switched to toml++ and reflect-cpp. The interface has remained the same
so this should not break any code
2025-12-06 10:55:46 -05:00

372 lines
11 KiB
C++

#ifndef RFL_LITERAL_HPP_
#define RFL_LITERAL_HPP_
#include <compare>
#include <cstdint>
#include <functional>
#include <limits>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "Result.hpp"
#include "Tuple.hpp"
#include "internal/StringLiteral.hpp"
#include "internal/find_index.hpp"
#include "internal/no_duplicate_field_names.hpp"
namespace rfl {
template <internal::StringLiteral _name>
struct LiteralHelper {
constexpr static internal::StringLiteral name_ = _name;
};
template <internal::StringLiteral... fields_>
class Literal {
using FieldsType = rfl::Tuple<LiteralHelper<fields_>...>;
public:
using ValueType = std::conditional_t<sizeof...(fields_) <= 255, std::uint8_t,
std::uint16_t>;
/// The number of different fields or different options that the literal
/// can assume.
static constexpr ValueType num_fields_ = sizeof...(fields_);
using ReflectionType = std::string;
/// Constructs a Literal from another literal.
Literal(const Literal<fields_...>& _other) = default;
/// Constructs a Literal from another literal.
Literal(Literal<fields_...>&& _other) noexcept = default;
Literal(const std::string& _str) : value_(find_value(_str).value()) {}
Literal() : value_(0) {}
~Literal() = default;
/// Constructs a new Literal.
template <internal::StringLiteral _name>
static Literal<fields_...> make() {
return Literal(Literal<fields_...>::template value_of<_name>());
}
/// Constructs a new Literal, equivalent to make, for reasons of consistency.
template <internal::StringLiteral _name>
static Literal<fields_...> from_name() {
return Literal<fields_...>::template make<_name>();
}
/// Constructs a new Literal.
template <ValueType _value>
static Literal<fields_...> from_value() {
static_assert(_value < num_fields_,
"Value cannot exceed number of fields.");
return Literal<fields_...>(_value);
}
/// Constructs a new Literal.
static Result<Literal<fields_...>> from_value(ValueType _value) {
if (_value >= num_fields_) {
return error("Value cannot exceed number of fields.");
}
return Literal<fields_...>(_value);
}
/// Determines whether the literal contains the string.
static bool contains(const std::string& _str) {
bool found = false;
has_value(_str, &found);
return found;
}
/// Determines whether the literal contains the string at compile time.
template <internal::StringLiteral _name>
static constexpr bool contains() {
return find_value_of<_name>() != -1;
}
/// Determines whether the literal contains any of the strings in the other
/// literal at compile time.
template <class OtherLiteralType>
static constexpr bool contains_any() {
return []<int... _is>(const std::integer_sequence<int, _is...>&) {
return (false || ... ||
OtherLiteralType::template contains<
find_name_within_own_fields<_is>()>());
}(std::make_integer_sequence<int, num_fields_>());
}
/// Determines whether the literal contains all of the strings in the other
/// literal at compile time.
template <class OtherLiteralType>
static constexpr bool contains_all() {
return []<int... _is>(const std::integer_sequence<int, _is...>&) {
return (true && ... &&
OtherLiteralType::template contains<
find_name_within_own_fields<_is>()>());
}(std::make_integer_sequence<int, num_fields_>());
}
/// Determines whether the literal has duplicate strings at compile time.
/// These is useful for checking collections of strings in other contexts.
static constexpr bool has_duplicates() {
return !internal::no_duplicate_field_names<FieldsType>();
}
/// Constructs a Literal from a string. Returns an error if the string
/// cannot be found.
static Result<Literal> from_string(const std::string& _str) {
const auto to_literal = [](const auto& _v) {
return Literal<fields_...>(_v);
};
return find_value(_str).transform(to_literal);
};
/// The name defined by the Literal.
std::string name() const { return find_name(); }
/// Returns all possible values of the literal as a std::vector<std::string>.
static std::vector<std::string> names() {
return allowed_strings_vec(std::make_integer_sequence<int, num_fields_>());
}
/// Helper function to retrieve a name at compile time.
template <int _value>
constexpr static auto name_of() {
constexpr auto name = find_name_within_own_fields<_value>();
return Literal<name>();
}
/// Assigns from another literal.
Literal<fields_...>& operator=(const Literal<fields_...>& _other) = default;
/// Assigns from another literal.
Literal<fields_...>& operator=(Literal<fields_...>&& _other) noexcept =
default;
/// Assigns the literal from a string
Literal<fields_...>& operator=(const std::string& _str) {
value_ = find_value(_str).value();
return *this;
}
/// <=> for other Literals with the same fields.
auto operator<=>(const Literal<fields_...>& _other) const {
return value() <=> _other.value();
}
/// <=> for other Literals with different fields.
template <internal::StringLiteral... _fields>
inline auto operator<=>(const Literal<_fields...>& _l2) const {
return name() <=> _l2.name();
}
/// <=> for strings.
inline auto operator<=>(const std::string& _str) const {
#if __cpp_lib_three_way_comparison >= 201907L
return name() <=> _str;
#else
auto const& const_name = name();
if (const_name < _str) {
return std::strong_ordering::less;
}
if (const_name == _str) {
return std::strong_ordering::equal;
}
return std::strong_ordering::greater;
#endif
}
/// <=> for const char*.
template <internal::StringLiteral... other_fields>
inline auto operator<=>(const char* _str) const {
#if __cpp_lib_three_way_comparison >= 201907L
return name() <=> _str;
#else
auto const& const_name = name();
if (const_name < _str) {
return std::strong_ordering::less;
}
if (const_name == _str) {
return std::strong_ordering::equal;
}
return std::strong_ordering::greater;
#endif
}
/// Equality operator.
template <class Other>
bool operator==(const Other& _other) const {
return (*this <=> _other) == 0;
}
/// Alias for .name().
std::string reflection() const { return name(); }
/// Returns the number of fields in the Literal.
static constexpr size_t size() { return num_fields_; }
/// Alias for .name().
std::string str() const { return name(); }
/// Alias for .names().
static std::vector<std::string> strings() {
return allowed_strings_vec(std::make_integer_sequence<int, num_fields_>());
}
/// Returns the value actually contained in the Literal.
ValueType value() const { return value_; }
/// Returns the value of the string literal in the template.
template <internal::StringLiteral _name>
static constexpr ValueType value_of() {
constexpr auto value = find_value_of<_name>();
static_assert(value >= 0, "String not supported.");
return value;
}
private:
/// Only the static methods are allowed to access this.
Literal(const ValueType _value) : value_(_value) {}
/// Returns all of the allowed fields.
static std::string allowed_strings() {
const auto vec =
allowed_strings_vec(std::make_integer_sequence<int, num_fields_>());
std::string str;
for (size_t i = 0; i < vec.size(); ++i) {
const auto head = "'" + vec[i] + "'";
str += i == 0 ? head : (", " + head);
}
return str;
}
/// Returns all of the allowed fields.
template <int... _is>
static std::vector<std::string> allowed_strings_vec(
std::integer_sequence<int, _is...>) {
std::vector<std::string> values;
(allowed_strings_vec_add_one<_is>(&values), ...);
return values;
}
template <int _i>
static void allowed_strings_vec_add_one(std::vector<std::string>* _values) {
using FieldType = tuple_element_t<_i, FieldsType>;
_values->emplace_back(FieldType::name_.str());
}
/// Finds the correct index associated with
/// the string at run time.
std::string find_name() const {
return find_name_set_str(std::make_integer_sequence<int, num_fields_>());
}
template <int... _is>
std::string find_name_set_str(std::integer_sequence<int, _is...>) const {
std::string name;
(find_name_set_if_matches<_is>(&name), ...);
return name;
}
template <int _i>
void find_name_set_if_matches(std::string* _name) const {
if (_i == value_) {
using FieldType = tuple_element_t<_i, FieldsType>;
*_name = FieldType::name_.str();
}
}
/// Finds the correct index associated with
/// the string at compile time within the Literal's own fields.
template <int _i>
constexpr static auto find_name_within_own_fields() {
return tuple_element_t<_i, FieldsType>::name_;
}
/// Finds the correct value associated with
/// the string at run time.
static Result<int> find_value(const std::string& _str) {
bool found = false;
const auto idx = find_value_set_idx(
_str, &found, std::make_integer_sequence<int, num_fields_>());
if (!found) {
return error(
"Literal does not support string '" + _str +
"'. The following strings are supported: " + allowed_strings() + ".");
}
return idx;
}
template <int... _is>
static int find_value_set_idx(const std::string& _str, bool* _found,
std::integer_sequence<int, _is...>) {
int idx = 0;
(find_value_set_if_matches<_is>(_str, _found, &idx), ...);
return idx;
}
template <int _i>
static void find_value_set_if_matches(const std::string& _str, bool* _found,
int* _idx) {
using FieldType = tuple_element_t<_i, FieldsType>;
if (!*_found && FieldType::name_.string_view() == _str) {
*_idx = _i;
*_found = true;
}
}
/// Finds the value of a string literal at compile time.
template <internal::StringLiteral _name>
static constexpr int find_value_of() {
return internal::find_index_or_minus_one<_name, FieldsType>();
}
/// Whether the literal contains this string.
static void has_value(const std::string& _str, bool* _found) {
find_value_set_idx(_str, _found,
std::make_integer_sequence<int, num_fields_>());
}
static_assert(sizeof...(fields_) <= std::numeric_limits<ValueType>::max(),
"Too many fields.");
static_assert(sizeof...(fields_) <= 1 || !has_duplicates(),
"Duplicate strings are not allowed in a Literal.");
private:
/// The underlying value.
ValueType value_;
};
/// Helper function to retrieve a name at compile time.
template <class LiteralType, int _value>
inline constexpr auto name_of() {
return LiteralType::template name_of<_value>();
}
/// Helper function to retrieve a value at compile time.
template <class LiteralType, internal::StringLiteral _name>
inline constexpr auto value_of() {
return LiteralType::template value_of<_name>();
}
} // namespace rfl
namespace std {
template <rfl::internal::StringLiteral... fields>
struct hash<rfl::Literal<fields...>> {
size_t operator()(const rfl::Literal<fields...>& _l) const {
return hash<int>()(static_cast<int>(_l.value()));
}
};
} // namespace std
#endif // RFL_LITERAL_HPP_