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
372 lines
11 KiB
C++
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_
|