#ifndef RFL_LITERAL_HPP_ #define RFL_LITERAL_HPP_ #include #include #include #include #include #include #include #include "Result.hpp" #include "internal/StringLiteral.hpp" #include "internal/VisitTree.hpp" namespace rfl { template struct LiteralHelper { constexpr static internal::StringLiteral field_ = _field; }; template class Literal { using FieldsType = std::tuple...>; public: using ValueType = std::conditional_t::max(), 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& _other) = default; /// Constructs a Literal from another literal. Literal(Literal&& _other) noexcept = default; Literal(const std::string& _str) : value_(find_value(_str).value()) {} /// A single-field literal is special because it /// can also have a default constructor. template > Literal() : value_(0) {} ~Literal() = default; /// Constructs a new Literal. template static Literal make() { return Literal(Literal::template value_of<_name>()); } /// Constructs a new Literal, equivalent to make, for reasons of /// consistency. template static Literal from_name() { return Literal::template make<_name>(); } /// Constructs a new Literal. template static Literal from_value() { static_assert(_value < num_fields_, "Value cannot exceed number of fields."); return Literal(_value); } /// Constructs a new Literal. static Result> from_value(ValueType _value) { if (_value >= num_fields_) { return Error("Value cannot exceed number of fields."); } return Literal(_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 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 static constexpr bool contains_any() { if constexpr (_i == num_fields_) { return false; } else { constexpr auto name = find_name_within_own_fields<_i>(); return OtherLiteralType::template contains() || contains_any(); } } /// Determines whether the literal contains all of the strings in the other /// literal at compile time. template 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()) { return contains_all(); } else { return contains_all(); } } } /// 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 from_string(const std::string& _str) { const auto to_literal = [](const auto& _v) { return Literal(_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. static std::vector names() { return allowed_strings_vec(); } /// Helper function to retrieve a name at compile time. template constexpr static auto name_of() { constexpr auto name = find_name_within_own_fields<_value>(); return Literal(); } /// Assigns from another literal. Literal& operator=(const Literal& _other) = default; /// Assigns from another literal. Literal& operator=(Literal&& _other) noexcept = default; /// Assigns the literal from a string Literal& operator=(const std::string& _str) { value_ = find_value(_str); return *this; } /// <=> for other Literals with the same fields. auto operator<=>(const Literal& _other) const { return value() <=> _other.value(); } /// <=> for other Literals with different fields. template inline auto operator<=>(const Literal<_fields...>& _l2) const { return name() <=> _l2.name(); } /// <=> for strings. inline auto operator<=>(const std::string& _str) const { return name() <=> _str; } /// <=> for const char*. template inline auto operator<=>(const char* _str) const { return name() <=> _str; } /// Equality operator. template 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 strings() { return allowed_strings_vec(); } /// Returns the value actually contained in the Literal. ValueType value() const { return value_; } /// Returns the value of the string literal in the template. template 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::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 static std::vector allowed_strings_vec( std::vector _values = {}) { 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; } } /// Whether the Literal contains duplicate strings. template constexpr static bool has_duplicate_strings() { if constexpr (_i >= num_fields_) { return false; } else { return is_duplicate<_i>() || has_duplicate_strings<_i + 1>(); } } template 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; } } /// Finds the correct index associated with /// the string at run time. template 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>(); } } /// Finds the correct index associated with /// the string at compile time within the Literal's own fields. template constexpr static auto find_name_within_own_fields() { using FieldType = typename std::tuple_element<_i, FieldsType>::type; return FieldType::field_; } /// Finds the correct value associated with /// the string at run time. template static Result find_value(const std::string& _str) { using FieldType = typename std::tuple_element<_i, FieldsType>::type; if (FieldType::field_.str() == _str) { return _i; } if constexpr (_i + 1 == num_fields_) { return Error("Literal does not support string '" + _str + "'. The following strings are supported: " + allowed_strings() + "."); } else { return find_value<_i + 1>(_str); } } /// Finds the value of a string literal at compile time. template 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 static void has_value(const std::string& _str, bool* _found) { if constexpr (_i == num_fields_) { *_found = false; return; } 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); } } static_assert(sizeof...(fields_) <= std::numeric_limits::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 inline constexpr auto name_of() { return LiteralType::template name_of<_value>(); } /// Helper function to retrieve a value at compile time. template inline constexpr auto value_of() { return LiteralType::template value_of<_name>(); } } // namespace rfl namespace std { template struct hash> { size_t operator()(const rfl::Literal& _l) const { return hash()(static_cast(_l.value())); } }; } // namespace std #endif // RFL_LITERAL_HPP_