draconisplusplus/include/rfl/Literal.hpp

383 lines
12 KiB
C++
Raw Normal View History

2024-05-31 22:59:00 -04:00
#ifndef RFL_LITERAL_HPP_
#define RFL_LITERAL_HPP_
#include <cstdint>
#include <functional>
#include <limits>
#include <stdexcept>
#include <string>
#include <tuple>
#include <type_traits>
#include "Result.hpp"
#include "internal/StringLiteral.hpp"
#include "internal/VisitTree.hpp"
namespace rfl {
2024-06-08 14:10:59 -04:00
template <internal::StringLiteral _field>
struct LiteralHelper {
constexpr static internal::StringLiteral field_ = _field;
};
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
template <internal::StringLiteral... fields_>
class Literal {
using FieldsType = std::tuple<LiteralHelper<fields_>...>;
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
public:
2024-06-08 15:53:06 -04:00
using ValueType = std::conditional_t<
sizeof...(fields_) <= std::numeric_limits<std::uint8_t>::max(),
std::uint8_t,
std::uint16_t>;
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// The number of different fields or different options that the literal
/// can assume.
static constexpr ValueType num_fields_ = sizeof...(fields_);
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
using ReflectionType = std::string;
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Constructs a Literal from another literal.
Literal(const Literal<fields_...>& _other) = default;
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Constructs a Literal from another literal.
Literal(Literal<fields_...>&& _other) noexcept = default;
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
Literal(const std::string& _str) : value_(find_value(_str).value()) {}
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// A single-field literal is special because it
/// can also have a default constructor.
2024-06-08 15:53:06 -04:00
template <
ValueType num_fields = num_fields_,
typename = std::enable_if_t<num_fields <= 1>>
2024-06-08 14:10:59 -04:00
Literal() : value_(0) {}
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
~Literal() = default;
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Constructs a new Literal.
template <internal::StringLiteral _name>
static Literal<fields_...> make() {
return Literal(Literal<fields_...>::template value_of<_name>());
}
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// 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>();
}
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Constructs a new Literal.
template <ValueType _value>
static Literal<fields_...> from_value() {
2024-06-08 15:53:06 -04:00
static_assert(
_value < num_fields_, "Value cannot exceed number of fields."
);
2024-06-08 14:10:59 -04:00
return Literal<fields_...>(_value);
2024-05-31 22:59:00 -04:00
}
2024-06-08 14:10:59 -04:00
/// 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);
}
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Determines whether the literal contains the string.
static bool contains(const std::string& _str) {
bool found = false;
has_value(_str, &found);
return found;
}
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Determines whether the literal contains the string at compile time.
template <internal::StringLiteral _name>
static constexpr bool contains() {
return find_value_of<_name>() != -1;
2024-05-31 22:59:00 -04:00
}
2024-06-08 14:10:59 -04:00
/// Determines whether the literal contains any of the strings in the other
/// literal at compile time.
template <class OtherLiteralType, int _i = 0>
static constexpr bool contains_any() {
if constexpr (_i == num_fields_) {
return false;
2024-05-31 22:59:00 -04:00
} else {
2024-06-08 14:10:59 -04:00
constexpr auto name = find_name_within_own_fields<_i>();
return OtherLiteralType::template contains<name>() ||
contains_any<OtherLiteralType, _i + 1>();
2024-05-31 22:59:00 -04:00
}
}
2024-06-08 14:10:59 -04:00
/// Determines whether the literal contains all of the strings in the other
/// literal at compile time.
template <class OtherLiteralType, int _i = 0, int _n_found = 0>
static constexpr bool contains_all() {
if constexpr (_i == num_fields_) {
return _n_found == OtherLiteralType::num_fields_;
} else {
constexpr auto name = find_name_within_own_fields<_i>();
if constexpr (OtherLiteralType::template contains<name>()) {
return contains_all<OtherLiteralType, _i + 1, _n_found + 1>();
} else {
return contains_all<OtherLiteralType, _i + 1, _n_found>();
}
}
}
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// 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 has_duplicate_strings(); }
/// 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);
2024-05-31 22:59:00 -04:00
};
2024-06-08 14:10:59 -04:00
/// The name defined by the Literal.
std::string name() const { return find_name(); }
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Returns all possible values of the literal as a
/// std::vector<std::string>.
static std::vector<std::string> names() { return allowed_strings_vec(); }
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// 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>();
}
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Assigns from another literal.
Literal<fields_...>& operator=(const Literal<fields_...>& _other) = default;
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Assigns from another literal.
2024-06-08 15:53:06 -04:00
Literal<fields_...>& operator=(Literal<fields_...>&& _other
) noexcept = default;
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Assigns the literal from a string
Literal<fields_...>& operator=(const std::string& _str) {
value_ = find_value(_str);
return *this;
}
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// <=> for other Literals with the same fields.
auto operator<=>(const Literal<fields_...>& _other) const {
return value() <=> _other.value();
}
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// <=> for other Literals with different fields.
template <internal::StringLiteral... _fields>
inline auto operator<=>(const Literal<_fields...>& _l2) const {
return name() <=> _l2.name();
}
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// <=> for strings.
inline auto operator<=>(const std::string& _str) const {
return name() <=> _str;
}
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// <=> for const char*.
template <internal::StringLiteral... other_fields>
inline auto operator<=>(const char* _str) const {
return name() <=> _str;
}
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Equality operator.
template <class Other>
bool operator==(const Other& _other) const {
return (*this <=> _other) == 0;
}
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Alias for .name().
std::string reflection() const { return name(); }
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Returns the number of fields in the Literal.
static constexpr size_t size() { return num_fields_; }
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Alias for .name().
std::string str() const { return name(); }
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Alias for .names().
static std::vector<std::string> strings() { return allowed_strings_vec(); }
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// Returns the value actually contained in the Literal.
ValueType value() const { return value_; }
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// 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;
2024-05-31 22:59:00 -04:00
}
2024-06-08 14:10:59 -04:00
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() {
2024-06-08 15:53:06 -04:00
const auto vec = allowed_strings_vec();
2024-06-08 14:10:59 -04:00
std::string str;
for (size_t i = 0; i < vec.size(); ++i) {
const auto head = "'" + vec[i] + "'";
str += i == 0 ? head : (", " + head);
}
return str;
2024-05-31 22:59:00 -04:00
}
2024-06-08 14:10:59 -04:00
/// Returns all of the allowed fields.
template <int _i = 0>
static std::vector<std::string> allowed_strings_vec(
2024-06-08 15:53:06 -04:00
std::vector<std::string> _values = {}
) {
2024-06-08 14:10:59 -04:00
using FieldType = typename std::tuple_element<_i, FieldsType>::type;
const auto head = FieldType::field_.str();
_values.push_back(head);
if constexpr (_i + 1 < num_fields_) {
return allowed_strings_vec<_i + 1>(std::move(_values));
} else {
return _values;
}
2024-05-31 22:59:00 -04:00
}
2024-06-08 14:10:59 -04:00
/// Whether the Literal contains duplicate strings.
template <int _i = 1>
constexpr static bool has_duplicate_strings() {
if constexpr (_i >= num_fields_) {
return false;
} else {
return is_duplicate<_i>() || has_duplicate_strings<_i + 1>();
}
2024-05-31 22:59:00 -04:00
}
2024-06-08 14:10:59 -04:00
template <int _i, int _j = _i - 1>
constexpr static bool is_duplicate() {
using FieldType1 = typename std::tuple_element<_i, FieldsType>::type;
using FieldType2 = typename std::tuple_element<_j, FieldsType>::type;
if constexpr (FieldType1::field_ == FieldType2::field_) {
return true;
} else if constexpr (_j > 0) {
return is_duplicate<_i, _j - 1>();
} else {
return false;
}
2024-05-31 22:59:00 -04:00
}
2024-06-08 14:10:59 -04:00
/// Finds the correct index associated with
/// the string at run time.
template <int _i = 0>
std::string find_name() const {
if (_i == value_) {
using FieldType = typename std::tuple_element<_i, FieldsType>::type;
return FieldType::field_.str();
}
if constexpr (_i + 1 == num_fields_) {
return "";
} else {
return find_name<_i + 1>();
}
2024-05-31 22:59:00 -04:00
}
2024-06-08 14:10:59 -04:00
/// 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() {
using FieldType = typename std::tuple_element<_i, FieldsType>::type;
return FieldType::field_;
2024-05-31 22:59:00 -04:00
}
2024-06-08 14:10:59 -04:00
/// Finds the correct value associated with
/// the string at run time.
template <int _i = 0>
static Result<int> find_value(const std::string& _str) {
2024-05-31 22:59:00 -04:00
using FieldType = typename std::tuple_element<_i, FieldsType>::type;
2024-06-08 14:10:59 -04:00
if (FieldType::field_.str() == _str) { return _i; }
if constexpr (_i + 1 == num_fields_) {
2024-06-08 15:53:06 -04:00
return Error(
"Literal does not support string '" + _str +
"'. The following strings are supported: " + allowed_strings() + "."
);
2024-05-31 22:59:00 -04:00
} else {
2024-06-08 14:10:59 -04:00
return find_value<_i + 1>(_str);
2024-05-31 22:59:00 -04:00
}
}
2024-06-08 14:10:59 -04:00
/// Finds the value of a string literal at compile time.
template <internal::StringLiteral _name, int _i = 0>
static constexpr int find_value_of() {
if constexpr (_i == num_fields_) {
return -1;
} else {
using FieldType = typename std::tuple_element<_i, FieldsType>::type;
if constexpr (FieldType::field_ == _name) {
return _i;
} else {
return find_value_of<_name, _i + 1>();
}
}
}
/// Whether the literal contains this string.
template <int _i = 0>
static void has_value(const std::string& _str, bool* _found) {
if constexpr (_i == num_fields_) {
*_found = false;
2024-05-31 22:59:00 -04:00
return;
2024-06-08 14:10:59 -04:00
} else {
using FieldType = typename std::tuple_element<_i, FieldsType>::type;
if (FieldType::field_.str() == _str) {
*_found = true;
return;
}
return has_value<_i + 1>(_str, _found);
2024-05-31 22:59:00 -04:00
}
}
2024-06-08 15:53:06 -04:00
static_assert(
sizeof...(fields_) <= std::numeric_limits<ValueType>::max(),
"Too many fields."
);
2024-05-31 22:59:00 -04:00
2024-06-08 15:53:06 -04:00
static_assert(
sizeof...(fields_) <= 1 || !has_duplicates(),
"Duplicate strings are not allowed in a Literal."
);
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
private:
/// The underlying value.
ValueType value_;
};
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// 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>();
}
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
/// 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>();
}
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
} // namespace rfl
2024-05-31 22:59:00 -04:00
namespace std {
2024-06-08 14:10:59 -04:00
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()));
}
};
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
} // namespace std
2024-05-31 22:59:00 -04:00
2024-06-08 14:10:59 -04:00
#endif // RFL_LITERAL_HPP_