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
This commit is contained in:
2025-12-06 10:55:46 -05:00
parent 2b5abeae58
commit ec13264050
365 changed files with 63946 additions and 357 deletions

View File

@@ -1,17 +0,0 @@
glaze_cmake_options = cmake.subproject_options()
glaze_cmake_options.add_cmake_defines({
'BUILD_SHARED_LIBS': 'OFF',
'BUILD_STATIC_LIBS': 'ON',
'CMAKE_INSTALL_LIBDIR': get_option('libdir'),
'CMAKE_INSTALL_INCLUDEDIR': get_option('includedir'),
'CMAKE_POSITION_INDEPENDENT_CODE': 'ON',
'galze_BUILD_EXAMPLES': 'OFF',
'glaze_DEVELOPER_MODE': 'OFF',
})
glaze_sp = cmake.subproject(
'glaze',
options: glaze_cmake_options,
)
glaze_dep = glaze_sp.dependency('glaze_glaze')

View File

@@ -1,2 +1,2 @@
cmake = import('cmake') cmake = import('cmake')
subdir('glaze') subdir('reflect-cpp')

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023-2025 Code17 GmbH
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,87 @@
#ifndef RFL_RFL_HPP_
#define RFL_RFL_HPP_
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4244)
#pragma warning(disable : 4101)
#endif
#include "rfl/AddStructName.hpp"
#include "rfl/AddTagsToVariants.hpp"
#include "rfl/AllOf.hpp"
#include "rfl/AllowRawPtrs.hpp"
#include "rfl/AnyOf.hpp"
#include "rfl/Attribute.hpp"
#include "rfl/Binary.hpp"
#include "rfl/Box.hpp"
#include "rfl/Bytestring.hpp"
#include "rfl/DefaultIfMissing.hpp"
#include "rfl/DefaultVal.hpp"
#include "rfl/Description.hpp"
#include "rfl/ExtraFields.hpp"
#include "rfl/Field.hpp"
#include "rfl/Flatten.hpp"
#include "rfl/Generic.hpp"
#include "rfl/Hex.hpp"
#include "rfl/Literal.hpp"
#include "rfl/NamedTuple.hpp"
#include "rfl/NoExtraFields.hpp"
#include "rfl/NoFieldNames.hpp"
#include "rfl/NoOptionals.hpp"
#include "rfl/Object.hpp"
#include "rfl/Oct.hpp"
#include "rfl/OneOf.hpp"
#include "rfl/Pattern.hpp"
#include "rfl/PatternValidator.hpp"
#include "rfl/Processors.hpp"
#include "rfl/Ref.hpp"
#include "rfl/Rename.hpp"
#include "rfl/Size.hpp"
#include "rfl/Skip.hpp"
#include "rfl/SnakeCaseToCamelCase.hpp"
#include "rfl/SnakeCaseToPascalCase.hpp"
#include "rfl/TaggedUnion.hpp"
#include "rfl/Timestamp.hpp"
#include "rfl/UnderlyingEnums.hpp"
#include "rfl/Validator.hpp"
#include "rfl/Variant.hpp"
#include "rfl/Vectorstring.hpp"
#include "rfl/always_false.hpp"
#include "rfl/apply.hpp"
#include "rfl/as.hpp"
#include "rfl/comparisons.hpp"
#include "rfl/concepts.hpp"
#include "rfl/default.hpp"
#include "rfl/define_literal.hpp"
#include "rfl/define_named_tuple.hpp"
#include "rfl/define_tagged_union.hpp"
#include "rfl/define_variant.hpp"
#include "rfl/enums.hpp"
#include "rfl/extract_discriminators.hpp"
#include "rfl/field_type.hpp"
#include "rfl/fields.hpp"
#include "rfl/from_generic.hpp"
#include "rfl/from_named_tuple.hpp"
#include "rfl/get.hpp"
#include "rfl/make_from_tuple.hpp"
#include "rfl/make_named_tuple.hpp"
#include "rfl/name_t.hpp"
#include "rfl/named_tuple_t.hpp"
#include "rfl/parsing/CustomParser.hpp"
#include "rfl/patterns.hpp"
#include "rfl/remove_fields.hpp"
#include "rfl/replace.hpp"
#include "rfl/to_generic.hpp"
#include "rfl/to_named_tuple.hpp"
#include "rfl/to_view.hpp"
#include "rfl/tuple_cat.hpp"
#include "rfl/type_name_t.hpp"
#include "rfl/view_t.hpp"
#include "rfl/visit.hpp"
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif

View File

@@ -0,0 +1,33 @@
#ifndef RFL_ADDSTRUCTNAME_HPP_
#define RFL_ADDSTRUCTNAME_HPP_
#include <tuple>
#include "Field.hpp"
#include "Literal.hpp"
#include "apply.hpp"
#include "internal/StringLiteral.hpp"
#include "internal/get_type_name.hpp"
#include "internal/remove_namespaces.hpp"
#include "make_named_tuple.hpp"
namespace rfl {
template <internal::StringLiteral field_name_>
struct AddStructName {
/// Adds the name of the struct as a new field.
template <class StructType>
static auto process(const auto& _view) {
using LiteralType = Literal<
internal::remove_namespaces<internal::get_type_name<StructType>()>()>;
using FieldType = Field<field_name_, LiteralType>;
const auto add_new_field = [](const auto&... _fields) {
return make_named_tuple(FieldType(LiteralType()), _fields...);
};
return rfl::apply(add_new_field, _view.fields());
}
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,30 @@
#ifndef RFL_ADDTAGSTOVARIANTS_HPP_
#define RFL_ADDTAGSTOVARIANTS_HPP_
namespace rfl {
/// This is a "fake" processor - it doesn't do much in itself, but its
/// inclusion instructs the parsers to automatically add tags to the variants
/// they might encounter.
struct AddTagsToVariants {
public:
template <class StructType>
static auto process(auto&& _named_tuple) {
return _named_tuple;
}
};
/// This is a "fake" processor - it doesn't do much in itself, but its
/// inclusion instructs the parsers to automatically add tags to the variants
/// they might encounter.
struct AddNamespacedTagsToVariants {
public:
template <class StructType>
static auto process(auto&& _named_tuple) {
return _named_tuple;
}
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,40 @@
#ifndef RFL_ALLOF_HPP_
#define RFL_ALLOF_HPP_
#include <vector>
#include "Result.hpp"
#include "parsing/schema/ValidationType.hpp"
namespace rfl {
/// Requires that all of the contraints C and Cs be true.
template <class C, class... Cs>
struct AllOf {
template <class T>
static rfl::Result<T> validate(T _value) noexcept {
return validate_impl<T, C, Cs...>(_value);
}
template <class T>
static parsing::schema::ValidationType to_schema() {
using ValidationType = parsing::schema::ValidationType;
const auto types = std::vector<ValidationType>(
{C::template to_schema<T>(), Cs::template to_schema<T>()...});
return ValidationType{ValidationType::AllOf{.types_ = types}};
}
private:
template <class T, class Head, class... Tail>
static rfl::Result<T> validate_impl(T _value) noexcept {
if constexpr (sizeof...(Tail) == 0) {
return Head::validate(_value);
} else {
return Head::validate(_value).and_then(validate_impl<T, Tail...>);
}
}
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,18 @@
#ifndef RFL_ALLOWRAWPTRS_HPP_
#define RFL_ALLOWRAWPTRS_HPP_
namespace rfl {
/// This is a "fake" processor - it doesn't do much in itself, but its
/// inclusion instructs the parsers to allow raw pointers.
struct AllowRawPtrs {
public:
template <class StructType>
static auto process(auto&& _named_tuple) {
return _named_tuple;
}
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,59 @@
#ifndef RFL_ANYOF_HPP_
#define RFL_ANYOF_HPP_
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "Result.hpp"
#include "parsing/schema/ValidationType.hpp"
namespace rfl {
/// Requires that all of the contraints C and Cs be true.
template <class C, class... Cs>
struct AnyOf {
template <class T>
static rfl::Result<T> validate(const T& _value) noexcept {
return validate_impl<T, C, Cs...>(_value, {});
}
template <class T>
static parsing::schema::ValidationType to_schema() {
using ValidationType = parsing::schema::ValidationType;
const auto types = std::vector<ValidationType>(
{C::template to_schema<T>(), Cs::template to_schema<T>()...});
return ValidationType{ValidationType::AnyOf{.types_ = types}};
}
private:
static std::string make_error_message(const std::vector<Error>& _errors) {
std::stringstream stream;
stream << "Expected at least one of the following validations to pass, but "
"none of them did:";
for (size_t i = 0; i < _errors.size(); ++i) {
stream << "\n" << i + 1 << ") " << _errors.at(i).what();
}
return stream.str();
}
template <class T, class Head, class... Tail>
static rfl::Result<T> validate_impl(const T& _value,
std::vector<Error> _errors) {
const auto handle_err = [&](Error&& _err) -> rfl::Result<T> {
_errors.push_back(std::forward<Error>(_err));
if constexpr (sizeof...(Tail) == 0) {
return error(make_error_message(_errors));
} else {
return validate_impl<T, Tail...>(
_value, std::forward<std::vector<Error>>(_errors));
}
};
return Head::validate(_value).or_else(handle_err);
}
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,136 @@
#ifndef RFL_ATTRIBUTE_HPP_
#define RFL_ATTRIBUTE_HPP_
#include <algorithm>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include "Literal.hpp"
#include "default.hpp"
namespace rfl {
template <class T>
struct Attribute {
using Type = T;
using ReflectionType = T;
Attribute() : value_(Type()) {}
Attribute(const Type& _value) : value_(_value) {}
Attribute(Type&& _value) noexcept : value_(std::move(_value)) {}
Attribute(Attribute<T>&& _attr) noexcept = default;
Attribute(const Attribute<Type>& _attr) = default;
template <class U>
Attribute(const Attribute<U>& _attr) : value_(_attr.get()) {}
template <class U>
Attribute(Attribute<U>&& _attr) : value_(_attr.get()) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Attribute(const U& _value) : value_(_value) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Attribute(U&& _value) noexcept : value_(std::forward<U>(_value)) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Attribute(const Attribute<U>& _attr) : value_(_attr.value()) {}
/// Assigns the underlying object to its default value.
template <class U = Type,
typename std::enable_if<std::is_default_constructible_v<U>,
bool>::type = true>
Attribute(const Default&) : value_(Type()) {}
~Attribute() = default;
/// Returns the underlying object.
const Type& get() const { return value_; }
/// Returns the underlying object.
Type& operator()() { return value_; }
/// Returns the underlying object.
const Type& operator()() const { return value_; }
/// Assigns the underlying object.
auto& operator=(const Type& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object.
auto& operator=(Type&& _value) noexcept {
value_ = std::move(_value);
return *this;
}
/// Assigns the underlying object.
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
auto& operator=(const U& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object to its default value.
template <class U = Type,
typename std::enable_if<std::is_default_constructible_v<U>,
bool>::type = true>
auto& operator=(const Default&) {
value_ = Type();
return *this;
}
/// Assigns the underlying object.
Attribute<T>& operator=(const Attribute<T>& _attr) = default;
/// Assigns the underlying object.
Attribute<T>& operator=(Attribute<T>&& _attr) = default;
/// Assigns the underlying object.
template <class U>
auto& operator=(const Attribute<U>& _attr) {
value_ = _attr.get();
return *this;
}
/// Assigns the underlying object.
template <class U>
auto& operator=(Attribute<U>&& _attr) {
value_ = std::forward<T>(_attr.value_);
return *this;
}
/// We want all parsers other than the XML parser to treat attributes like
/// normal fields, so we just implement the reflection interface.
const ReflectionType& reflection() const { return value_; }
/// Assigns the underlying object.
void set(const Type& _value) { value_ = _value; }
/// Assigns the underlying object.
void set(Type&& _value) { value_ = std::move(_value); }
/// Returns the underlying object.
Type& value() { return value_; }
/// Returns the underlying object.
const Type& value() const { return value_; }
/// The underlying value.
Type value_;
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,127 @@
#ifndef RFL_BINARY_HPP_
#define RFL_BINARY_HPP_
#include <algorithm>
#include <bitset>
#include <sstream>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
namespace rfl {
/// Used to define a field in the NamedTuple.
template <class T>
requires std::is_unsigned_v<T>
struct Binary {
/// The underlying type.
using Type = T;
using ReflectionType = std::string;
using Bitset = std::bitset<sizeof(Type) * 8>;
Binary() : value_(0) {}
Binary(const Type& _value) : value_(_value) {}
Binary(Binary&& _other) noexcept = default;
Binary(const Binary& _other) = default;
template <class U>
Binary(const Binary<U>& _other) : value_(_other.get()) {}
template <class U>
Binary(Binary<U>&& _other) : value_(_other.get()) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Binary(const U& _value) : value_(_value) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Binary(U&& _value) noexcept : value_(std::forward<U>(_value)) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Binary(const Binary<U>& _other) : value_(_other.value()) {}
Binary(const std::string& _str)
: value_(static_cast<T>(Bitset{_str}.to_ullong())) {}
~Binary() = default;
/// Returns the underlying object.
const Type& get() const { return value_; }
/// Returns the underlying object.
Type& operator()() { return value_; }
/// Returns the underlying object.
const Type& operator()() const { return value_; }
/// Assigns the underlying object.
auto& operator=(const Type& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object.
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
auto& operator=(const U& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object.
Binary& operator=(const Binary<T>& _other) = default;
/// Assigns the underlying object.
Binary& operator=(Binary<T>&& _other) = default;
/// Assigns the underlying object.
template <class U>
auto& operator=(const Binary<U>& _other) {
value_ = _other.get();
return *this;
}
/// Assigns the underlying object.
template <class U>
auto& operator=(const std::string& _str) {
value_ = static_cast<T>(Bitset{_str}.to_ullong());
return *this;
}
/// Assigns the underlying object.
template <class U>
auto& operator=(Binary<U>&& _other) {
value_ = std::forward<T>(_other.value_);
return *this;
}
/// Necessary for the automated parsing to work.
std::string reflection() const { return Bitset{value_}.to_string(); }
/// Assigns the underlying object.
void set(const Type& _value) { value_ = _value; }
/// Returns the underlying value as a string, alias for .reflection().
std::string str() const { return reflection(); }
/// Returns the underlying object.
Type& value() { return value_; }
/// Returns the underlying object.
const Type& value() const { return value_; }
/// The underlying value.
Type value_;
};
} // namespace rfl
#endif // RFL_FIELD_HPP_

View File

@@ -0,0 +1,159 @@
#ifndef RFL_BOX_HPP_
#define RFL_BOX_HPP_
#include <memory>
#include "Result.hpp"
namespace rfl {
enum class Copyability {
COPYABLE,
NON_COPYABLE
};
/// The Box class behaves very similarly to the unique_ptr, but unlike the
/// unique_ptr, it is 100% guaranteed to be filled at all times (unless the user
/// tries to access it after calling std::move does something else that is
/// clearly bad practice).
///
/// By default Box behaves like a unique_ptr in relation to copying, but it can be
/// configured to add copy constructor and assignment operators that call the
/// same function of the contained type T.
template <class T, Copyability C = Copyability::NON_COPYABLE>
class Box {
public:
/// The only way of creating new boxes is
/// Box<T>::make(...).
template <class... Args>
static Box make(Args&&... _args) {
return Box(std::make_unique<T>(std::forward<Args>(_args)...));
}
/// You can generate them from unique_ptrs as well, in which case it will
/// return an Error, if the unique_ptr is not set.
static Result<Box> make(std::unique_ptr<T>&& _ptr) {
if (!_ptr) {
return error("std::unique_ptr was a nullptr.");
}
return Box(std::move(_ptr));
}
Box() : ptr_(std::make_unique<T>()) {}
/// Copy constructor if copyable
Box(const Box& _other) requires (C == Copyability::COPYABLE)
{
ptr_ = std::make_unique<T>(*_other);
}
/// Copy constructor if not copyable
Box(const Box& _other) requires (C == Copyability::NON_COPYABLE) = delete;
Box(Box&& _other) = default;
template <class U, Copyability C2>
Box(Box<U, C2>&& _other) noexcept
: ptr_(std::forward<std::unique_ptr<U>>(_other.ptr())) {}
~Box() = default;
/// Returns a pointer to the underlying object
T* get() const { return ptr_.get(); }
/// Copy assignment operator if copyable
Box& operator=(const Box<T>& other) requires (C == Copyability::COPYABLE) {
if(this != &other) {
ptr_ = std::make_unique<T>(*other);
}
return *this;
}
/// Copy assignment operator if not copyable
Box& operator=(const Box& _other) requires (C == Copyability::NON_COPYABLE) = delete;
/// Move assignment operator
Box& operator=(Box&& _other) noexcept = default;
/// Move assignment operator
template <class U>
Box& operator=(Box<U>&& _other) noexcept {
ptr_ = std::forward<std::unique_ptr<U>>(_other.ptr());
return *this;
}
/// Returns the underlying object.
T& operator*() { return *ptr_; }
/// Returns the underlying object.
T& operator*() const { return *ptr_; }
/// Returns the underlying object.
T* operator->() { return ptr_.get(); }
/// Returns the underlying object.
T* operator->() const { return ptr_.get(); }
/// Returns the underlying unique_ptr
std::unique_ptr<T>& ptr() { return ptr_; }
/// Returns the underlying unique_ptr
const std::unique_ptr<T>& ptr() const { return ptr_; }
private:
/// Only make is allowed to use this constructor.
explicit Box(std::unique_ptr<T>&& _ptr) : ptr_(std::move(_ptr)) {}
private:
/// The underlying unique_ptr_
std::unique_ptr<T> ptr_;
};
/// Generates a new Ref<T>.
template <class T, class... Args>
auto make_box(Args&&... _args) {
return Box<T>::make(std::forward<Args>(_args)...);
}
/// Template specialization for a box that is copyable.
template<typename T>
using CopyableBox = Box<T, Copyability::COPYABLE>;
template <class T, class... Args>
auto make_copyable_box(Args&&... _args) {
return CopyableBox<T>::make(std::forward<Args>(_args)...);
}
template <class T1, class T2>
inline auto operator<=>(const Box<T1>& _b1, const Box<T2>& _b2) {
return _b1.ptr() <=> _b2.ptr();
}
template <class CharT, class Traits, class T>
inline std::basic_ostream<CharT, Traits>& operator<<(
std::basic_ostream<CharT, Traits>& _os, const Box<T>& _b) {
_os << _b.get();
return _os;
}
} // namespace rfl
namespace std {
template <class T>
struct hash<rfl::Box<T>> {
size_t operator()(const rfl::Box<T>& _b) const {
return std::hash<std::unique_ptr<T>>{}(_b.ptr());
}
};
template <class T>
inline void swap(rfl::Box<T>& _b1, rfl::Box<T>& _b2) {
return swap(_b1.ptr(), _b2.ptr());
}
} // namespace std
#endif

View File

@@ -0,0 +1,13 @@
#ifndef RFL_BYTESTRING_HPP_
#define RFL_BYTESTRING_HPP_
#include <cstddef>
#include <vector>
namespace rfl {
using Bytestring = std::vector<std::byte>;
} // namespace rfl
#endif

View File

@@ -0,0 +1,19 @@
#ifndef RFL_DEFAULTIFMISSING_HPP_
#define RFL_DEFAULTIFMISSING_HPP_
namespace rfl {
/// This is a "fake" processor - it doesn't do much in itself, but its
/// inclusion instructs the parsers to use the default values for missing
/// fields.
struct DefaultIfMissing {
public:
template <class StructType>
static auto process(auto&& _named_tuple) {
return _named_tuple;
}
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,129 @@
#ifndef RFL_DEFAULTVAL_HPP_
#define RFL_DEFAULTVAL_HPP_
#include <type_traits>
#include <utility>
#include "default.hpp"
namespace rfl {
template <class T>
struct DefaultVal {
public:
using Type = std::remove_cvref_t<T>;
DefaultVal() : value_(Type()) {}
DefaultVal(const Type& _value) : value_(_value) {}
DefaultVal(Type&& _value) noexcept : value_(std::move(_value)) {}
DefaultVal(DefaultVal&& _field) noexcept = default;
DefaultVal(const DefaultVal& _field) = default;
template <class U>
DefaultVal(const DefaultVal<U>& _field) : value_(_field.get()) {}
template <class U>
DefaultVal(DefaultVal<U>&& _field) noexcept(
noexcept(Type(std::move(_field.value()))))
: value_(std::move(_field.value())) {}
template <class U>
requires(std::is_convertible_v<U, Type>)
DefaultVal(const U& _value) : value_(_value) {}
template <class U>
requires(std::is_convertible_v<U, Type>)
DefaultVal(U&& _value) noexcept : value_(std::forward<U>(_value)) {}
template <class U>
requires(std::is_convertible_v<U, Type>)
DefaultVal(const DefaultVal<U>& _field) : value_(_field.value()) {}
/// Assigns the underlying object to its default value.
template <class U = Type>
requires(std::is_default_constructible_v<U>)
DefaultVal(const Default&) : value_(Type()) {}
~DefaultVal() = default;
/// Returns the underlying object.
const Type& get() const { return value_; }
/// Returns the underlying object.
Type& operator()() { return value_; }
/// Returns the underlying object.
const Type& operator()() const { return value_; }
/// Assigns the underlying object.
auto& operator=(const Type& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object.
auto& operator=(Type&& _value) noexcept {
value_ = std::move(_value);
return *this;
}
/// Assigns the underlying object.
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
auto& operator=(const U& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object to its default value.
template <class U = Type,
typename std::enable_if<std::is_default_constructible_v<U>,
bool>::type = true>
auto& operator=(const Default&) {
value_ = Type();
return *this;
}
/// Assigns the underlying object.
DefaultVal& operator=(const DefaultVal& _field) = default;
/// Assigns the underlying object.
DefaultVal& operator=(DefaultVal&& _field) = default;
/// Assigns the underlying object.
template <class U>
auto& operator=(const DefaultVal<U>& _field) {
value_ = _field.get();
return *this;
}
/// Assigns the underlying object.
template <class U>
auto& operator=(DefaultVal<U>&& _field) {
value_ = std::forward<U>(_field.value_);
return *this;
}
/// Assigns the underlying object.
void set(const Type& _value) { value_ = _value; }
/// Assigns the underlying object.
void set(Type&& _value) { value_ = std::move(_value); }
/// Returns the underlying object.
Type& value() { return value_; }
/// Returns the underlying object.
const Type& value() const { return value_; }
/// The underlying value.
Type value_;
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,150 @@
#ifndef RFL_DESCRIPTION_HPP_
#define RFL_DESCRIPTION_HPP_
#include <algorithm>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include "Literal.hpp"
#include "default.hpp"
#include "internal/StringLiteral.hpp"
namespace rfl {
/// Used to add a description to the field - this is only relevant for the JSON
/// schema and will be ignored by the normal serialization routines.
template <internal::StringLiteral _description, class T>
struct Description {
/// The underlying type.
using Type = T;
/// The description of the field.
using Content = rfl::Literal<_description>;
using ReflectionType = Type;
Description() : value_(Type()) {}
Description(const Type& _value) : value_(_value) {}
Description(Type&& _value) noexcept : value_(std::move(_value)) {}
Description(Description&& _field) noexcept = default;
Description(const Description& _field) = default;
template <class U>
Description(const Description<_description, U>& _field)
: value_(_field.get()) {}
template <class U>
Description(Description<_description, U>&& _field) : value_(_field.get()) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Description(const U& _value) : value_(_value) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Description(U&& _value) noexcept : value_(std::forward<U>(_value)) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Description(const Description<_description, U>& _field)
: value_(_field.value()) {}
/// Assigns the underlying object to its default value.
template <class U = Type,
typename std::enable_if<std::is_default_constructible_v<U>,
bool>::type = true>
Description(const Default&) : value_(Type()) {}
~Description() = default;
/// The description of the field, for internal use.
constexpr static const internal::StringLiteral description_ = _description;
/// Returns the underlying object.
const Type& get() const { return value_; }
/// Returns the underlying object.
Type& operator()() { return value_; }
/// Returns the underlying object.
const Type& operator()() const { return value_; }
/// Assigns the underlying object.
auto& operator=(const Type& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object.
auto& operator=(Type&& _value) noexcept {
value_ = std::move(_value);
return *this;
}
/// Assigns the underlying object.
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
auto& operator=(const U& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object to its default value.
template <class U = Type,
typename std::enable_if<std::is_default_constructible_v<U>,
bool>::type = true>
auto& operator=(const Default&) {
value_ = Type();
return *this;
}
/// Assigns the underlying object.
Description& operator=(
const Description& _field) = default;
/// Assigns the underlying object.
Description& operator=(
Description&& _field) = default;
/// Assigns the underlying object.
template <class U>
auto& operator=(const Description<_description, U>& _field) {
value_ = _field.get();
return *this;
}
/// Assigns the underlying object.
template <class U>
auto& operator=(Description<_description, U>&& _field) {
value_ = std::forward<T>(_field.value_);
return *this;
}
/// Returns the underlying object - necessary for the reflection to work.
const Type& reflection() const { return value_; }
/// Assigns the underlying object.
void set(const Type& _value) { value_ = _value; }
/// Assigns the underlying object.
void set(Type&& _value) { value_ = std::move(_value); }
/// Returns the underlying object.
Type& value() { return value_; }
/// Returns the underlying object.
const Type& value() const { return value_; }
/// The underlying value.
Type value_;
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,15 @@
#ifndef RFL_EXTRAFIELDS_HPP_
#define RFL_EXTRAFIELDS_HPP_
#include "Object.hpp"
namespace rfl {
/// Used to embed additional fields for which the names cannot be known in
/// advance and can therefore not be encoded in the struct.
template <class T>
class ExtraFields : public Object<T> {};
} // namespace rfl
#endif

View File

@@ -0,0 +1,151 @@
#ifndef RFL_FIELD_HPP_
#define RFL_FIELD_HPP_
#include <string_view>
#include <type_traits>
#include <utility>
#include "Literal.hpp"
#include "default.hpp"
#include "internal/Array.hpp"
#include "internal/StringLiteral.hpp"
#include "internal/wrap_in_rfl_array_t.hpp"
namespace rfl {
/// Used to define a field in the NamedTuple.
template <internal::StringLiteral _name, class T>
struct Field {
/// The underlying type.
using Type = internal::wrap_in_rfl_array_t<T>;
/// The name of the field.
using Name = rfl::Literal<_name>;
Field(const Type& _value) : value_(_value) {}
Field(Type&& _value) noexcept : value_(std::move(_value)) {}
Field(Field&& _field) noexcept = default;
Field(const Field& _field) = default;
template <class U>
Field(const Field<_name, U>& _field) : value_(_field.get()) {}
template <class U>
Field(Field<_name, U>&& _field) : value_(_field.get()) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Field(const U& _value) : value_(_value) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Field(U&& _value) noexcept : value_(std::forward<U>(_value)) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Field(const Field<_name, U>& _field) : value_(_field.value()) {}
/// Assigns the underlying object to its default value.
template <class U = Type,
typename std::enable_if<std::is_default_constructible_v<U>,
bool>::type = true>
Field(const Default&) : value_(Type()) {}
~Field() = default;
/// The name of the field, for internal use.
constexpr static const internal::StringLiteral name_ = _name;
/// Returns the underlying object.
const Type& get() const { return value_; }
/// The name of the field.
constexpr static std::string_view name() { return name_.string_view(); }
/// Returns the underlying object.
Type& operator()() { return value_; }
/// Returns the underlying object.
const Type& operator()() const { return value_; }
/// Assigns the underlying object.
auto& operator=(const Type& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object.
auto& operator=(Type&& _value) noexcept {
value_ = std::move(_value);
return *this;
}
/// Assigns the underlying object.
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
auto& operator=(const U& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object to its default value.
template <class U = Type,
typename std::enable_if<std::is_default_constructible_v<U>,
bool>::type = true>
auto& operator=(const Default&) {
value_ = Type();
return *this;
}
/// Assigns the underlying object.
Field& operator=(const Field& _field) = default;
/// Assigns the underlying object.
Field& operator=(Field&& _field) = default;
/// Assigns the underlying object.
template <class U>
auto& operator=(const Field<_name, U>& _field) {
value_ = _field.get();
return *this;
}
/// Assigns the underlying object.
template <class U>
auto& operator=(Field<_name, U>&& _field) {
value_ = std::forward<T>(_field.value_);
return *this;
}
/// Assigns the underlying object.
void set(const Type& _value) { value_ = _value; }
/// Assigns the underlying object.
void set(Type&& _value) { value_ = std::move(_value); }
/// Returns the underlying object.
Type& value() { return value_; }
/// Returns the underlying object.
const Type& value() const { return value_; }
/// The underlying value.
Type value_;
};
template <internal::StringLiteral _name, class T>
inline auto make_field(T&& _value) {
using T0 = std::remove_cvref_t<T>;
if constexpr (std::is_array_v<T0>) {
return Field<_name, T0>(internal::Array<T0>(std::forward<T>(_value)));
} else {
return Field<_name, T0>(std::forward<T>(_value));
}
}
} // namespace rfl
#endif // RFL_FIELD_HPP_

View File

@@ -0,0 +1,120 @@
#ifndef RFL_FLATTEN_HPP_
#define RFL_FLATTEN_HPP_
#include <algorithm>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
namespace rfl {
/// Used to embed another struct into the generated output.
template <class T>
struct Flatten {
/// The underlying type.
using Type = std::remove_cvref_t<T>;
Flatten() = default;
Flatten(const Type& _value) : value_(_value) {}
Flatten(Type&& _value) noexcept : value_(std::forward<Type>(_value)) {}
Flatten(const Flatten& _f) = default;
Flatten(Flatten&& _f) noexcept = default;
template <class U>
Flatten(const Flatten<U>& _f) : value_(_f.get()) {}
template <class U>
Flatten(Flatten<U>&& _f) : value_(_f.get()) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Flatten(const U& _value) : value_(_value) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Flatten(U&& _value) : value_(_value) {}
~Flatten() = default;
/// Returns the underlying object.
Type& get() { return value_; }
/// Returns the underlying object.
const Type& get() const { return value_; }
/// Returns the underlying object.
Type& operator()() { return value_; }
/// Returns the underlying object.
const Type& operator()() const { return value_; }
/// Assigns the underlying object.
Flatten& operator=(const T& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object.
Flatten& operator=(T&& _value) {
value_ = std::forward<Type>(_value);
return *this;
}
/// Assigns the underlying object.
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Flatten& operator=(const U& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object.
Flatten& operator=(const Flatten& _f) = default;
/// Assigns the underlying object.
Flatten& operator=(Flatten&& _f) = default;
/// Assigns the underlying object.
template <class U>
Flatten& operator=(const Flatten<U>& _f) {
value_ = _f.get();
return *this;
}
/// Assigns the underlying object.
template <class U>
Flatten& operator=(Flatten<U>&& _f) {
value_ = std::forward<U>(_f);
return *this;
}
/// Three-way comparison operator
template <class U>
auto operator<=>(const Flatten<U>& _f) const {
return value_ <=> _f.value_;
}
/// Equality comparison operator.
template <class U>
bool operator==(const Flatten<U>& _f) const {
return value_ == _f.get();
}
/// Assigns the underlying object.
void set(const Type& _value) { value_ = _value; }
/// Assigns the underlying object.
void set(Type&& _value) { value_ = std::forward<Type>(_value); }
/// The underlying value.
Type value_;
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,299 @@
#ifndef RFL_GENERIC_HPP_
#define RFL_GENERIC_HPP_
#include <optional>
#include <ostream>
#include <string>
#include <type_traits>
#include <variant>
#include <vector>
#include "Object.hpp"
#include "Result.hpp"
#include "Variant.hpp"
#include "common.hpp"
namespace rfl {
class RFL_API Generic {
public:
constexpr static std::nullopt_t Null = std::nullopt;
using Array = std::vector<Generic>;
using Object = rfl::Object<Generic>;
using VariantType = std::variant<bool, int64_t, double, std::string, Object,
Array, std::nullopt_t>;
using ReflectionType = std::optional<
std::variant<bool, int64_t, double, std::string, Object, Array>>;
Generic();
Generic(Generic&& _other) noexcept;
Generic(const Generic& _other);
Generic(const VariantType& _value);
Generic(VariantType&& _value) noexcept;
Generic(const ReflectionType& _value);
template <class T,
typename std::enable_if<std::is_convertible_v<T, VariantType>,
bool>::type = true>
Generic(const T& _value) {
value_ = _value;
}
template <class T,
typename std::enable_if<std::is_convertible_v<T, VariantType>,
bool>::type = true>
Generic(T&& _value) noexcept : value_(std::forward<T>(_value)) {}
~Generic();
/// Returns the underlying object.
const VariantType& get() const { return value_; }
/// Whether the object contains the null value.
bool is_null() const noexcept;
/// Assigns the underlying object.
Generic& operator=(const VariantType& _value);
/// Assigns the underlying object.
Generic& operator=(VariantType&& _value) noexcept;
/// Assigns the underlying object.
template <class T,
typename std::enable_if<std::is_convertible_v<T, VariantType>,
bool>::type = true>
auto& operator=(const T& _value) {
using Type = std::remove_cvref_t<T>;
if constexpr (std::is_same_v<Type, bool>) {
value_.emplace<0>(_value);
} else if constexpr (std::is_integral_v<Type>) {
value_.emplace<1>(static_cast<int64_t>(_value));
} else if constexpr (std::is_floating_point_v<Type>) {
value_.emplace<2>(static_cast<double>(_value));
} else {
value_ = _value;
}
return *this;
}
/// Assigns the underlying object.
Generic& operator=(const Generic& _other);
/// Assigns the underlying object.
Generic& operator=(Generic&& _other);
/// Returns the underlying object, necessary for the serialization to work.
ReflectionType reflection() const noexcept;
/// Casts the underlying value to an rfl::Generic::Array or returns an
/// rfl::Error, if the underlying value is not an rfl::Generic::Array.
Result<Array> to_array() const noexcept {
return std::visit(
[](auto _v) -> Result<Array> {
using V = std::remove_cvref_t<decltype(_v)>;
if constexpr (std::is_same_v<V, Array>) {
return _v;
} else {
return error(
"rfl::Generic: Could not cast the underlying value to an "
"rfl::Generic::Array.");
}
},
value_);
}
/// Casts the underlying value to a boolean or returns an rfl::Error, if the
/// underlying value is not a boolean.
Result<bool> to_bool() const noexcept {
return std::visit(
[](auto _v) -> Result<bool> {
using V = std::remove_cvref_t<decltype(_v)>;
if constexpr (std::is_same_v<V, bool>) {
return _v;
} else {
return error(
"rfl::Generic: Could not cast the underlying value to a "
"boolean.");
}
},
value_);
}
/// Casts the underlying value to a double or returns an rfl::Error, if the
/// underlying value is not a number or the conversion would result in loss of
/// precision.
Result<double> to_double() const noexcept {
return std::visit(
[](auto _v) -> Result<double> {
using V = std::remove_cvref_t<decltype(_v)>;
if constexpr (std::is_same_v<V, double>) {
return _v;
} else if constexpr (std::is_same_v<V, int64_t>) {
auto _d = static_cast<double>(_v);
if (static_cast<int64_t>(_d) == _v) {
return _d;
} else {
return error(
"rfl::Generic: Could not cast the underlying value to a "
"double without loss of precision.");
}
} else {
return error(
"rfl::Generic: Could not cast the underlying value to a "
"double.");
}
},
value_);
}
/// Casts the underlying value to an integer or returns an rfl::Error, if the
/// underlying value is not an integer.
Result<int> to_int() const noexcept {
return std::visit(
[](auto _v) -> Result<int> {
using V = std::remove_cvref_t<decltype(_v)>;
if constexpr (std::is_same_v<V, int64_t>) {
return static_cast<int>(_v);
} else {
return error(
"rfl::Generic: Could not cast the underlying value to an "
"integer.");
}
},
value_);
}
/// Casts the underlying value to an int64 or returns an rfl::Error, if the
/// underlying value is not an integer.
Result<int64_t> to_int64() const noexcept {
return std::visit(
[](auto _v) -> Result<int64_t> {
using V = std::remove_cvref_t<decltype(_v)>;
if constexpr (std::is_same_v<V, int64_t>) {
return _v;
} else {
return error(
"rfl::Generic: Could not cast the underlying value to an "
"int64.");
}
},
value_);
}
/// Casts the underlying value to an rfl::Generic::Object or returns an
/// rfl::Error, if the underlying value is not an rfl::Generic::Object.
Result<Object> to_object() const noexcept {
return std::visit(
[](auto _v) -> Result<Object> {
using V = std::remove_cvref_t<decltype(_v)>;
if constexpr (std::is_same_v<V, Object>) {
return _v;
} else {
return error(
"rfl::Generic: Could not cast the underlying value to an "
"rfl::Generic::Object.");
}
},
value_);
}
/// Casts the underlying value to rfl::Generic::Null or returns an
/// rfl::Error, if the underlying value is not rfl::Generic::Null.
Result<std::nullopt_t> to_null() const noexcept {
return std::visit(
[](auto _v) -> Result<std::nullopt_t> {
using V = std::remove_cvref_t<decltype(_v)>;
if constexpr (std::is_same_v<V, std::nullopt_t>) {
return _v;
} else {
return error(
"rfl::Generic: Could not cast the underlying value to "
"rfl::Generic::Null.");
}
},
value_);
}
/// Casts the underlying value to a string or returns an rfl::Error, if the
/// underlying value is not a string.
Result<std::string> to_string() const noexcept {
return std::visit(
[](auto _v) -> Result<std::string> {
using V = std::remove_cvref_t<decltype(_v)>;
if constexpr (std::is_same_v<V, std::string>) {
return _v;
} else {
return error(
"rfl::Generic: Could not cast the underlying value to a "
"string.");
}
},
value_);
}
/// Returns the underlying variant.
VariantType& variant() noexcept { return value_; };
/// Returns the underlying variant.
const VariantType& variant() const noexcept { return value_; };
private:
static VariantType from_reflection_type(const ReflectionType& _r) noexcept;
private:
VariantType value_;
};
/// Casts the underlying value to an rfl::Generic::Array or returns an
/// rfl::Error, if the underlying value is not an rfl::Generic::Array.
inline Result<Generic::Array> to_array(const Generic& _g) noexcept {
return _g.to_array();
}
/// Casts the underlying value to a boolean or returns an rfl::Error, if the
/// underlying value is not a boolean.
inline Result<bool> to_bool(const Generic& _g) noexcept { return _g.to_bool(); }
/// Casts the underlying value to a double or returns an rfl::Error, if the
/// underlying value is not a double.
inline Result<double> to_double(const Generic& _g) noexcept {
return _g.to_double();
}
/// Casts the underlying value to an integer or returns an rfl::Error, if the
/// underlying value is not an integer.
inline Result<int> to_int(const Generic& _g) noexcept { return _g.to_int(); }
/// Casts the underlying value to an int64 or returns an rfl::Error, if the
/// underlying value is not an integer.
inline Result<long> to_int64(const Generic& _g) noexcept {
return _g.to_int64();
}
/// Casts the underlying value to an rfl::Generic::Object or returns an
/// rfl::Error, if the underlying value is not an rfl::Generic::Object.
inline Result<Generic::Object> to_object(const Generic& _g) noexcept {
return _g.to_object();
}
/// Casts the underlying value to a double or returns an rfl::Error, if the
/// underlying value is not a double.
inline Result<std::nullopt_t> to_null(const Generic& _g) noexcept {
return _g.to_null();
}
/// Casts the underlying value to a string or returns an rfl::Error, if the
/// underlying value is not a string.
inline Result<std::string> to_string(const Generic& _g) noexcept {
return _g.to_string();
}
} // namespace rfl
#endif

View File

@@ -0,0 +1,129 @@
#ifndef RFL_HEX_HPP_
#define RFL_HEX_HPP_
#include <algorithm>
#include <sstream>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
namespace rfl {
/// Used to define a field in the NamedTuple.
template <class T>
requires std::is_integral_v<T>
struct Hex {
/// The underlying type.
using Type = T;
using ReflectionType = std::string;
Hex() : value_(0) {}
Hex(const Type& _value) : value_(_value) {}
Hex(Hex<T>&& _other) noexcept = default;
Hex(const Hex<T>& _other) = default;
template <class U>
Hex(const Hex<U>& _other) : value_(_other.get()) {}
template <class U>
Hex(Hex<U>&& _other) : value_(_other.get()) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Hex(const U& _value) : value_(_value) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Hex(U&& _value) noexcept : value_(std::forward<U>(_value)) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Hex(const Hex<U>& _other) : value_(_other.value()) {}
Hex(const std::string& _str) {
std::istringstream(_str) >> std::hex >> value_;
}
~Hex() = default;
/// Returns the underlying object.
const Type& get() const { return value_; }
/// Returns the underlying object.
Type& operator()() { return value_; }
/// Returns the underlying object.
const Type& operator()() const { return value_; }
/// Assigns the underlying object.
auto& operator=(const Type& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object.
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
auto& operator=(const U& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object.
Hex<T>& operator=(const Hex<T>& _other) = default;
/// Assigns the underlying object.
Hex<T>& operator=(Hex<T>&& _other) = default;
/// Assigns the underlying object.
template <class U>
auto& operator=(const Hex<U>& _other) {
value_ = _other.get();
return *this;
}
/// Assigns the underlying object.
template <class U>
auto& operator=(const std::string& _str) {
std::istringstream(_str) >> std::hex >> value_;
return *this;
}
/// Assigns the underlying object.
template <class U>
auto& operator=(Hex<U>&& _other) {
value_ = std::forward<T>(_other.value_);
return *this;
}
/// Necessary for the automated parsing to work.
std::string reflection() const {
std::stringstream stream;
stream << std::hex << value_;
return stream.str();
}
/// Assigns the underlying object.
void set(const Type& _value) { value_ = _value; }
/// Returns the underlying value as a string, alias for .reflection().
std::string str() const { return reflection(); }
/// Returns the underlying object.
Type& value() { return value_; }
/// Returns the underlying object.
const Type& value() const { return value_; }
/// The underlying value.
Type value_;
};
} // namespace rfl
#endif // RFL_FIELD_HPP_

View File

@@ -0,0 +1,371 @@
#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_

View File

@@ -0,0 +1,32 @@
#ifndef RFL_METAFIELD_HPP_
#define RFL_METAFIELD_HPP_
#include <string>
namespace rfl {
/// Contains meta-information about a field in a struct.
class MetaField {
public:
MetaField(const std::string& _name, const std::string& _type)
: name_(_name), type_(_type) {}
~MetaField() = default;
/// The name of the field we describe.
const std::string& name() const { return name_; };
/// The type of the field we describe.
const std::string& type() const { return type_; };
private:
/// The name of the field we describe.
std::string name_;
/// The type of the field we describe.
std::string type_;
};
} // namespace rfl
#endif // RFL_TAGGEDUNION_HPP_

View File

@@ -0,0 +1,681 @@
#ifndef RFL_NAMEDTUPLE_HPP_
#define RFL_NAMEDTUPLE_HPP_
#include <algorithm>
#include <string_view>
#include <type_traits>
#include <utility>
#include "Field.hpp"
#include "Literal.hpp"
#include "Tuple.hpp"
#include "apply.hpp"
#include "get.hpp"
#include "internal/StringLiteral.hpp"
#include "internal/find_index.hpp"
#include "internal/is_extra_fields.hpp"
#include "make_from_tuple.hpp"
#include "tuple_cat.hpp"
namespace rfl {
/// A named tuple behaves like a tuple,
/// but the fields have explicit names, which
/// allows for reflection.
/// IMPORTANT: We have two template specializations. One with fields, one
/// without fields.
template <class... FieldTypes>
class NamedTuple;
// ----------------------------------------------------------------------------
template <class... FieldTypes>
class NamedTuple {
template <int _i>
struct Index {};
static constexpr auto seq_ =
std::make_integer_sequence<int, sizeof...(FieldTypes)>();
public:
using Fields = rfl::Tuple<std::remove_cvref_t<FieldTypes>...>;
using Names = Literal<std::remove_cvref_t<FieldTypes>::name_...>;
using Values = rfl::Tuple<typename std::remove_cvref_t<FieldTypes>::Type...>;
public:
/// Construct from the values.
NamedTuple(typename std::remove_cvref<FieldTypes>::type::Type&&... _values)
: values_(
std::forward<typename std::remove_cvref<FieldTypes>::type::Type>(
_values)...) {}
/// Construct from the values.
NamedTuple(
const typename std::remove_cvref<FieldTypes>::type::Type&... _values)
: values_(rfl::make_tuple(_values...)) {}
/// Construct from the fields.
NamedTuple(FieldTypes&&... _fields)
: values_(rfl::make_tuple(std::move(_fields.value_)...)) {}
/// Construct from the fields.
NamedTuple(const FieldTypes&... _fields)
: values_(rfl::make_tuple(_fields.value_...)) {}
/// Construct from a tuple containing fields.
NamedTuple(rfl::Tuple<FieldTypes...>&& _tup)
: NamedTuple(rfl::make_from_tuple<NamedTuple<FieldTypes...>>(
std::forward<rfl::Tuple<FieldTypes...>>(_tup))) {}
/// Construct from a tuple containing fields.
NamedTuple(const rfl::Tuple<FieldTypes...>& _tup)
: NamedTuple(rfl::make_from_tuple<NamedTuple<FieldTypes...>>(_tup)) {}
/// Copy constructor.
NamedTuple(const NamedTuple<FieldTypes...>& _other) = default;
/// Move constructor.
NamedTuple(NamedTuple<FieldTypes...>&& _other) = default;
/// Copy constructor.
template <class... OtherFieldTypes>
NamedTuple(const NamedTuple<OtherFieldTypes...>& _other)
: NamedTuple(retrieve_fields(_other.fields(), seq_)) {}
/// Move constructor.
template <class... OtherFieldTypes>
NamedTuple(NamedTuple<OtherFieldTypes...>&& _other)
: NamedTuple(retrieve_fields(
std::forward<NamedTuple<OtherFieldTypes...>>(_other).fields(),
seq_)) {}
~NamedTuple() = default;
/// Returns a new named tuple with additional fields.
template <internal::StringLiteral _name, class FType, class... Tail>
auto add(Field<_name, FType>&& _head, Tail&&... _tail) && {
using Head = Field<_name, FType>;
if constexpr (sizeof...(Tail) > 0) {
return NamedTuple<FieldTypes..., std::remove_cvref_t<Head>>(
std::move(*this).make_fields(seq_, std::forward<Head>(_head)))
.add(std::forward<Tail>(_tail)...);
} else {
return NamedTuple<FieldTypes..., std::remove_cvref_t<Head>>(
std::move(*this).make_fields(seq_, std::forward<Head>(_head)));
}
}
/// Returns a new named tuple with additional fields.
template <internal::StringLiteral _name, class FType, class... Tail>
auto add(Field<_name, FType> _head, const Tail&... _tail) const& {
using Head = Field<_name, FType>;
if constexpr (sizeof...(Tail) > 0) {
return NamedTuple<FieldTypes..., std::remove_cvref_t<Head>>(
make_fields(seq_, _head))
.add(_tail...);
} else {
return NamedTuple<FieldTypes..., std::remove_cvref_t<Head>>(
make_fields(seq_, _head));
}
}
/// Template specialization for rfl::Tuple, so we can pass fields from other
/// named tuples.
template <class... TupContent, class... Tail>
auto add(rfl::Tuple<TupContent...>&& _tuple, Tail&&... _tail) && {
if constexpr (sizeof...(Tail) > 0) {
return std::move(*this)
.add_tuple(std::forward<rfl::Tuple<TupContent...>>(_tuple))
.add(std::forward<Tail>(_tail)...);
} else {
return std::move(*this).add_tuple(
std::forward<rfl::Tuple<TupContent...>>(_tuple));
}
}
/// Template specialization for rfl::Tuple, so we can pass fields from other
/// named tuples.
template <class... TupContent, class... Tail>
auto add(rfl::Tuple<TupContent...> _tuple, const Tail&... _tail) const& {
if constexpr (sizeof...(Tail) > 0) {
return add_tuple(std::move(_tuple)).add(_tail...);
} else {
return add_tuple(std::move(_tuple));
}
}
/// Template specialization for NamedTuple, so we can pass fields from other
/// named tuples.
template <class... TupContent, class... Tail>
auto add(NamedTuple<TupContent...>&& _named_tuple, Tail&&... _tail) && {
return std::move(*this).add(
std::forward<rfl::Tuple<TupContent...>>(
std::forward<NamedTuple<TupContent...>>(_named_tuple).fields()),
std::forward<Tail>(_tail)...);
}
/// Template specialization for NamedTuple, so we can pass fields from other
/// named tuples.
template <class... TupContent, class... Tail>
auto add(NamedTuple<TupContent...> _named_tuple,
const Tail&... _tail) const& {
return add(_named_tuple.fields(), _tail...);
}
/// Creates a new named tuple by applying the supplied function to
/// field. The function is expected to return a named tuple itself.
template <typename F>
auto and_then(const F& _f) && {
const auto transform_field = [&_f](auto... _fields) {
return rfl::tuple_cat(_f(std::move(_fields)).fields()...);
};
const auto to_nt = []<class... NewFields>(rfl::Tuple<NewFields...>&& _tup) {
return NamedTuple<NewFields...>(_tup);
};
auto new_fields = rfl::apply(transform_field, std::move(*this).fields());
return to_nt(std::move(new_fields));
}
/// Creates a new named tuple by applying the supplied function to
/// field. The function is expected to return a named tuple itself.
template <typename F>
auto and_then(const F& _f) const& {
const auto transform_field = [&_f](auto... _fields) {
return rfl::tuple_cat(_f(std::move(_fields)).fields()...);
};
const auto to_nt = []<class... NewFields>(rfl::Tuple<NewFields...>&& _tup) {
return NamedTuple<NewFields...>(_tup);
};
auto new_fields = rfl::apply(transform_field, std::move(fields()));
return to_nt(std::move(new_fields));
}
/// Invokes a callable object once for each field in order.
template <typename F>
void apply(F&& _f) const& {
const auto apply_to_field = [&_f](const auto&... fields) {
((_f(fields)), ...);
};
rfl::apply(apply_to_field, fields());
}
/// Returns a tuple containing the fields.
Fields fields() && { return std::move(*this).make_fields(seq_); }
/// Returns a tuple containing the fields.
Fields fields() const& { return make_fields(seq_); }
/// Gets a field by index.
template <int _index>
auto& get() {
return rfl::get<_index>(*this);
}
/// Gets a field by name.
template <internal::StringLiteral _field_name>
auto& get() {
return rfl::get<_field_name>(*this);
}
/// Gets a field by the field type.
template <class Field>
auto& get() {
return rfl::get<Field>(*this);
}
/// Gets a field by index.
template <int _index>
const auto& get() const {
return rfl::get<_index>(*this);
}
/// Gets a field by name.
template <internal::StringLiteral _field_name>
const auto& get() const {
return rfl::get<_field_name>(*this);
}
/// Gets a field by the field type.
template <class Field>
const auto& get() const {
return rfl::get<Field>(*this);
}
/// Returns the results wrapped in a field.
template <internal::StringLiteral _field_name>
auto get_field() const {
return rfl::make_field<_field_name>(rfl::get<_field_name>(*this));
}
/// Copy assignment operator.
NamedTuple<FieldTypes...>& operator=(
const NamedTuple<FieldTypes...>& _other) = default;
/// Move assignment operator.
NamedTuple<FieldTypes...>& operator=(
NamedTuple<FieldTypes...>&& _other) noexcept = default;
/// Equality operator
inline auto operator==(const rfl::NamedTuple<FieldTypes...>& _other) const {
return values() == _other.values();
}
/// Three-way comparison operator.
inline auto operator<=>(const rfl::NamedTuple<FieldTypes...>& _other) const {
return values() <=> _other.values();
}
/// Returns the number of fields. Note that this is not necessary the same
/// thing as .size(), because there might be rfl::ExtraFields, which are
/// simply counted as one entry by .size(), but are counted by individually by
/// .num_fields().
size_t num_fields() const {
if constexpr (pos_extra_fields() == -1) {
return size();
} else {
return calc_num_fields<pos_extra_fields()>();
}
}
/// The position of the extra fields, or -1 if there aren't any.
constexpr static int pos_extra_fields() { return pos_extra_fields_; }
/// Replaces one or several fields, returning a new version
/// with the non-replaced fields left unchanged.
template <internal::StringLiteral _name, class FType, class... OtherRFields>
auto replace(Field<_name, FType>&& _field,
OtherRFields&&... _other_fields) && {
using RField = Field<_name, FType>;
constexpr auto num_other_fields = sizeof...(OtherRFields);
if constexpr (num_other_fields == 0) {
return std::move(*this).template replace_value<RField>(_field.value_);
} else {
return std::move(*this)
.template replace_value<RField>(_field.value_)
.replace(std::forward<OtherRFields>(_other_fields)...);
}
}
/// Replaces one or several fields, returning a new version
/// with the non-replaced fields left unchanged.
template <internal::StringLiteral _name, class FType, class... OtherRFields>
auto replace(Field<_name, FType> _field,
const OtherRFields&... _other_fields) const& {
using RField = Field<_name, FType>;
constexpr auto num_other_fields = sizeof...(OtherRFields);
if constexpr (num_other_fields == 0) {
return replace_value<RField>(std::move(_field.value_));
} else {
return replace_value<RField>(std::move(_field.value_))
.replace(_other_fields...);
}
}
/// Template specialization for rfl::Tuple, so we can pass fields from other
/// named tuples.
template <class... TupContent, class... Tail>
auto replace(rfl::Tuple<TupContent...>&& _tuple, Tail&&... _tail) && {
if constexpr (sizeof...(Tail) > 0) {
return std::move(*this)
.replace_tuple(std::forward<rfl::Tuple<TupContent...>>(_tuple))
.replace(std::forward<Tail>(_tail)...);
} else {
return std::move(*this).replace_tuple(
std::forward<rfl::Tuple<TupContent...>>(_tuple));
}
}
/// Template specialization for rfl::Tuple, so we can pass fields from other
/// named tuples.
template <class... TupContent, class... Tail>
auto replace(rfl::Tuple<TupContent...> _tuple, const Tail&... _tail) const& {
if constexpr (sizeof...(Tail) > 0) {
return replace_tuple(std::move(_tuple)).replace(_tail...);
} else {
return replace_tuple(std::move(_tuple));
}
}
/// Template specialization for NamedTuple, so we can pass fields from other
/// named tuples.
template <class... TupContent, class... Tail>
auto replace(NamedTuple<TupContent...>&& _named_tuple, Tail&&... _tail) && {
return std::move(*this).replace(
std::forward<NamedTuple<TupContent...>>(_named_tuple).fields(),
std::forward<Tail>(_tail)...);
}
/// Template specialization for NamedTuple, so we can pass fields from other
/// named tuples.
template <class... TupContent, class... Tail>
auto replace(NamedTuple<TupContent...> _named_tuple,
const Tail&... _tail) const& {
return replace(_named_tuple.fields(), _tail...);
}
/// Returns the size of the named tuple
static constexpr size_t size() { return rfl::tuple_size_v<Values>; }
/// Creates a new named tuple by applying the supplied function to every
/// field.
template <typename F>
auto transform(const F& _f) && {
const auto transform_field = [&_f](auto... fields) {
return rfl::make_tuple(_f(std::move(fields))...);
};
const auto to_nt = []<class... NewFields>(rfl::Tuple<NewFields...>&& _tup) {
return NamedTuple<NewFields...>(_tup);
};
auto new_fields = rfl::apply(transform_field, std::move(*this).fields());
return to_nt(std::move(new_fields));
}
/// Creates a new named tuple by applying the supplied function to every
/// field.
template <typename F>
auto transform(const F& _f) const& {
const auto transform_field = [&_f](auto... fields) {
return rfl::make_tuple(_f(std::move(fields))...);
};
const auto to_nt = []<class... NewFields>(rfl::Tuple<NewFields...>&& _tup) {
return NamedTuple<NewFields...>(_tup);
};
auto new_fields = rfl::apply(transform_field, std::move(fields()));
return to_nt(std::move(new_fields));
}
/// Returns the underlying rfl::Tuple.
Values& values() { return values_; }
/// Returns the underlying rfl::Tuple.
const Values& values() const { return values_; }
private:
/// Adds the elements of a tuple to a newly created named tuple,
/// and other elements to a newly created named tuple.
template <class... TupContent>
constexpr auto add_tuple(rfl::Tuple<TupContent...>&& _tuple) && {
const auto a = [this](auto&&... _fields) {
return std::move(*this).add(std::forward<TupContent>(_fields)...);
};
return rfl::apply(a, std::forward<rfl::Tuple<TupContent...>>(_tuple));
}
/// Adds the elements of a tuple to a newly created named tuple,
/// and other elements to a newly created named tuple.
template <class... TupContent>
constexpr auto add_tuple(rfl::Tuple<TupContent...>&& _tuple) const& {
const auto a = [this](auto&&... _fields) {
return this->add(std::forward<TupContent>(_fields)...);
};
return rfl::apply(a, std::forward<rfl::Tuple<TupContent...>>(_tuple));
}
/// Unfortunately, MSVC forces us to do this...
template <int _pos>
size_t calc_num_fields() const {
const auto& extra_fields = get<_pos>();
if constexpr (std::is_pointer_v<
std::remove_cvref_t<decltype(extra_fields)>>) {
return size() + extra_fields->size() - 1;
} else {
return size() + extra_fields.size() - 1;
}
}
/// Finds the position of the extra fields, or -1 if there aren't any.
template <int _i = 0, int _idx = -1>
constexpr static int find_extra_fields() {
if constexpr (_i == size()) {
return _idx;
} else {
using FieldType = internal::nth_element_t<_i, FieldTypes...>;
constexpr bool is_extra_fields =
internal::is_extra_fields_v<typename FieldType::Type>;
static_assert(_idx == -1 || !is_extra_fields,
"There can only be one rfl::ExtraFields in any struct or "
"named tuple.");
if constexpr (is_extra_fields) {
return find_extra_fields<_i + 1, _i>();
} else {
return find_extra_fields<_i + 1, _idx>();
}
}
}
/// Generates the fields.
template <int... _is, class... AdditionalArgs>
auto make_fields(std::integer_sequence<int, _is...>,
AdditionalArgs&&... _args) && {
const auto wrap = [this]<int _i>(Index<_i>) {
using FieldType = internal::nth_element_t<_i, FieldTypes...>;
return FieldType(std::move(rfl::get<_i>(values_)));
};
return rfl::make_tuple(wrap(Index<_is>{})...,
std::forward<AdditionalArgs>(_args)...);
}
/// Generates the fields.
template <int... _is, class... AdditionalArgs>
auto make_fields(std::integer_sequence<int, _is...>,
AdditionalArgs... _args) const& {
const auto wrap = [this]<int _i>(Index<_i>) {
using FieldType = internal::nth_element_t<_i, FieldTypes...>;
return FieldType(rfl::get<_i>(values_));
};
return rfl::make_tuple(wrap(Index<_is>{})..., _args...);
}
/// Generates a new named tuple with one value replaced with a new value.
template <int _index, class V, class T, int... _is>
auto make_replaced(V&& _values, T&& _val,
std::integer_sequence<int, _is...>) const {
const auto wrap = [&]<int _i>(Index<_i>) {
if constexpr (_i == _index) {
return std::forward<T>(_val);
} else {
using FieldType = internal::nth_element_t<_i, FieldTypes...>;
using U = typename FieldType::Type;
return FieldType(std::forward<U>(rfl::get<_i>(_values)));
}
};
return NamedTuple<FieldTypes...>(wrap(Index<_is>{})...);
}
/// Replaced the field signified by the field type.
template <class Field, class T>
NamedTuple<FieldTypes...> replace_value(T&& _val) && {
using FieldType = std::remove_cvref_t<Field>;
constexpr auto index = internal::find_index<FieldType::name_, Fields>();
return make_replaced<index>(std::move(values_), std::forward<T>(_val),
seq_);
}
/// Replaced the field signified by the field type.
template <class Field, class T>
NamedTuple<FieldTypes...> replace_value(T&& _val) const& {
using FieldType = std::remove_cvref_t<Field>;
constexpr auto index = internal::find_index<FieldType::name_, Fields>();
auto values = values_;
return make_replaced<index>(std::move(values), std::forward<T>(_val), seq_);
}
/// Adds the elements of a tuple to a newly created named tuple,
/// and other elements to a newly created named tuple.
template <class... TupContent>
auto replace_tuple(rfl::Tuple<TupContent...>&& _tuple) && {
const auto r = [this](auto&&... _fields) {
return std::move(*this).replace(std::forward<TupContent>(_fields)...);
};
return rfl::apply(r, std::forward<rfl::Tuple<TupContent...>>(_tuple));
}
/// Adds the elements of a tuple to a newly created named tuple,
/// and other elements to a newly created named tuple.
template <class... TupContent>
auto replace_tuple(rfl::Tuple<TupContent...>&& _tuple) const& {
const auto r = [this](auto&&... _fields) {
return this->replace(std::forward<TupContent>(_fields)...);
};
return rfl::apply(r, std::forward<rfl::Tuple<TupContent...>>(_tuple));
}
/// Retrieves the fields from another tuple.
template <class... OtherFieldTypes, int... _is>
constexpr static Fields retrieve_fields(
rfl::Tuple<OtherFieldTypes...>&& _other_fields,
std::integer_sequence<int, _is...>) {
const auto get_field = [&]<int _i>(Index<_i>) {
constexpr auto field_name =
internal::nth_element_t<_i, FieldTypes...>::name_;
constexpr auto index =
internal::find_index<field_name, rfl::Tuple<OtherFieldTypes...>>();
using FieldType = internal::nth_element_t<_i, FieldTypes...>;
using T = std::remove_cvref_t<typename FieldType::Type>;
return FieldType(std::forward<T>(rfl::get<index>(_other_fields).value_));
};
return rfl::make_tuple(get_field(Index<_is>{})...);
}
private:
/// The values actually contained in the named tuple.
/// As you can see, a NamedTuple is just a normal tuple under-the-hood,
/// everything else is resolved at compile time. It should have no
/// runtime overhead over a normal rfl::Tuple.
Values values_;
/// The position of rfl::ExtraFields, or -1 if there aren't any.
constexpr static int pos_extra_fields_ = find_extra_fields();
};
// ----------------------------------------------------------------------------
/// We need a special template instantiation for empty named tuples.
template <>
class NamedTuple<> {
public:
using Fields = rfl::Tuple<>;
using Names = Literal<>;
using Values = rfl::Tuple<>;
NamedTuple(){};
~NamedTuple() = default;
/// Returns a new named tuple with additional fields.
template <internal::StringLiteral _name, class FType, class... Tail>
auto add(Field<_name, FType> _head, const Tail&... _tail) const {
if constexpr (sizeof...(Tail) > 0) {
return NamedTuple<Field<_name, FType>>(std::move(_head)).add(_tail...);
} else {
return NamedTuple<Field<_name, FType>>(std::move(_head));
}
}
/// Template specialization for rfl::Tuple, so we can pass fields from other
/// named tuples.
template <class... TupContent, class... Tail>
auto add(rfl::Tuple<TupContent...> _tuple, const Tail&... _tail) const {
if constexpr (sizeof...(Tail) > 0) {
return NamedTuple<TupContent...>(std::move(_tuple)).add(_tail...);
} else {
return NamedTuple<TupContent...>(std::move(_tuple));
}
}
/// Template specialization for NamedTuple, so we can pass fields from other
/// named tuples.
template <class... TupContent, class... Tail>
auto add(NamedTuple<TupContent...> _named_tuple, const Tail&... _tail) const {
return add(_named_tuple.fields(), _tail...);
}
/// Returns an empty named tuple.
template <typename F>
auto and_then(const F&) const {
return NamedTuple<>();
}
/// Does nothing at all.
template <typename F>
void apply(F&&) const {}
/// Returns an empty tuple.
auto fields() const { return rfl::Tuple(); }
/// Must always be 0.
size_t num_fields() const { return 0; }
/// Must always be -1.
constexpr static int pos_extra_fields() { return -1; }
/// Must always be 0.
static constexpr size_t size() { return 0; }
/// Returns an empty named tuple.
template <typename F>
auto transform(const F&) const {
return NamedTuple<>();
}
/// Returns an empty tuple.
auto values() const { return rfl::Tuple(); }
};
// ----------------------------------------------------------------------------
template <internal::StringLiteral _name1, class Type1,
internal::StringLiteral _name2, class Type2>
inline auto operator*(const rfl::Field<_name1, Type1>& _f1,
const rfl::Field<_name2, Type2>& _f2) {
return NamedTuple(_f1, _f2);
}
template <internal::StringLiteral _name, class Type, class... FieldTypes>
inline auto operator*(const NamedTuple<FieldTypes...>& _tup,
const rfl::Field<_name, Type>& _f) {
return _tup.add(_f);
}
template <internal::StringLiteral _name, class Type, class... FieldTypes>
inline auto operator*(const rfl::Field<_name, Type>& _f,
const NamedTuple<FieldTypes...>& _tup) {
return NamedTuple(_f).add(_tup);
}
template <class... FieldTypes1, class... FieldTypes2>
inline auto operator*(const NamedTuple<FieldTypes1...>& _tup1,
const NamedTuple<FieldTypes2...>& _tup2) {
return _tup1.add(_tup2);
}
template <internal::StringLiteral _name1, class Type1,
internal::StringLiteral _name2, class Type2>
inline auto operator*(rfl::Field<_name1, Type1>&& _f1,
rfl::Field<_name2, Type2>&& _f2) {
return NamedTuple(std::forward<Field<_name1, Type1>>(_f1),
std::forward<Field<_name2, Type2>>(_f2));
}
template <internal::StringLiteral _name, class Type, class... FieldTypes>
inline auto operator*(NamedTuple<FieldTypes...>&& _tup,
rfl::Field<_name, Type>&& _f) {
return _tup.add(std::forward<Field<_name, Type>>(_f));
}
template <internal::StringLiteral _name, class Type, class... FieldTypes>
inline auto operator*(rfl::Field<_name, Type>&& _f,
NamedTuple<FieldTypes...>&& _tup) {
return NamedTuple(std::forward<Field<_name, Type>>(_f))
.add(std::forward<NamedTuple<FieldTypes...>>(_tup));
}
template <class... FieldTypes1, class... FieldTypes2>
inline auto operator*(NamedTuple<FieldTypes1...>&& _tup1,
NamedTuple<FieldTypes2...>&& _tup2) {
return _tup1.add(std::forward<NamedTuple<FieldTypes2...>>(_tup2));
}
} // namespace rfl
#endif // RFL_NAMEDTUPLE_HPP_

View File

@@ -0,0 +1,19 @@
#ifndef RFL_NOEXTRAFIELDS_HPP_
#define RFL_NOEXTRAFIELDS_HPP_
namespace rfl {
/// This is a "fake" processor - it doesn't do much in itself, but its
/// inclusion instructs the parsers to return an error when there are extra
/// fields instead of ignoring them.
struct NoExtraFields {
public:
template <class StructType>
static auto process(auto&& _named_tuple) {
return _named_tuple;
}
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,18 @@
#ifndef RFL_NOFIELDNAMES_HPP_
#define RFL_NOFIELDNAMES_HPP_
namespace rfl {
/// This is a "fake" processor - it doesn't do much in itself, but its
/// inclusion instructs the parsers to strip field names.
struct NoFieldNames {
public:
template <class StructType>
static auto process(auto&& _named_tuple) {
return _named_tuple;
}
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,18 @@
#ifndef RFL_NOOPTIONALS_HPP_
#define RFL_NOOPTIONALS_HPP_
namespace rfl {
/// This is a "fake" processor - it doesn't do much in itself, but its
/// inclusion instructs the parsers to require the inclusion of all fields.
struct NoOptionals {
public:
template <class StructType>
static auto process(auto&& _named_tuple) {
return _named_tuple;
}
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,248 @@
#ifndef RFL_OBJECT_HPP_
#define RFL_OBJECT_HPP_
#include <algorithm>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "Result.hpp"
namespace rfl {
/// Used to embed additional fields for which the names cannot be known in
/// advance and can therefore not be encoded in the struct.
template <class T>
class Object {
public:
using DataType = std::vector<std::pair<std::string, T>>;
using Type = T;
/// We want this to behave as similarly to C++ standard containers as
/// possible.
using key_type = std::string;
using mapped_type = T;
using value_type = std::pair<std::string, T>;
using size_type = typename DataType::size_type;
using difference_type = typename DataType::size_type;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = typename DataType::pointer;
using const_pointer = typename DataType::const_pointer;
using iterator = typename DataType::iterator;
using const_iterator = typename DataType::const_iterator;
using reverse_iterator = typename DataType::reverse_iterator;
using const_reverse_iterator = typename DataType::const_reverse_iterator;
Object() : data_(), i_(0) {}
Object(const Object<T>& _f) = default;
Object(Object<T>&& _f) noexcept = default;
~Object() = default;
/// Iterator to the beginning.
auto begin() { return data_.begin(); }
/// Iterator to the beginning.
auto begin() const { return data_.begin(); }
/// Const iterator to the beginning.
auto cbegin() const { return data_.cbegin(); }
/// Iterator to the end.
auto end() { return data_.end(); }
/// Iterator to the end.
auto end() const { return data_.end(); }
/// Const iterator to the end.
auto cend() const { return data_.cend(); }
/// Reverse iterator.
auto rbegin() { return data_.rbegin(); }
/// Reverse iterator.
auto rbegin() const { return data_.rbegin(); }
/// Const reverse iterator.
auto crbegin() const { return data_.crbegin(); }
/// Reverse iterator.
auto rend() { return data_.rend(); }
/// Reverse iterator.
auto rend() const { return data_.rend(); }
/// Const reverse iterator.
auto crend() const { return data_.crend(); }
Object<T>& operator=(const Object<T>& _f) = default;
Object<T>& operator=(Object<T>&& _f) = default;
/// Whether the object is empty.
auto empty() const { return data_.size() == 0; }
/// The number of elements currently inside the object.
auto size() const { return data_.size(); }
std::size_t count(const key_type& key) const { return std::count_if(cbegin(), cend(), [&](const auto& p) { return p.first == key; }); }
/// The maximum possible size.
auto max_size() const { return data_.max_size(); }
/// Inserts a new element at the end.
template <class... Args>
void insert(const Args&... _values) {
(data_.push_back(_values), ...);
i_ = 0;
}
/// Inserts a new element at the end.
template <class... Args>
void insert(Args&&... _values) {
(data_.emplace_back(std::move(_values)), ...);
i_ = 0;
}
/// Inserts a new element at the end.
void insert(const std::string& _k, const T& _v) {
insert(std::make_pair(_k, _v));
}
/// Inserts a new element at the end.
void insert(const std::string& _k, T&& _v) {
insert(std::make_pair(_k, std::move(_v)));
}
/// Inserts a new element at the end.
void insert(std::string&& _k, T&& _v) {
insert(std::make_pair(std::move(_k), std::move(_v)));
}
/// Inserts a new element at the end.
void insert(const std::string_view& _k, const T& _v) {
insert(std::make_pair(std::string(_k), _v));
}
/// Inserts a new element at the end.
void insert(const std::string_view& _k, T&& _v) {
insert(std::make_pair(std::string(_k), std::move(_v)));
}
/// Alias for insert that primarily exists for compatability with standard
/// containers.
template <class... Args>
void emplace(Args&&... _args) {
insert(std::forward<Args>(_args)...);
}
/// Alias for insert that primarily exists for compatability with standard
/// containers.
template <class... Args>
void emplace(const Args&... _args) {
insert(_args...);
}
/// Inserts several new elements at the end.
template <class InputIt>
void insert_range(InputIt _first, InputIt _last) {
for (auto it = _first; it != _last; ++it) {
insert(*it);
}
}
/// Inserts several new elements at the end.
template <class RangeType>
void insert_range(RangeType _range) {
for (const auto& val : _range) {
insert(val);
}
}
/// Returns the element signified by the key or creates a new one.
T& operator[](const std::string& _key) {
const auto i = find(_key);
if (i != size()) {
return data_[i].second;
}
data_.emplace_back(std::make_pair(_key, T()));
i_ = 0;
return data_.back().second;
}
/// Returns the element signified by the key or creates a new one.
T& operator[](std::string&& _key) {
const auto i = find(_key);
if (i != size()) {
return data_[i].second;
}
data_.emplace_back(std::make_pair(std::move(_key), T()));
i_ = 0;
return data_.back().second;
}
/// Deletes all elements.
void clear() {
data_.clear();
i_ = 0;
}
/// Returns the element signified by the key or throws an exception.
T& at(const std::string& _key) {
const auto i = find(_key);
if (i == size()) {
throw std::runtime_error("Key named '" + _key + "' not found.");
}
return data_[i].second;
}
/// Returns the element signified by the key or throws an exception.
const T& at(const std::string& _key) const {
const auto i = find(_key);
if (i == size()) {
throw std::runtime_error("Key named '" + _key + "' not found.");
}
return data_[i].second;
}
/// Returns a result wrapping the element signified by the key.
Result<T> get(const std::string& _key) const noexcept {
const auto i = find(_key);
if (i == size()) {
return error("Key named '" + _key + "' not found.");
}
return data_[i].second;
}
private:
size_t find(const std::string& _key) const {
for (size_t i = i_; i < size(); ++i) {
if (data_[i].first == _key) {
i_ = i + 1;
return i;
}
}
for (size_t i = 0; i < i_; ++i) {
if (data_[i].first == _key) {
i_ = i + 1;
return i;
}
}
return size();
}
private:
DataType data_;
/// Allows faster access
mutable size_t i_;
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,130 @@
#ifndef RFL_OCT_HPP_
#define RFL_OCT_HPP_
#include <algorithm>
#include <ios>
#include <sstream>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
namespace rfl {
/// Used to define a field in the NamedTuple.
template <class T>
requires std::is_integral_v<T>
struct Oct {
/// The underlying type.
using Type = T;
using ReflectionType = std::string;
Oct() : value_(0) {}
Oct(const Type& _value) : value_(_value) {}
Oct(Oct<T>&& _other) noexcept = default;
Oct(const Oct<T>& _other) = default;
template <class U>
Oct(const Oct<U>& _other) : value_(_other.get()) {}
template <class U>
Oct(Oct<U>&& _other) : value_(_other.get()) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Oct(const U& _value) : value_(_value) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Oct(U&& _value) noexcept : value_(std::forward<U>(_value)) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Oct(const Oct<U>& _other) : value_(_other.value()) {}
Oct(const std::string& _str) {
std::istringstream(_str) >> std::oct >> value_;
}
~Oct() = default;
/// Returns the underlying object.
const Type& get() const { return value_; }
/// Returns the underlying object.
Type& operator()() { return value_; }
/// Returns the underlying object.
const Type& operator()() const { return value_; }
/// Assigns the underlying object.
auto& operator=(const Type& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object.
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
auto& operator=(const U& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object.
Oct<T>& operator=(const Oct<T>& _other) = default;
/// Assigns the underlying object.
Oct<T>& operator=(Oct<T>&& _other) = default;
/// Assigns the underlying object.
template <class U>
auto& operator=(const Oct<U>& _other) {
value_ = _other.get();
return *this;
}
/// Assigns the underlying object.
template <class U>
auto& operator=(const std::string& _str) {
std::istringstream(_str) >> std::oct >> value_;
return *this;
}
/// Assigns the underlying object.
template <class U>
auto& operator=(Oct<U>&& _other) {
value_ = std::forward<T>(_other.value_);
return *this;
}
/// Necessary for the automated parsing to work.
std::string reflection() const {
std::stringstream stream;
stream << std::oct << value_;
return stream.str();
}
/// Assigns the underlying object.
void set(const Type& _value) { value_ = _value; }
/// Returns the underlying value as a string, alias for .reflection().
std::string str() const { return reflection(); }
/// Returns the underlying object.
Type& value() { return value_; }
/// Returns the underlying object.
const Type& value() const { return value_; }
/// The underlying value.
Type value_;
};
} // namespace rfl
#endif // RFL_FIELD_HPP_

View File

@@ -0,0 +1,71 @@
#ifndef RFL_ONEOF_HPP_
#define RFL_ONEOF_HPP_
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "Result.hpp"
#include "parsing/schema/ValidationType.hpp"
namespace rfl {
/// Requires that all of the contraints C and Cs be true.
template <class C, class... Cs>
struct OneOf {
template <class T>
static rfl::Result<T> validate(const T& _value) noexcept {
return validate_impl<T, C, Cs...>(_value, {});
}
template <class T>
static parsing::schema::ValidationType to_schema() {
using ValidationType = parsing::schema::ValidationType;
const auto types = std::vector<ValidationType>(
{C::template to_schema<T>(), Cs::template to_schema<T>()...});
return ValidationType{ValidationType::OneOf{.types_ = types}};
}
private:
static std::string make_error_message(const std::vector<Error>& _errors) {
std::stringstream stream;
stream << "Expected exactly 1 out of " << sizeof...(Cs) + 1
<< " validations to pass, but " << sizeof...(Cs) + 1 - _errors.size()
<< " of them did. The following errors were generated: ";
for (size_t i = 0; i < _errors.size(); ++i) {
stream << "\n" << i + 1 << ") " << _errors.at(i).what();
}
return stream.str();
}
template <class T, class Head, class... Tail>
static rfl::Result<T> validate_impl(const T& _value,
std::vector<Error> _errors) {
return Head::validate(_value)
.and_then([&](auto&& _result) -> rfl::Result<T> {
if constexpr (sizeof...(Tail) == 0) {
if (_errors.size() == sizeof...(Cs)) {
return _value;
// The AI suggests return std::forward<decltype(_result)>(_result);
// is it correct in this context?
}
return error(make_error_message(_errors));
} else {
return validate_impl<T, Tail...>(_value, std::move(_errors));
}
})
.or_else([&](auto&& _err) -> rfl::Result<T> {
_errors.emplace_back(std::move(_err));
if constexpr (sizeof...(Tail) == 0) {
return error(make_error_message(_errors));
} else {
return validate_impl<T, Tail...>(_value, std::move(_errors));
}
});
}
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,14 @@
#ifndef RFL_PATTERN_HPP_
#define RFL_PATTERN_HPP_
#include "PatternValidator.hpp"
#include "Validator.hpp"
namespace rfl {
template <internal::StringLiteral _regex, internal::StringLiteral _name>
using Pattern = Validator<std::string, PatternValidator<_regex, _name>>;
} // namespace rfl
#endif

View File

@@ -0,0 +1,46 @@
#ifndef RFL_PATTERNVALIDATOR_HPP_
#define RFL_PATTERNVALIDATOR_HPP_
#include <sstream>
#include <string>
#if __has_include(<ctre.hpp>)
#include <ctre.hpp>
#else
#include "thirdparty/ctre.hpp"
#endif
#include "Literal.hpp"
#include "Result.hpp"
#include "internal/StringLiteral.hpp"
#include "parsing/schema/ValidationType.hpp"
namespace rfl {
template <internal::StringLiteral _regex, internal::StringLiteral _name>
struct PatternValidator {
using Name = Literal<_name>;
using Regex = Literal<_regex>;
static Result<std::string> validate(const std::string& _str) noexcept {
if (ctre::match<ctll::fixed_string<_regex.length>{
ctll::construct_from_pointer, _regex.arr_.data()}>(_str)) {
return _str;
} else {
std::stringstream stream;
stream << "String '" << _str << "' did not match format '" << _name.str()
<< "': '" << _regex.str() << "'.";
return error(stream.str());
}
}
template <class T>
static parsing::schema::ValidationType to_schema() {
using ValidationType = parsing::schema::ValidationType;
return ValidationType{ValidationType::Regex{.pattern_ = Regex().str()}};
}
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,83 @@
#ifndef RFL_INTERNAL_PROCESSORS_HPP_
#define RFL_INTERNAL_PROCESSORS_HPP_
#include <type_traits>
#include <utility>
#include "internal/is_add_tags_to_variants_v.hpp"
#include "internal/is_allow_raw_ptrs_v.hpp"
#include "internal/is_default_if_missing_v.hpp"
#include "internal/is_no_extra_fields_v.hpp"
#include "internal/is_no_field_names_v.hpp"
#include "internal/is_no_optionals_v.hpp"
#include "internal/is_underlying_enums_v.hpp"
namespace rfl {
template <class... Ps>
struct Processors;
template <>
struct Processors<> {
static constexpr bool add_tags_to_variants_ = false;
static constexpr bool add_namespaced_tags_to_variants_ = false;
static constexpr bool allow_raw_ptrs_ = false;
static constexpr bool all_required_ = false;
static constexpr bool default_if_missing_ = false;
static constexpr bool no_extra_fields_ = false;
static constexpr bool no_field_names_ = false;
static constexpr bool underlying_enums_ = false;
template <class T, class NamedTupleType>
static auto process(NamedTupleType&& _named_tuple) {
return _named_tuple;
}
};
template <class Head, class... Tail>
struct Processors<Head, Tail...> {
static constexpr bool add_tags_to_variants_ =
std::disjunction_v<internal::is_add_tags_to_variants<Head>,
internal::is_add_tags_to_variants<Tail>...>;
static constexpr bool add_namespaced_tags_to_variants_ =
std::disjunction_v<internal::is_add_namespaced_tags_to_variants<Head>,
internal::is_add_namespaced_tags_to_variants<Tail>...>;
static constexpr bool allow_raw_ptrs_ =
std::disjunction_v<internal::is_allow_raw_ptrs<Head>,
internal::is_allow_raw_ptrs<Tail>...>;
static constexpr bool all_required_ =
std::disjunction_v<internal::is_no_optionals<Head>,
internal::is_no_optionals<Tail>...>;
static constexpr bool default_if_missing_ =
std::disjunction_v<internal::is_default_if_missing<Head>,
internal::is_default_if_missing<Tail>...>;
static constexpr bool no_extra_fields_ =
std::disjunction_v<internal::is_no_extra_fields<Head>,
internal::is_no_extra_fields<Tail>...>;
static constexpr bool no_field_names_ =
std::disjunction_v<internal::is_no_field_names<Head>,
internal::is_no_field_names<Tail>...>;
static constexpr bool underlying_enums_ =
std::disjunction_v<internal::is_underlying_enums<Head>,
internal::is_underlying_enums<Tail>...>;
template <class T, class NamedTupleType>
static auto process(NamedTupleType&& _named_tuple) {
static_assert(!add_tags_to_variants_ || !add_namespaced_tags_to_variants_,
"You cannot add both rfl::AddTagsToVariants and "
"rfl::AddNamespacedTagsToVariants.");
return Processors<Tail...>::template process<T>(
Head::template process<T>(std::move(_named_tuple)));
}
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,146 @@
#ifndef RFL_REF_HPP_
#define RFL_REF_HPP_
#include <memory>
#include <stdexcept>
#include "Result.hpp"
namespace rfl {
/// The Ref class behaves very similarly to the shared_ptr, but unlike the
/// unique_ptr, it is 100% guaranteed to be filled at all times (unless the user
/// tries to access it after calling std::move does something else that is
/// clearly bad practice).
template <class T>
class Ref {
public:
/// The default way of creating new references is
/// Ref<T>::make(...) or make_ref<T>(...).
template <class... Args>
static Ref<T> make(Args&&... _args) {
return Ref<T>(std::make_shared<T>(std::forward<Args>(_args)...));
}
/// You can generate them from shared_ptrs as well, in which case it will
/// return an Error, if the shared_ptr is not set.
static Result<Ref<T>> make(std::shared_ptr<T>&& _ptr) {
if (!_ptr) {
return error("std::shared_ptr was a nullptr.");
}
return Ref<T>(std::move(_ptr));
}
/// You can generate them from shared_ptrs as well, in which case it will
/// return an Error, if the shared_ptr is not set.
static Result<Ref<T>> make(const std::shared_ptr<T>& _ptr) {
if (!_ptr) {
return error("std::shared_ptr was a nullptr.");
}
return Ref<T>(_ptr);
}
Ref() : ptr_(std::make_shared<T>()) {}
Ref(const Ref<T>& _other) = default;
Ref(Ref<T>&& _other) = default;
template <class U>
Ref(const Ref<U>& _other) : ptr_(_other.ptr()) {}
template <class U>
Ref(Ref<U>&& _other) noexcept
: ptr_(std::forward<std::shared_ptr<U>>(_other.ptr())) {}
~Ref() = default;
/// Returns a pointer to the underlying object
T* get() const { return ptr_.get(); }
/// Returns the underlying object.
T& operator*() { return *ptr_; }
/// Returns the underlying object.
T& operator*() const { return *ptr_; }
/// Returns the underlying object.
T* operator->() { return ptr_.get(); }
/// Returns the underlying object.
T* operator->() const { return ptr_.get(); }
/// Returns the underlying shared_ptr
std::shared_ptr<T>& ptr() { return ptr_; }
/// Returns the underlying shared_ptr
const std::shared_ptr<T>& ptr() const { return ptr_; }
/// Copy assignment operator.
template <class U>
Ref<T>& operator=(const Ref<U>& _other) {
ptr_ = _other.ptr();
return *this;
}
/// Move assignment operator
template <class U>
Ref<T>& operator=(Ref<U>&& _other) noexcept {
ptr_ = std::forward<std::shared_ptr<U>>(_other.ptr());
return *this;
}
/// Move assignment operator
Ref<T>& operator=(Ref<T>&& _other) noexcept = default;
/// Copy assignment operator
Ref<T>& operator=(const Ref<T>& _other) = default;
private:
/// Only make is allowed to use this constructor.
explicit Ref(std::shared_ptr<T>&& _ptr) : ptr_(std::move(_ptr)) {}
/// Only make is allowed to use this constructor.
explicit Ref(const std::shared_ptr<T>& _ptr) : ptr_(_ptr) {}
private:
/// The underlying shared_ptr_
std::shared_ptr<T> ptr_;
};
/// Generates a new Ref<T>.
template <class T, class... Args>
auto make_ref(Args&&... _args) {
return Ref<T>::make(std::forward<Args>(_args)...);
}
template <class T1, class T2>
inline auto operator<=>(const Ref<T1>& _t1, const Ref<T2>& _t2) {
return _t1.ptr() <=> _t2.ptr();
}
template <class CharT, class Traits, class T>
inline std::basic_ostream<CharT, Traits>& operator<<(
std::basic_ostream<CharT, Traits>& _os, const Ref<T>& _b) {
_os << _b.get();
return _os;
}
} // namespace rfl
namespace std {
template <class T>
struct hash<rfl::Ref<T>> {
size_t operator()(const rfl::Ref<T>& _r) const {
return std::hash<std::shared_ptr<T>>{}(_r.ptr());
}
};
template <class T>
inline void swap(rfl::Ref<T>& _r1, rfl::Ref<T>& _r2) {
return swap(_r1.ptr(), _r2.ptr());
}
} // namespace std
#endif // RFL_REF_HPP_

View File

@@ -0,0 +1,141 @@
#ifndef RFL_RENAME_HPP_
#define RFL_RENAME_HPP_
#include <algorithm>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include "Literal.hpp"
#include "default.hpp"
#include "internal/StringLiteral.hpp"
namespace rfl {
/// Used to assign a new name to a field, which is different from the name
/// inside the struct.
template <internal::StringLiteral _name, class T>
struct Rename {
/// The underlying type.
using Type = T;
/// The name of the field.
using Name = rfl::Literal<_name>;
Rename() : value_(Type()) {}
Rename(const Type& _value) : value_(_value) {}
Rename(Type&& _value) noexcept : value_(std::move(_value)) {}
Rename(Rename<_name, T>&& _field) noexcept = default;
Rename(const Rename<_name, Type>& _field) = default;
template <class U>
Rename(const Rename<_name, U>& _field) : value_(_field.get()) {}
template <class U>
Rename(Rename<_name, U>&& _field) : value_(_field.get()) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Rename(const U& _value) : value_(_value) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Rename(U&& _value) noexcept : value_(std::forward<U>(_value)) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Rename(const Rename<_name, U>& _field) : value_(_field.value()) {}
/// Assigns the underlying object to its default value.
template <class U = Type,
typename std::enable_if<std::is_default_constructible_v<U>,
bool>::type = true>
Rename(const Default&) : value_(Type()) {}
~Rename() = default;
/// The name of the field, for internal use.
constexpr static const internal::StringLiteral name_ = _name;
/// Returns the underlying object.
const Type& get() const { return value_; }
/// Returns the underlying object.
Type& operator()() { return value_; }
/// Returns the underlying object.
const Type& operator()() const { return value_; }
/// Assigns the underlying object.
auto& operator=(const Type& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object.
auto& operator=(Type&& _value) noexcept {
value_ = std::move(_value);
return *this;
}
/// Assigns the underlying object.
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
auto& operator=(const U& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object to its default value.
template <class U = Type,
typename std::enable_if<std::is_default_constructible_v<U>,
bool>::type = true>
auto& operator=(const Default&) {
value_ = Type();
return *this;
}
/// Assigns the underlying object.
Rename<_name, T>& operator=(const Rename<_name, T>& _field) = default;
/// Assigns the underlying object.
Rename<_name, T>& operator=(Rename<_name, T>&& _field) = default;
/// Assigns the underlying object.
template <class U>
auto& operator=(const Rename<_name, U>& _field) {
value_ = _field.get();
return *this;
}
/// Assigns the underlying object.
template <class U>
auto& operator=(Rename<_name, U>&& _field) {
value_ = std::forward<T>(_field.value_);
return *this;
}
/// Assigns the underlying object.
void set(const Type& _value) { value_ = _value; }
/// Assigns the underlying object.
void set(Type&& _value) { value_ = std::move(_value); }
/// Returns the underlying object.
Type& value() { return value_; }
/// Returns the underlying object.
const Type& value() const { return value_; }
/// The underlying value.
Type value_;
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,447 @@
#ifndef RFL_RESULT_HPP_
#define RFL_RESULT_HPP_
#ifdef REFLECTCPP_USE_STD_EXPECTED
#include <expected>
#endif
#include <array>
#include <functional>
#include <new>
#include <stdexcept>
#include <string>
#include <type_traits>
namespace rfl {
/// Defines the error class to be returned when something went wrong
class Error {
public:
Error(const std::string& _what) : what_(_what) {}
Error(std::string&& _what) : what_(std::move(_what)) {}
Error(const Error& e) = default;
Error(Error&& e) = default;
Error& operator=(const Error&) = default;
Error& operator=(Error&&) = default;
/// Returns the error message, equivalent to .what() in std::exception.
const std::string& what() const & { return what_; }
/// Moves the error message out of Error object and leaves what_ in a moved from state
std::string what() && { return std::move(what_); }
private:
/// Documents what went wrong
std::string what_;
};
/// To be returned when there is nothing to return, but there might be an error.
struct Nothing {};
/// This implementation is for cases where std::expected is defined
#ifdef REFLECTCPP_USE_STD_EXPECTED
template <class E>
using Unexpected = std::unexpected<E>;
template <class T>
using Result = std::expected<T, rfl::Error>;
/// This implementation is for cases where std::expected is not defined
#else // REFLECTCPP_USE_STD_EXPECTED
template <class E>
struct Unexpected {
Unexpected(E&& _err) : err_{std::forward<E>(_err)} {}
Unexpected(const E& _err) : err_{_err} {}
Unexpected(Unexpected&&) = default;
Unexpected(const Unexpected&) = default;
Unexpected& operator=(Unexpected&&) = default;
Unexpected& operator=(const Unexpected&) = default;
const E& error() const& { return err_; }
E&& error() && { return std::move(err_); }
E& error() & { return err_; }
private:
E err_;
};
/// The Result class is used for monadic error handling.
template <class T>
class Result {
static_assert(!std::is_same<T, Error>(), "The result type cannot be Error.");
using TOrErr = std::array<unsigned char, std::max(sizeof(T), sizeof(Error))>;
public:
// using Type = T;
using value_type = T;
using error_type = rfl::Error;
Result(const T& _val) : success_(true) { new (&get_t()) T(_val); }
Result(T&& _val) noexcept : success_(true) {
new (&get_t()) T(std::move(_val));
}
Result(const Unexpected<Error>& _err) : success_(false) {
new (&get_err()) Error(_err.error());
}
Result(Unexpected<Error>&& _err) : success_(false) {
new (&get_err()) Error(std::move(_err.error()));
}
Result(Result<T>&& _other) noexcept : success_(_other.success_) {
move_from_other(_other);
}
Result(const Result<T>& _other) : success_(_other.success_) {
copy_from_other(_other);
}
template <class U, typename std::enable_if<std::is_convertible_v<U, T>,
bool>::type = true>
Result(Result<U>&& _other) : success_(_other && true) {
auto temp = std::forward<Result<U> >(_other).transform(
[](U&& _u) { return T(std::forward<U>(_u)); });
move_from_other(temp);
}
template <class U, typename std::enable_if<std::is_convertible_v<U, T>,
bool>::type = true>
Result(const Result<U>& _other) : success_(_other && true) {
auto temp = _other.transform([](const U& _u) { return T(_u); });
move_from_other(temp);
}
~Result() { destroy(); }
/// Monadic operation - F must be a function of type T -> Result<U>.
template <class F>
auto and_then(const F& _f) && {
/// Result_U is expected to be of type Result<U>.
using Result_U = typename std::invoke_result<F, T>::type;
if (success_) {
return Result_U(_f(std::move(*this).get_t()));
} else {
return Result_U(std::move(*this).get_err());
}
}
/// Monadic operation - F must be a function of type T -> Result<U>.
template <class F>
auto and_then(const F& _f) const& {
/// Result_U is expected to be of type Result<U>.
using Result_U = typename std::invoke_result<F, T>::type;
if (success_) {
return Result_U(_f(get_t()));
} else {
return Result_U(get_err());
}
}
/// Returns true if the result contains a value, false otherwise.
operator bool() const noexcept { return success_; }
/// Allows access to the underlying value. Careful: Will result in undefined
/// behavior, if the result contains an error.
T&& operator*() && noexcept { return std::move(*this).get_t(); }
/// Allows access to the underlying value. Careful: Will result in undefined
/// behavior, if the result contains an error.
T& operator*() & noexcept { return get_t(); }
/// Allows read access to the underlying value. Careful: Will result in
/// undefined behavior, if the result contains an error.
const T& operator*() const& noexcept { return get_t(); }
/// Assigns the underlying object.
Result<T>& operator=(const Result<T>& _other) {
if (this == &_other) {
return *this;
}
destroy();
success_ = _other.success_;
copy_from_other(_other);
return *this;
}
/// Assigns the underlying object.
Result<T>& operator=(Result<T>&& _other) noexcept {
if (this == &_other) {
return *this;
}
destroy();
success_ = _other.success_;
move_from_other(_other);
return *this;
}
Result<T>& operator=(Unexpected<Error>&& _err) noexcept {
destroy();
success_ = false;
new (&get_err()) Error(_err.error());
return *this;
}
Result<T>& operator=(const Unexpected<Error>& _err) noexcept {
destroy();
success_ = false;
new (&get_err()) Error(_err.error());
return *this;
}
/// Assigns the underlying object.
template <class U, typename std::enable_if<std::is_convertible_v<U, T>,
bool>::type = true>
auto& operator=(const Result<U>& _other) {
const auto to_t = [](const U& _u) -> T { return _u; };
t_or_err_ = _other.transform(to_t).t_or_err_;
return *this;
}
/// Expects a function that takes of type Error -> Result<T> and returns
/// Result<T>.
template <class F>
Result<T> or_else(const F& _f) && {
if (success_) {
return std::move(*this).get_t();
} else {
return _f(std::move(*this).get_err());
}
}
/// Expects a function that takes of type Error -> Result<T> and returns
/// Result<T>.
template <class F>
Result<T> or_else(const F& _f) const& {
if (success_) {
return get_t();
} else {
return _f(get_err());
}
}
/// Functor operation - F must be a function of type T -> U.
template <class F>
auto transform(const F& _f) && {
/// Result_U is expected to be of type Result<U>.
using U = std::invoke_result_t<F, T>;
if (success_) {
return rfl::Result<U>(_f(std::move(*this).get_t()));
} else {
return rfl::Result<U>(rfl::Unexpected(std::move(*this).get_err()));
}
}
/// Functor operation - F must be a function of type T -> U.
template <class F>
auto transform(const F& _f) const& {
/// Result_U is expected to be of type Result<U>.
using U = typename std::invoke_result<F, T>::type;
if (success_) {
return rfl::Result<U>(_f(get_t()));
} else {
return rfl::Result<U>(get_err());
}
}
/// Returns the value if the result does not contain an error, throws an
/// exceptions if not. Similar to .unwrap() in Rust.
T&& value() && {
if (success_) {
return std::move(*this).get_t();
} else {
throw std::runtime_error(get_err().what());
}
}
/// Returns the value if the result does not contain an error, throws an
/// exceptions if not. Similar to .unwrap() in Rust.
T& value() & {
if (success_) {
return get_t();
} else {
throw std::runtime_error(get_err().what());
}
}
/// Returns the value if the result does not contain an error, throws an
/// exceptions if not. Similar to .unwrap() in Rust.
const T& value() const& {
if (success_) {
return get_t();
} else {
throw std::runtime_error(get_err().what());
}
}
/// Returns the value or a default.
T&& value_or(T&& _default) && noexcept {
if (success_) {
return std::move(*this).get_t();
} else {
return std::forward<T>(_default);
}
}
/// Returns the value or a default.
T value_or(const T& _default) const& noexcept {
if (success_) {
return get_t();
} else {
return _default;
}
}
template <class G = rfl::Error>
rfl::Error error_or(G&& _default) && {
if (success_) {
return std::forward<G>(_default);
} else {
return std::move(*this).get_err();
}
}
// As specified by the standard :
// https://en.cppreference.com/w/cpp/utility/expected
// Observers
template <class G = rfl::Error>
rfl::Error error_or(G&& _default) const& {
if (success_) {
return std::forward<G>(_default);
} else {
return get_err();
}
}
bool has_value() const noexcept { return success_; }
Error& error() && {
if (success_) throw std::runtime_error("Expected does not contain value");
return std::move(*this).get_err();
}
Error& error() & {
if (success_) throw std::runtime_error("Expected does not contain value");
return get_err();
}
const Error& error() const& {
if (success_) throw std::runtime_error("Expected does not contain value");
return get_err();
}
T* operator->() noexcept { return &get_t(); }
const T* operator->() const noexcept { return &get_t(); }
template <class F>
rfl::Result<T> transform_error(F&& f) && {
static_assert(
std::is_same<std::invoke_result_t<F, rfl::Error>, rfl::Error>(),
"A function passed to transform_error must return an error.");
if (!has_value()) {
return rfl::Result<T>{std::invoke(f, std::move(*this).get_err())};
} else {
return rfl::Result<T>{std::move(*this).value()};
}
}
template <class F>
rfl::Result<T> transform_error(F&& f) const& {
static_assert(
std::is_same<std::invoke_result_t<F, rfl::Error>, rfl::Error>(),
"A function passed to transform_error must return an error.");
if (!has_value()) {
return rfl::Result<T>{std::invoke(f, get_err())};
} else {
return rfl::Result<T>{value()};
}
}
private:
void copy_from_other(const Result<T>& _other) {
if (success_) {
new (&get_t()) T(_other.get_t());
} else {
new (&get_err()) Error(_other.get_err());
}
}
void destroy() {
if (success_) {
if constexpr (std::is_destructible_v<std::remove_cv_t<T> >) {
get_t().~T();
}
} else {
get_err().~Error();
}
}
T&& get_t() && noexcept {
return std::move(*std::launder(reinterpret_cast<T*>(t_or_err_.data())));
}
T& get_t() & noexcept {
return *std::launder(reinterpret_cast<T*>(t_or_err_.data()));
}
const T& get_t() const& noexcept {
return *std::launder(reinterpret_cast<const T*>(t_or_err_.data()));
}
Error&& get_err() && noexcept {
return std::move(*std::launder(reinterpret_cast<Error*>(t_or_err_.data())));
}
Error& get_err() & noexcept {
return *std::launder(reinterpret_cast<Error*>(t_or_err_.data()));
}
const Error& get_err() const& noexcept {
return *std::launder(reinterpret_cast<const Error*>(t_or_err_.data()));
}
void move_from_other(Result<T>& _other) noexcept {
if (success_) {
new (&get_t()) T(std::move(_other.get_t()));
} else {
new (&get_err()) Error(std::move(_other.get_err()));
}
}
/// Signifies whether this was a success.
bool success_;
/// The underlying data, can either be T or Error.
alignas(std::max(alignof(T), alignof(Error))) TOrErr t_or_err_;
};
#endif
/// Shorthand for unexpected error.
inline Unexpected<Error> error(const std::string& _what) {
return Unexpected<Error>(Error(_what));
}
inline Unexpected<Error> error(std::string&& _what) {
return Unexpected<Error>(Error(std::move(_what)));
}
/// Shorthand for unexpected error.
inline Unexpected<Error> error(const Error& _err) {
return Unexpected<Error>(_err);
}
} // namespace rfl
#endif

View File

@@ -0,0 +1,35 @@
#ifndef RFL_SIZE_HPP_
#define RFL_SIZE_HPP_
#include <map>
#include "Ref.hpp"
#include "Result.hpp"
#include "parsing/schema/ValidationType.hpp"
namespace rfl {
template <class V>
struct Size {
template <class T>
static rfl::Result<T> validate(const T& _t) {
const auto to_t = [&](const auto&) { return _t; };
const auto embellish_error = [](const auto& _err) -> Error {
return Error("Size validation failed: " + _err.what());
};
return V::validate(_t.size()).transform(to_t).transform_error(
embellish_error);
}
template <class T>
static parsing::schema::ValidationType to_schema() {
using ValidationType = parsing::schema::ValidationType;
return ValidationType{ValidationType::Size{
.size_limit_ =
rfl::Ref<ValidationType>::make(V::template to_schema<size_t>())}};
}
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,20 @@
#ifndef RFL_SKIP_HPP_
#define RFL_SKIP_HPP_
#include "internal/Skip.hpp"
namespace rfl {
template <class T>
using Skip = internal::Skip<T, true, true>;
template <class T>
using SkipSerialization = internal::Skip<T, true, false>;
template <class T>
using SkipDeserialization = internal::Skip<T, false, true>;
} // namespace rfl
#endif

View File

@@ -0,0 +1,39 @@
#ifndef RFL_SNAKECASETOCAMELCASE_HPP_
#define RFL_SNAKECASETOCAMELCASE_HPP_
#include "Field.hpp"
#include "internal/is_rename.hpp"
#include "internal/transform_snake_case.hpp"
namespace rfl {
struct SnakeCaseToCamelCase {
public:
/// Replaces all instances of snake_case field names with camelCase.
template <class StructType>
static auto process(const auto& _named_tuple) {
return _named_tuple.transform([]<class FieldType>(const FieldType& _f) {
if constexpr (FieldType::name() != "xml_content" &&
!internal::is_rename_v<typename FieldType::Type>) {
return handle_one_field(_f);
} else {
return _f;
}
});
}
private:
/// Applies the logic to a single field.
template <class FieldType>
static auto handle_one_field(const FieldType& _f) {
using NewFieldType =
Field<internal::transform_snake_case<FieldType::name_,
/*capitalize=*/false>(),
typename FieldType::Type>;
return NewFieldType(_f.value());
}
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,40 @@
#ifndef RFL_SNAKECASETOPASCALCASE_HPP_
#define RFL_SNAKECASETOPASCALCASE_HPP_
#include "Field.hpp"
#include "internal/is_rename.hpp"
#include "internal/transform_snake_case.hpp"
namespace rfl {
struct SnakeCaseToPascalCase {
public:
/// Replaces all instances of snake_case field names with PascalCase.
template <class StructType>
static auto process(const auto& _named_tuple) {
const auto handle_one = []<class FieldType>(const FieldType& _f) {
if constexpr (FieldType::name() != "xml_content" &&
!internal::is_rename_v<typename FieldType::Type>) {
return handle_one_field(_f);
} else {
return _f;
}
};
return _named_tuple.transform(handle_one);
}
private:
/// Applies the logic to a single field.
template <class FieldType>
static auto handle_one_field(const FieldType& _f) {
using NewFieldType =
Field<internal::transform_snake_case<FieldType::name_,
/*capitalize=*/true>(),
typename FieldType::Type>;
return NewFieldType(_f.value());
}
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,146 @@
#ifndef RFL_TAGGEDUNION_HPP_
#define RFL_TAGGEDUNION_HPP_
#include "Variant.hpp"
#include "define_literal.hpp"
#include "internal/Getter.hpp"
#include "internal/StringLiteral.hpp"
#include "internal/tag_t.hpp"
namespace rfl {
// https://serde.rs/enum-representations.html
template <internal::StringLiteral _discriminator, class... Ts>
struct TaggedUnion {
static constexpr internal::StringLiteral discrimininator_ = _discriminator;
/// The type of the underlying variant.
using VariantType = rfl::Variant<Ts...>;
TaggedUnion(const VariantType& _variant) : variant_(_variant) {}
TaggedUnion(VariantType&& _variant) noexcept
: variant_(std::move(_variant)) {}
TaggedUnion(const TaggedUnion<_discriminator, Ts...>& _tagged_union) =
default;
TaggedUnion(TaggedUnion<_discriminator, Ts...>&& _tagged_union) noexcept =
default;
template <class T,
typename std::enable_if<std::is_convertible_v<T, VariantType>,
bool>::type = true>
TaggedUnion(const T& _t) : variant_(_t) {}
template <class T,
typename std::enable_if<std::is_convertible_v<T, VariantType>,
bool>::type = true>
TaggedUnion(T&& _t) noexcept : variant_(std::forward<T>(_t)) {}
~TaggedUnion() = default;
/// Assigns the underlying object.
TaggedUnion<_discriminator, Ts...>& operator=(const VariantType& _variant) {
variant_ = _variant;
return *this;
}
/// Assigns the underlying object.
TaggedUnion<_discriminator, Ts...>& operator=(VariantType&& _variant) {
variant_ = std::move(_variant);
return *this;
}
/// Assigns the underlying object.
template <class T,
typename std::enable_if<std::is_convertible_v<T, VariantType>,
bool>::type = true>
TaggedUnion<_discriminator, Ts...>& operator=(T&& _variant) {
variant_ = std::forward<T>(_variant);
return *this;
}
/// Assigns the underlying object.
template <class T,
typename std::enable_if<std::is_convertible_v<T, VariantType>,
bool>::type = true>
TaggedUnion<_discriminator, Ts...>& operator=(const T& _variant) {
variant_ = _variant;
return *this;
}
/// Assigns the underlying object.
TaggedUnion<_discriminator, Ts...>& operator=(
const TaggedUnion<_discriminator, Ts...>& _other) = default;
/// Assigns the underlying object.
TaggedUnion<_discriminator, Ts...>& operator=(
TaggedUnion<_discriminator, Ts...>&& _other) = default;
/// Returns the underlying variant.
VariantType& variant() { return variant_; }
/// Returns the underlying variant.
const VariantType& variant() const { return variant_; }
/// Applies function _f to all underlying alternatives.
template <class F>
auto visit(F&& _f)
-> decltype(std::declval<VariantType>().visit(std::declval<F&&>())) {
return variant_.visit(std::forward<F>(_f));
}
/// Applies function _f to all underlying alternatives.
template <class F>
auto visit(F&& _f) const
-> decltype(std::declval<VariantType>().visit(std::declval<F&&>())) {
return variant_.visit(std::forward<F>(_f));
}
/// The underlying variant - a TaggedUnion is a thin wrapper
/// around a variant that is mainly used for parsing.
VariantType variant_;
};
template <typename T>
concept TaggedUnionBased = requires(T t) {
[]<internal::StringLiteral _discriminator, typename... Args>(
TaggedUnion<_discriminator, Args...> const&) {}(t);
};
template <class T>
struct PossibleTags;
template <internal::StringLiteral _discriminator, class... Ts>
struct PossibleTags<TaggedUnion<_discriminator, Ts...>> {
using Type = define_literal_t<internal::tag_t<_discriminator, Ts>...>;
};
template <class T>
using possible_tags_t = typename PossibleTags<T>::Type;
template <internal::StringLiteral _discriminator, class... Ts>
bool operator==(
const TaggedUnion<_discriminator, Ts...>& lhs,
const TaggedUnion<_discriminator, Ts...>& rhs
) {
return (lhs.variant().index() == rhs.variant().index()) &&
lhs.variant().visit(
[&rhs](const auto& l) {
return rhs.variant().visit(
[&l](const auto& r) -> bool {
if constexpr (std::is_same_v<std::decay_t<decltype(l)>, std::decay_t<decltype(r)>>)
return l == r;
else
return false;
}
);
}
);
}
} // namespace rfl
#endif // RFL_TAGGEDUNION_HPP_

View File

@@ -0,0 +1,121 @@
#ifndef RFL_TIMESTAMP_HPP_
#define RFL_TIMESTAMP_HPP_
#include <ctime>
#include <iomanip>
#include <sstream>
#include <stdexcept>
#include <string>
#include "Literal.hpp"
#include "Result.hpp"
#include "internal/StringLiteral.hpp"
namespace rfl {
/// For serializing and deserializing time stamps.
template <internal::StringLiteral _format>
class Timestamp {
constexpr static const internal::StringLiteral format_ = _format;
public:
using Format = rfl::Literal<_format>;
using ReflectionType = std::string;
Timestamp() : tm_(std::tm{}) {}
Timestamp(const char* _str) : tm_(std::tm{}) {
const auto r = strptime(_str, _format.str().c_str(), &tm_);
if (r == NULL) {
throw std::runtime_error("String '" + std::string(_str) +
"' did not match format '" + Format().str() +
"'.");
}
}
Timestamp(const std::string& _str) : Timestamp(_str.c_str()) {}
Timestamp(const std::tm& _tm) : tm_(_tm) {}
Timestamp(const time_t _t) : tm_(std::tm{}) {
auto t = _t;
#if defined(_MSC_VER) || defined(__MINGW32__)
gmtime_s(&tm_, &t);
#else
gmtime_r(&t, &tm_);
#endif
}
~Timestamp() = default;
/// Returns a result containing the timestamp when successful or an Error
/// otherwise.
static Result<Timestamp> from_string(const char* _str) noexcept {
try {
return Timestamp(_str);
} catch (std::exception& e) {
return error(e.what());
}
}
/// Returns a result containing the timestamp when successful or an Error
/// otherwise.
static Result<Timestamp> from_string(const std::string& _str) {
return from_string(_str.c_str());
}
/// Returns a result containing the timestamp when successful or an Error
/// otherwise.
static Result<Timestamp> make(const auto& _str) noexcept {
return from_string(_str);
}
/// Necessary for the serialization to work.
ReflectionType reflection() const {
char outstr[200];
strftime(outstr, 200, format_.str().c_str(), &tm_);
return std::string(outstr);
}
/// Expresses the underlying timestamp as a string.
std::string str() const { return reflection(); }
/// Trivial accessor to the underlying time stamp.
std::tm& tm() { return tm_; }
/// Trivial (const) accessor to the underlying time stamp.
const std::tm& tm() const { return tm_; }
/// Returns a UTC time represented by a time_t type.
time_t to_time_t() const {
auto tm = tm_;
#if defined(_MSC_VER) || defined(__MINGW32__)
return _mkgmtime(&tm);
#else
return static_cast<time_t>(timegm(&tm) - tm_.tm_gmtoff);
#endif
}
private:
#if defined(_MSC_VER) || defined(__MINGW32__)
// This workaround is necessary, because strptime is not available on Windows.
char* strptime(const char* _s, const char* _f, std::tm* _tm) {
std::istringstream input(_s);
input.imbue(std::locale(setlocale(LC_ALL, nullptr)));
input >> std::get_time(_tm, _f);
if (input.fail()) {
return NULL;
}
return (char*)(_s + input.tellg());
}
#endif
private:
/// The underlying time stamp.
std::tm tm_;
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,267 @@
#ifndef RFL_TUPLE_HPP_
#define RFL_TUPLE_HPP_
#include <array>
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
#include "internal/nth_element_t.hpp"
#include "internal/ptr_cast.hpp"
#include "internal/tuple/calculate_positions.hpp"
namespace rfl {
template <class... FieldTypes>
class Tuple;
template <>
class Tuple<> {
public:
Tuple() {}
};
template <class... Types>
class Tuple {
static constexpr size_t size_ = sizeof...(Types);
static constexpr auto positions_ =
internal::tuple::calculate_positions<Types...>();
static constexpr auto seq_ = std::make_integer_sequence<int, size_>{};
static constexpr unsigned int num_bytes_ = std::get<size_>(positions_);
using DataType = std::array<unsigned char, num_bytes_>;
public:
Tuple(const Types&... _t) { copy_from_types(_t..., seq_); }
Tuple(Types&&... _t) noexcept { move_from_types(std::move(_t)..., seq_); }
Tuple() : Tuple(Types()...) {}
Tuple(const Tuple& _other) { copy_from_other(_other, seq_); }
Tuple(Tuple&& _other) noexcept { move_from_other(std::move(_other), seq_); }
~Tuple() { destroy_if_necessary(seq_); }
/// Gets an element by index.
template <int _index>
constexpr auto& get() {
using Type = internal::nth_element_t<_index, Types...>;
return *internal::ptr_cast<Type*>(data_.data() + pos<_index>());
}
/// Gets an element by index.
template <int _index>
constexpr const auto& get() const {
using Type = internal::nth_element_t<_index, Types...>;
return *internal::ptr_cast<const Type*>(data_.data() + pos<_index>());
}
/// Assigns the underlying object.
Tuple& operator=(const Tuple& _other) {
if (this == &_other) {
return *this;
}
auto temp = Tuple(_other);
destroy_if_necessary(seq_);
move_from_other(std::move(temp), seq_);
return *this;
}
/// Assigns the underlying object.
Tuple& operator=(Tuple&& _other) noexcept {
if (this == &_other) {
return *this;
}
destroy_if_necessary(seq_);
move_from_other(std::move(_other), seq_);
return *this;
}
/// Equality operator.
template <class... OtherTypes>
bool operator==(const Tuple<OtherTypes...>& _other) const noexcept {
static_assert(sizeof...(Types) == sizeof...(OtherTypes),
"The size of the two tuples must be the same.");
const auto is_same = [&]<int _i>(std::integral_constant<int, _i>) -> bool {
return this->get<_i>() == _other.template get<_i>();
};
return [&]<int... _is>(std::integer_sequence<int, _is...>) {
return (true && ... && is_same(std::integral_constant<int, _is>{}));
}(std::make_integer_sequence<int, sizeof...(Types)>());
}
/// Three-way comparison operator.
template <class... OtherTypes>
auto operator<=>(const Tuple<OtherTypes...>& _other) const noexcept {
static_assert(sizeof...(Types) == sizeof...(OtherTypes),
"The size of the two tuples must be the same.");
const auto compare = [&]<int _i>(std::strong_ordering* _ordering,
std::integral_constant<int, _i>) {
if (*_ordering != std::strong_ordering::equivalent &&
this->get<_i>() != _other.template get<_i>()) {
*_ordering = (this->get<_i>() <=> _other.template get<_i>());
}
};
return [&]<int... _is>(std::integer_sequence<int, _is...>) {
auto ordering = std::strong_ordering::equivalent;
(compare(&ordering, std::integral_constant<int, _is>{}), ...);
return ordering;
}(std::make_integer_sequence<int, sizeof...(Types)>());
}
private:
template <int... _is>
void copy_from_other(const Tuple& _other,
std::integer_sequence<int, _is...>) {
const auto copy_one = [this]<int _i>(const auto& _other,
std::integral_constant<int, _i>) {
using Type = internal::nth_element_t<_i, Types...>;
::new (static_cast<void*>(data_.data() + pos<_i>()))
Type(_other.template get<_i>());
};
(copy_one(_other, std::integral_constant<int, _is>{}), ...);
}
template <int... _is>
void copy_from_types(const Types&... _types,
std::integer_sequence<int, _is...>) {
const auto copy_one = [this]<int _i>(const auto& _t,
std::integral_constant<int, _i>) {
using Type = internal::nth_element_t<_i, Types...>;
::new (static_cast<void*>(data_.data() + pos<_i>())) Type(_t);
};
(copy_one(_types, std::integral_constant<int, _is>{}), ...);
}
template <int... _is>
void destroy_if_necessary(std::integer_sequence<int, _is...>) {
const auto destroy_one = [](auto& _t) {
using Type = std::remove_cvref_t<decltype(_t)>;
if constexpr (std::is_destructible_v<Type>) {
_t.~Type();
}
};
(destroy_one(get<_is>()), ...);
}
template <int... _is>
void move_from_other(Tuple&& _other, std::integer_sequence<int, _is...>) {
const auto move_one = [this]<int _i>(auto&& _other,
std::integral_constant<int, _i>) {
using Type = internal::nth_element_t<_i, Types...>;
::new (static_cast<void*>(data_.data() + pos<_i>()))
Type(std::move(_other.template get<_i>()));
};
(move_one(_other, std::integral_constant<int, _is>{}), ...);
}
template <int... _is>
void move_from_types(Types&&... _types, std::integer_sequence<int, _is...>) {
const auto move_one = [this]<int _i>(auto&& _t,
std::integral_constant<int, _i>) {
using Type = internal::nth_element_t<_i, Types...>;
::new (static_cast<void*>(data_.data() + pos<_i>())) Type(std::move(_t));
};
(move_one(std::move(_types), std::integral_constant<int, _is>{}), ...);
}
template <int _i>
static consteval unsigned int pos() {
return std::get<_i>(positions_);
}
private:
/// The underlying data, can be any of the underlying types.
alignas(Types...) DataType data_;
};
/// Gets an element by index.
template <int _index, class... Types>
constexpr auto& get(rfl::Tuple<Types...>& _tup) {
return _tup.template get<_index>();
}
/// Gets an element by index.
template <int _index, class... Types>
constexpr const auto& get(const rfl::Tuple<Types...>& _tup) {
return _tup.template get<_index>();
}
/// Gets an element by index.
template <int _index, class... Types>
constexpr auto& get(std::tuple<Types...>& _tup) {
return std::get<_index>(_tup);
}
/// Gets an element by index.
template <int _index, class... Types>
constexpr const auto& get(const std::tuple<Types...>& _tup) {
return std::get<_index>(_tup);
}
template <class... Types>
auto make_tuple(Types&&... _args) {
return rfl::Tuple<std::decay_t<Types>...>(std::forward<Types>(_args)...);
}
template <int N, class T>
struct tuple_element;
template <int N, class... Ts>
struct tuple_element<N, rfl::Tuple<Ts...>> {
using type = internal::nth_element_t<N, Ts...>;
};
template <int N, class... Ts>
struct tuple_element<N, std::tuple<Ts...>> {
using type = internal::nth_element_t<N, Ts...>;
};
template <int N, class T>
using tuple_element_t =
typename rfl::tuple_element<N, std::remove_cvref_t<T>>::type;
template <class T>
struct tuple_size;
template <class... Ts>
struct tuple_size<rfl::Tuple<Ts...>> {
static constexpr auto value = sizeof...(Ts);
};
template <class... Ts>
struct tuple_size<std::tuple<Ts...>> {
static constexpr auto value = sizeof...(Ts);
};
template <class T>
inline constexpr auto tuple_size_v =
rfl::tuple_size<std::remove_cvref_t<T>>::value;
} // namespace rfl
namespace std {
/// Gets an element by index.
template <int _index, class... Types>
constexpr auto& get(rfl::Tuple<Types...>& _tup) {
return _tup.template get<_index>();
}
/// Gets an element by index.
template <int _index, class... Types>
constexpr const auto& get(const rfl::Tuple<Types...>& _tup) {
return _tup.template get<_index>();
}
} // namespace std
#endif

View File

@@ -0,0 +1,18 @@
#ifndef RFL_UNDERLYINGENUMS_HPP_
#define RFL_UNDERLYINGENUMS_HPP_
namespace rfl {
/// This is a 'fake' processor - it doesn't do much by itself, but its
/// its inclusion instructs parsers not to convert enum types to strings, but to integers
struct UnderlyingEnums {
public:
template <class StructType>
static auto process(auto&& _named_tuple) {
return _named_tuple;
}
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,135 @@
#ifndef RFL_VALIDATOR_HPP_
#define RFL_VALIDATOR_HPP_
#include <cstddef>
#include <exception>
#include <functional>
#include <type_traits>
#include <utility>
#include "AllOf.hpp"
#include "Result.hpp"
#include "internal/HasValidation.hpp"
namespace rfl {
template <class T, class V, class... Vs>
requires internal::HasValidation<AllOf<V, Vs...>, T>
struct Validator {
public:
using ReflectionType = T;
using ValidationType =
std::conditional_t<sizeof...(Vs) == 0, V, AllOf<V, Vs...>>;
/// Exception-free validation.
static Result<Validator> from_value(const T& _value) noexcept {
try {
return Validator(_value);
} catch (std::exception& e) {
return error(e.what());
}
}
Validator() : value_(ValidationType::validate(T()).value()) {}
Validator(Validator&& _other) noexcept = default;
Validator(const Validator& _other) = default;
Validator(T&& _value) : value_(ValidationType::validate(_value).value()) {}
Validator(const T& _value)
: value_(ValidationType::validate(_value).value()) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, T>,
bool>::type = true>
Validator(U&& _value)
: value_(ValidationType::validate(T(std::forward<U>(_value))).value()) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, T>,
bool>::type = true>
Validator(const U& _value)
: value_(ValidationType::validate(T(_value)).value()) {}
~Validator() = default;
/// Assigns the underlying object.
auto& operator=(const T& _value) {
value_ = ValidationType::validate(_value).value();
return *this;
}
/// Assigns the underlying object.
auto& operator=(T&& _value) {
value_ = ValidationType::validate(std::forward<T>(_value)).value();
return *this;
}
/// Assigns the underlying object.
Validator& operator=(const Validator& _other) =
default;
/// Assigns the underlying object.
Validator& operator=(Validator&& _other) noexcept =
default;
/// Assigns the underlying object.
template <class U, typename std::enable_if<std::is_convertible_v<U, T>,
bool>::type = true>
auto& operator=(U&& _value) noexcept {
value_ = ValidationType::validate(T(std::forward<U>(_value))).value();
return *this;
}
/// Assigns the underlying object.
template <class U, typename std::enable_if<std::is_convertible_v<U, T>,
bool>::type = true>
auto& operator=(const U& _value) {
value_ = ValidationType::validate(T(_value)).value();
return *this;
}
/// Equality operator other Validators.
bool operator==(const Validator& _other) const {
return value() == _other.value();
}
/// Exposes the underlying value.
T& value() { return value_; }
/// Exposes the underlying value.
const T& value() const { return value_; }
/// Necessary for the serialization to work.
const T& reflection() const { return value_; }
private:
/// The underlying value.
T value_;
};
template <class T, class V, class... Vs>
inline auto operator<=>(const Validator<T, V, Vs...>& _v1,
const Validator<T, V, Vs...>& _v2) {
return _v1.value() <=> _v2.value();
}
template <class T, class V, class... Vs>
inline auto operator<=>(const Validator<T, V, Vs...>& _v, const T& _t) {
return _v.value() <=> _t;
}
} // namespace rfl
namespace std {
template <class T, class V, class... Vs>
struct hash<rfl::Validator<T, V, Vs...>> {
size_t operator()(const rfl::Validator<T, V, Vs...>& _v) const {
return hash<T>()(_v.value());
}
};
} // namespace std
#endif

View File

@@ -0,0 +1,559 @@
#ifndef RFL_VARIANT_HPP_
#define RFL_VARIANT_HPP_
#include <array>
#include <cstdint>
#include <limits>
#include <optional>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include "internal/element_index.hpp"
#include "internal/nth_element_t.hpp"
#include "internal/ptr_cast.hpp"
#include "internal/variant/find_max_size.hpp"
#include "internal/variant/is_alternative_type.hpp"
#include "internal/variant/result_t.hpp"
namespace rfl {
template <class... AlternativeTypes>
class Variant {
static constexpr auto max_size_wrapper_ =
internal::variant::find_max_size<AlternativeTypes...>();
static constexpr unsigned long num_bytes_ = max_size_wrapper_.size_;
using DataType = std::array<unsigned char, num_bytes_>;
using IndexType =
std::conditional_t<sizeof...(AlternativeTypes) <=
std::numeric_limits<std::uint8_t>::max(),
std::uint8_t, std::uint16_t>;
static constexpr IndexType size_ = sizeof...(AlternativeTypes);
template <class F>
using result_t = internal::variant::result_t<F, AlternativeTypes...>;
template <IndexType _i>
using Index = std::integral_constant<IndexType, _i>;
template <class T>
struct TypeWrapper {};
public:
Variant() : index_(IndexType()), data_(DataType()) {
using FirstAlternative = internal::nth_element_t<0, AlternativeTypes...>;
move_from_type(FirstAlternative());
}
Variant(const Variant& _other)
: index_(IndexType()), data_(DataType()) {
copy_from_other(_other);
}
Variant(Variant&& _other) noexcept
: index_(IndexType()), data_(DataType()) {
move_from_other(std::move(_other));
}
template <class T,
typename std::enable_if<internal::variant::is_alternative_type<
T, AlternativeTypes...>(),
bool>::type = true>
Variant(const T& _t) : index_(IndexType()), data_(DataType()) {
copy_from_type(_t);
}
template <class T,
typename std::enable_if<internal::variant::is_alternative_type<
T, AlternativeTypes...>(),
bool>::type = true>
Variant(T&& _t) noexcept : index_(IndexType()), data_(DataType()) {
move_from_type(std::forward<T>(_t));
}
~Variant() { destroy_if_necessary(); }
/// Emplaces a new element into the variant.
template <class T, class... Args>
constexpr T& emplace(Args&&... _args) {
auto t = T{std::forward<Args>(_args)...};
destroy_if_necessary();
move_from_type(std::move(t));
return *internal::ptr_cast<T*>(data_.data());
}
/// Emplaces a new element into the variant.
template <int _i, class... Args>
constexpr auto& emplace(Args&&... _args) {
using T = internal::nth_element_t<_i, AlternativeTypes...>;
return emplace<T>(std::move(_args)...);
}
/// Returns the index of the element currently held.
constexpr int index() const noexcept { return index_; }
/// Assigns the underlying object.
template <class T,
typename std::enable_if<internal::variant::is_alternative_type<
T, AlternativeTypes...>(),
bool>::type = true>
Variant& operator=(const T& _t) {
auto temp = Variant(_t);
destroy_if_necessary();
move_from_other(std::move(temp));
return *this;
}
/// Assigns the underlying object.
template <class T,
typename std::enable_if<internal::variant::is_alternative_type<
T, AlternativeTypes...>(),
bool>::type = true>
Variant& operator=(T&& _t) noexcept {
destroy_if_necessary();
move_from_type(std::forward<T>(_t));
return *this;
}
/// Assigns the underlying object.
Variant& operator=(const Variant& _other) {
if (this == &_other) {
return *this;
}
auto temp = Variant(_other);
destroy_if_necessary();
move_from_other(std::move(temp));
return *this;
}
/// Assigns the underlying object.
Variant& operator=(Variant&& _other) noexcept {
if (this == &_other) {
return *this;
}
destroy_if_necessary();
move_from_other(std::move(_other));
return *this;
}
/// Swaps the content with the other variant.
void swap(Variant& _other) noexcept {
if (this == &_other) {
return;
}
auto temp = Variant(std::move(*this));
move_from_other(std::move(_other));
_other = std::move(temp);
}
template <class F>
result_t<F> visit(F&& _f) {
using ResultType = result_t<F>;
if constexpr (std::is_same_v<ResultType, void>) {
bool visited = false;
do_visit_no_result(std::forward<F>(_f), &visited,
std::make_integer_sequence<IndexType, size_>());
} else if constexpr (std::is_reference_v<ResultType>) {
std::remove_reference_t<ResultType>* res = nullptr;
do_visit_with_reference(std::forward<F>(_f), &res,
std::make_integer_sequence<IndexType, size_>());
return *res;
} else {
auto res = std::optional<ResultType>();
do_visit_with_result(std::forward<F>(_f), &res,
std::make_integer_sequence<IndexType, size_>());
return std::move(*res);
}
}
template <class F>
result_t<F> visit(F&& _f) const {
using ResultType = result_t<F>;
if constexpr (std::is_same_v<ResultType, void>) {
bool visited = false;
do_visit_no_result(std::forward<F>(_f), &visited,
std::make_integer_sequence<IndexType, size_>());
} else if constexpr (std::is_reference_v<ResultType>) {
std::remove_reference_t<ResultType>* res = nullptr;
do_visit_with_reference(std::forward<F>(_f), &res,
std::make_integer_sequence<IndexType, size_>());
return *res;
} else {
auto res = std::optional<ResultType>();
do_visit_with_result(std::forward<F>(_f), &res,
std::make_integer_sequence<IndexType, size_>());
return std::move(*res);
}
}
private:
void copy_from_other(const Variant& _other) {
const auto copy_one = [this](const auto& _t) { this->copy_from_type(_t); };
_other.visit(copy_one);
}
template <class T>
void copy_from_other_type(const T& _t) {
bool set = false;
const auto copy_one = [&, this]<class AltType>(const T& _t,
const TypeWrapper<AltType>) {
if constexpr (std::is_convertible_v<T, AltType>) {
if (!set) {
move_from_type(AltType(_t));
set = true;
}
}
};
(copy_one(_t, TypeWrapper<AlternativeTypes>{}), ...);
}
template <class T>
void copy_from_type(const T& _t) noexcept {
using CurrentType = std::remove_cvref_t<decltype(_t)>;
index_ =
internal::element_index<CurrentType,
std::remove_cvref_t<AlternativeTypes>...>();
new (data_.data()) CurrentType(_t);
}
void destroy_if_necessary() {
const auto destroy_one = [](auto& _t) {
using T = std::remove_cvref_t<decltype(_t)>;
if constexpr (std::is_destructible_v<T>) {
_t.~T();
}
};
visit(destroy_one);
}
template <class F, IndexType... _is>
void do_visit_no_result(F& _f, bool* _visited,
std::integer_sequence<IndexType, _is...>) {
auto visit_one = [this]<IndexType _i>(const F& _f, bool* _visited,
Index<_i>) {
if (!*_visited && index_ == _i) {
_f(get_alternative<_i>());
*_visited = true;
}
};
(visit_one(_f, _visited, Index<_is>{}), ...);
}
template <class F, IndexType... _is>
void do_visit_no_result(F& _f, bool* _visited,
std::integer_sequence<IndexType, _is...>) const {
auto visit_one = [this]<IndexType _i>(const F& _f, bool* _visited,
Index<_i>) {
if (!*_visited && index_ == _i) {
_f(get_alternative<_i>());
*_visited = true;
}
};
(visit_one(_f, _visited, Index<_is>{}), ...);
}
template <class F, IndexType... _is>
void do_visit_no_result(const F& _f, bool* _visited,
std::integer_sequence<IndexType, _is...>) {
const auto visit_one = [this]<IndexType _i>(const F& _f, bool* _visited,
Index<_i>) {
if (!*_visited && index_ == _i) {
_f(get_alternative<_i>());
*_visited = true;
}
};
(visit_one(_f, _visited, Index<_is>{}), ...);
}
template <class F, IndexType... _is>
void do_visit_no_result(const F& _f, bool* _visited,
std::integer_sequence<IndexType, _is...>) const {
const auto visit_one = [this]<IndexType _i>(const F& _f, bool* _visited,
Index<_i>) {
if (!*_visited && index_ == _i) {
_f(get_alternative<_i>());
*_visited = true;
}
};
(visit_one(_f, _visited, Index<_is>{}), ...);
}
template <class F, class ResultType, IndexType... _is>
void do_visit_with_result(F& _f, std::optional<ResultType>* _result,
std::integer_sequence<IndexType, _is...>) {
auto visit_one = [this]<IndexType _i>(const F& _f,
std::optional<ResultType>* _result,
Index<_i>) {
if (!*_result && index_ == _i) {
_result->emplace(_f(get_alternative<_i>()));
}
};
(visit_one(_f, _result, Index<_is>{}), ...);
}
template <class F, class ResultType, IndexType... _is>
void do_visit_with_result(F& _f, std::optional<ResultType>* _result,
std::integer_sequence<IndexType, _is...>) const {
auto visit_one = [this]<IndexType _i>(const F& _f,
std::optional<ResultType>* _result,
Index<_i>) {
if (!*_result && index_ == _i) {
_result->emplace(_f(get_alternative<_i>()));
}
};
(visit_one(_f, _result, Index<_is>{}), ...);
}
template <class F, class ResultType, IndexType... _is>
void do_visit_with_result(const F& _f, std::optional<ResultType>* _result,
std::integer_sequence<IndexType, _is...>) {
const auto visit_one = [this]<IndexType _i>(const F& _f,
std::optional<ResultType>* _result,
Index<_i>) {
if (!*_result && index_ == _i) {
_result->emplace(_f(get_alternative<_i>()));
}
};
(visit_one(_f, _result, Index<_is>{}), ...);
}
template <class F, class ResultType, IndexType... _is>
void do_visit_with_result(const F& _f, std::optional<ResultType>* _result,
std::integer_sequence<IndexType, _is...>) const {
const auto visit_one = [this]<IndexType _i>(const F& _f,
std::optional<ResultType>* _result,
Index<_i>) {
if (!*_result && index_ == _i) {
_result->emplace(_f(get_alternative<_i>()));
}
};
(visit_one(_f, _result, Index<_is>{}), ...);
}
template <class F, class ResultType, IndexType... _is>
void do_visit_with_reference(F& _f, ResultType** _result,
std::integer_sequence<IndexType, _is...>) {
const auto visit_one = [this]<IndexType _i>(const F& _f, ResultType** _result,
Index<_i>) {
if (!*_result && index_ == _i) {
*_result = &_f(get_alternative<_i>());
}
};
(visit_one(_f, _result, Index<_is>{}), ...);
}
template <class F, class ResultType, IndexType... _is>
void do_visit_with_reference(F& _f, ResultType** _result,
std::integer_sequence<IndexType, _is...>) const {
const auto visit_one = [this]<IndexType _i>(const F& _f, ResultType** _result,
Index<_i>) {
if (!*_result && index_ == _i) {
*_result = &_f(get_alternative<_i>());
}
};
(visit_one(_f, _result, Index<_is>{}), ...);
}
template <class F, class ResultType, IndexType... _is>
void do_visit_with_reference(const F& _f, ResultType** _result,
std::integer_sequence<IndexType, _is...>) {
const auto visit_one = [this]<IndexType _i>(const F& _f, ResultType** _result,
Index<_i>) {
if (!*_result && index_ == _i) {
*_result = &_f(get_alternative<_i>());
}
};
(visit_one(_f, _result, Index<_is>{}), ...);
}
template <class F, class ResultType, IndexType... _is>
void do_visit_with_reference(const F& _f, ResultType** _result,
std::integer_sequence<IndexType, _is...>) const {
const auto visit_one = [this]<IndexType _i>(const F& _f, ResultType** _result,
Index<_i>) {
if (!*_result && index_ == _i) {
*_result = &_f(get_alternative<_i>());
}
};
(visit_one(_f, _result, Index<_is>{}), ...);
}
template <IndexType _i>
auto& get_alternative() noexcept {
using CurrentType = internal::nth_element_t<_i, AlternativeTypes...>;
return *internal::ptr_cast<CurrentType*>(data_.data());
}
template <IndexType _i>
const auto& get_alternative() const noexcept {
using CurrentType = internal::nth_element_t<_i, AlternativeTypes...>;
return *internal::ptr_cast<const CurrentType*>(data_.data());
}
void move_from_other(Variant&& _other) noexcept {
const auto move_one = [this](auto&& _t) {
this->move_from_type(std::forward<std::remove_cvref_t<decltype(_t)>>(_t));
};
std::move(_other).visit(move_one);
}
template <class T>
void move_from_type(T&& _t) noexcept {
using CurrentType = std::remove_cvref_t<decltype(_t)>;
index_ =
internal::element_index<CurrentType,
std::remove_cvref_t<AlternativeTypes>...>();
new (data_.data()) CurrentType(std::forward<T>(_t));
}
private:
/// Index indicating which of the alternatives is currently contained in the
/// variant.
IndexType index_;
/// The underlying data, can be any of the underlying types.
alignas(AlternativeTypes...) DataType data_;
};
template <typename V>
concept VariantBased = requires(std::decay_t<V> v) {
[]<typename... Args>(Variant<Args...> const&) {}(v);
};
template <class T, class... Types>
constexpr T* get_if(Variant<Types...>* _v) noexcept {
const auto get = [](auto& _v) -> T* {
using Type = std::remove_cvref_t<decltype(_v)>;
if constexpr (std::is_same<Type, std::remove_cvref_t<T>>()) {
return &_v;
} else {
return nullptr;
}
};
return _v->visit(get);
}
template <class T, class... Types>
constexpr const T* get_if(const Variant<Types...>* _v) noexcept {
const auto get = [](const auto& _v) -> const T* {
using Type = std::remove_cvref_t<decltype(_v)>;
if constexpr (std::is_same<Type, std::remove_cvref_t<T>>()) {
return &_v;
} else {
return nullptr;
}
};
return _v->visit(get);
}
template <int _i, class... Types>
constexpr auto* get_if(Variant<Types...>* _v) noexcept {
using T = internal::nth_element_t<_i, Types...>;
return get_if<T>(_v);
}
template <int _i, class... Types>
constexpr auto* get_if(const Variant<Types...>* _v) noexcept {
using T = internal::nth_element_t<_i, Types...>;
return get_if<T>(_v);
}
template <class T, class... Types>
constexpr T& get(Variant<Types...>& _v) {
auto ptr = get_if<T>(&_v);
if (!ptr) {
throw std::runtime_error("Variant does not contain signified type.");
}
return *ptr;
}
template <class T, class... Types>
constexpr T&& get(Variant<Types...>&& _v) {
auto ptr = get_if<T>(&_v);
if (!ptr) {
throw std::runtime_error("Variant does not contain signified type.");
}
return std::move(*ptr);
}
template <class T, class... Types>
constexpr const T& get(const Variant<Types...>& _v) {
auto ptr = get_if<T>(&_v);
if (!ptr) {
throw std::runtime_error("Variant does not contain signified type.");
}
return *ptr;
}
template <int _i, class... Types>
constexpr auto& get(Variant<Types...>& _v) {
auto ptr = get_if<_i>(&_v);
if (!ptr) {
throw std::runtime_error("Variant does not contain signified type.");
}
return *ptr;
}
template <int _i, class... Types>
constexpr auto&& get(Variant<Types...>&& _v) {
auto ptr = get_if<_i>(&_v);
if (!ptr) {
throw std::runtime_error("Variant does not contain signified type.");
}
return std::move(*ptr);
}
template <int _i, class... Types>
constexpr const auto& get(const Variant<Types...>& _v) {
auto ptr = get_if<_i>(&_v);
if (!ptr) {
throw std::runtime_error("Variant does not contain signified type.");
}
return *ptr;
}
template <class T, class... Types>
constexpr bool holds_alternative(const Variant<Types...>& _v) noexcept {
constexpr auto ix = internal::element_index<std::remove_cvref_t<T>,
std::remove_cvref_t<Types>...>();
static_assert(ix != -1, "Type not supported.");
return ix == _v.index();
}
template <int N, class T>
struct variant_alternative;
template <int N, class... Types>
struct variant_alternative<N, Variant<Types...>> {
using type = internal::nth_element_t<N, Types...>;
};
template <int N, class VariantType>
using variant_alternative_t =
typename variant_alternative<N, std::remove_cvref_t<VariantType>>::type;
template <class T>
struct variant_size;
template <class... Types>
struct variant_size<Variant<Types...>>
: std::integral_constant<size_t, sizeof...(Types)> {};
template <class VariantType>
constexpr size_t variant_size_v =
variant_size<std::remove_cvref_t<VariantType>>();
} // namespace rfl
namespace std {
template <class... Types>
void swap(rfl::Variant<Types...>& _lhs, rfl::Variant<Types...>& _rhs) noexcept {
_lhs.swap(_rhs);
};
} // namespace std
#endif

View File

@@ -0,0 +1,13 @@
#ifndef RFL_VECTORSTRING_HPP_
#define RFL_VECTORSTRING_HPP_
#include <cstddef>
#include <vector>
namespace rfl {
using Vectorstring = std::vector<char>;
} // namespace rfl
#endif

View File

@@ -0,0 +1,12 @@
#ifndef RFL_ALWAYSFALSE_HPP_
#define RFL_ALWAYSFALSE_HPP_
namespace rfl {
/// To be used inside visitor patterns
template <class>
inline constexpr bool always_false_v = false;
} // namespace rfl
#endif // RFL_ALWAYSFALSE_HPP_

View File

@@ -0,0 +1,31 @@
#ifndef RFL_APPLY_HPP_
#define RFL_APPLY_HPP_
#include <utility>
#include "Tuple.hpp"
#include "internal/tuple/apply.hpp"
namespace rfl {
template <class F, class... Types>
auto apply(F&& _f, const rfl::Tuple<Types...>& _tup) {
return internal::tuple::apply(
_f, _tup, std::make_integer_sequence<int, sizeof...(Types)>());
}
template <class F, class... Types>
auto apply(F&& _f, rfl::Tuple<Types...>& _tup) {
return internal::tuple::apply(
_f, _tup, std::make_integer_sequence<int, sizeof...(Types)>());
}
template <class F, class... Types>
auto apply(F&& _f, rfl::Tuple<Types...>&& _tup) {
return internal::tuple::apply(
_f, std::move(_tup), std::make_integer_sequence<int, sizeof...(Types)>());
}
} // namespace rfl
#endif

View File

@@ -0,0 +1,35 @@
#ifndef RFL_AS_HPP_
#define RFL_AS_HPP_
#include "from_named_tuple.hpp"
#include "make_named_tuple.hpp"
#include "to_named_tuple.hpp"
namespace rfl {
/// Generates a type T from the input values.
template <class T, class Head, class... Tail>
T as(Head&& _head, Tail&&... _tail) {
if constexpr (sizeof...(_tail) == 0) {
return from_named_tuple<T>(to_named_tuple(std::forward<Head>(_head)));
} else {
return from_named_tuple<T>(
to_named_tuple(std::forward<Head>(_head))
.add(to_named_tuple(std::forward<Tail>(_tail))...));
}
}
/// Generates a type T from the input values.
template <class T, class Head, class... Tail>
T as(const Head& _head, const Tail&... _tail) {
if constexpr (sizeof...(_tail) == 0) {
return from_named_tuple<T>(to_named_tuple(_head));
} else {
return from_named_tuple<T>(
to_named_tuple(_head).add(to_named_tuple(_tail)...));
}
}
} // namespace rfl
#endif

View File

@@ -0,0 +1,19 @@
#ifndef RFL_COMMON_HPP_
#define RFL_COMMON_HPP_
#ifdef RFL_BUILD_SHARED
#ifdef _WIN32
#ifdef reflectcpp_EXPORTS
#define RFL_API __declspec(dllexport)
#else
#define RFL_API __declspec(dllimport)
#endif
#else
#define RFL_API __attribute__((visibility("default")))
#endif
#else
#define RFL_API
#endif
#endif

View File

@@ -0,0 +1,164 @@
#ifndef RFL_COMPARISONS_HPP_
#define RFL_COMPARISONS_HPP_
#include <sstream>
#include <type_traits>
#include "Result.hpp"
#include "parsing/schema/ValidationType.hpp"
namespace rfl {
template <auto _threshold>
struct EqualTo {
template <class T>
static Result<T> validate(T _value) noexcept {
constexpr auto threshold = static_cast<T>(_threshold);
if (_value != threshold) {
std::stringstream stream;
stream << "Value expected to be equal to " << threshold << ", but got "
<< _value << ".";
return error(stream.str());
}
return _value;
}
template <class T>
static parsing::schema::ValidationType to_schema() {
using ValidationType = parsing::schema::ValidationType;
const auto value =
std::is_floating_point_v<T>
? rfl::Variant<double, int>(static_cast<double>(_threshold))
: rfl::Variant<double, int>(static_cast<int>(_threshold));
return ValidationType{ValidationType::EqualTo{.value_ = value}};
}
};
template <auto _threshold>
struct Minimum {
template <class T>
static Result<T> validate(T _value) noexcept {
constexpr auto threshold = static_cast<T>(_threshold);
if (_value < threshold) {
std::stringstream stream;
stream << "Value expected to be greater than or equal to " << threshold
<< ", but got " << _value << ".";
return error(stream.str());
}
return _value;
}
template <class T>
static parsing::schema::ValidationType to_schema() {
using ValidationType = parsing::schema::ValidationType;
const auto value =
std::is_floating_point_v<T>
? rfl::Variant<double, int>(static_cast<double>(_threshold))
: rfl::Variant<double, int>(static_cast<int>(_threshold));
return ValidationType{ValidationType::Minimum{.value_ = value}};
}
};
template <auto _threshold>
struct ExclusiveMinimum {
template <class T>
static Result<T> validate(T _value) noexcept {
constexpr auto threshold = static_cast<T>(_threshold);
if (_value <= threshold) {
std::stringstream stream;
stream << "Value expected to be greater than " << threshold
<< ", but got " << _value << ".";
return error(stream.str());
}
return _value;
}
template <class T>
static parsing::schema::ValidationType to_schema() {
using ValidationType = parsing::schema::ValidationType;
const auto value =
std::is_floating_point_v<T>
? rfl::Variant<double, int>(static_cast<double>(_threshold))
: rfl::Variant<double, int>(static_cast<int>(_threshold));
return ValidationType{ValidationType::ExclusiveMinimum{.value_ = value}};
}
};
template <auto _threshold>
struct Maximum {
template <class T>
static Result<T> validate(T _value) noexcept {
constexpr auto threshold = static_cast<T>(_threshold);
if (_value > threshold) {
std::stringstream stream;
stream << "Value expected to be less than or equal to " << threshold
<< ", but got " << _value << ".";
return error(stream.str());
}
return _value;
}
template <class T>
static parsing::schema::ValidationType to_schema() {
using ValidationType = parsing::schema::ValidationType;
const auto value =
std::is_floating_point_v<T>
? rfl::Variant<double, int>(static_cast<double>(_threshold))
: rfl::Variant<double, int>(static_cast<int>(_threshold));
return ValidationType{ValidationType::Maximum{.value_ = value}};
}
};
template <auto _threshold>
struct ExclusiveMaximum {
template <class T>
static Result<T> validate(T _value) noexcept {
constexpr auto threshold = static_cast<T>(_threshold);
if (_value >= threshold) {
std::stringstream stream;
stream << "Value expected to be less than " << threshold << ", but got "
<< _value << ".";
return error(stream.str());
}
return _value;
}
template <class T>
static parsing::schema::ValidationType to_schema() {
using ValidationType = parsing::schema::ValidationType;
const auto value =
std::is_floating_point_v<T>
? rfl::Variant<double, int>(static_cast<double>(_threshold))
: rfl::Variant<double, int>(static_cast<int>(_threshold));
return ValidationType{ValidationType::ExclusiveMaximum{.value_ = value}};
}
};
template <auto _threshold>
struct NotEqualTo {
template <class T>
static Result<T> validate(T _value) noexcept {
constexpr auto threshold = static_cast<T>(_threshold);
if (_value == threshold) {
std::stringstream stream;
stream << "Value expected not to be equal to " << threshold
<< ", but got " << _value << ".";
return error(stream.str());
}
return _value;
}
template <class T>
static parsing::schema::ValidationType to_schema() {
using ValidationType = parsing::schema::ValidationType;
const auto value =
std::is_floating_point_v<T>
? rfl::Variant<double, int>(static_cast<double>(_threshold))
: rfl::Variant<double, int>(static_cast<int>(_threshold));
return ValidationType{ValidationType::NotEqualTo{.value_ = value}};
}
};
} // namespace rfl
#endif

View File

@@ -0,0 +1,67 @@
#ifndef RFL_CONCEPTS_HPP_
#define RFL_CONCEPTS_HPP_
#include <concepts>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <type_traits>
namespace rfl::concepts {
/// Concept for byte-like types that can be used in contiguous containers
/// Includes char, signed char, unsigned char, std::byte, and uint8_t
template <typename T>
concept ByteLike = std::same_as<T, char> || std::same_as<T, signed char> ||
std::same_as<T, unsigned char> ||
std::same_as<T, std::uint8_t> || std::same_as<T, std::byte>;
/// Concept for containers with a contiguous sequence of byte-like types
/// Requires:
/// - Container has a value_type that is byte-like
/// - Container provides data() method returning a pointer to contiguous memory
/// - Container provides size() method returning the number of elements
/// - Container supports range-based for loops (begin/end)
template <typename Container>
concept ContiguousByteContainer = requires(const Container& c) {
typename Container::value_type;
{ c.data() } -> std::convertible_to<const typename Container::value_type*>;
{ c.size() } -> std::convertible_to<std::size_t>;
{ c.begin() } -> std::input_iterator;
{ c.end() } -> std::input_iterator;
requires ByteLike<typename Container::value_type>;
requires std::contiguous_iterator<decltype(c.begin())>;
};
/// Concept for mutable containers with a contiguous sequence of byte-like types
/// Extends ContiguousByteContainer with mutable access requirements
template <typename Container>
concept MutableContiguousByteContainer =
ContiguousByteContainer<Container> && requires(Container& c) {
{ c.data() } -> std::convertible_to<typename Container::value_type*>;
{ c.begin() } -> std::output_iterator<typename Container::value_type>;
{ c.end() } -> std::output_iterator<typename Container::value_type>;
};
/// Concept for back-insertable byte containers (like std::vector<uint8_t>)
/// Useful for containers that can grow dynamically during serialization
template <typename Container>
concept BackInsertableByteContainer =
ContiguousByteContainer<Container> &&
requires(Container& c, typename Container::value_type v) {
c.push_back(v);
c.reserve(std::size_t{});
{ c.capacity() } -> std::convertible_to<std::size_t>;
};
/// Concept for byte spans or views (read-only, non-owning containers)
/// Includes std::span<const uint8_t>, std::string_view when used with char
/// data, etc.
template <typename Container>
concept ByteSpanLike = ContiguousByteContainer<Container> &&
std::is_trivially_copyable_v<Container> &&
std::is_trivially_destructible_v<Container>;
} // namespace rfl::concepts
#endif // RFL_CONCEPTS_HPP_

View File

@@ -0,0 +1,19 @@
#ifndef RFL_CONFIG_HPP_
#define RFL_CONFIG_HPP_
namespace rfl::config {
// To specify a different range for a particular enum type, specialize the
// enum_range template for that enum type.
template <typename T>
struct enum_range {
// In your template specialization, uncomment these two lines and replace them
// with the values of your choice.
// static constexpr int min = ...;
// static constexpr int max = ...;
};
} // namespace rfl::config
#endif

View File

@@ -0,0 +1,14 @@
#ifndef RFL_DEFAULT_HPP_
#define RFL_DEFAULT_HPP_
namespace rfl {
/// Helper class that can be passed to a field
/// to trigger the default value of the type.
struct Default {};
inline static const auto default_value = Default{};
} // namespace rfl
#endif

View File

@@ -0,0 +1,16 @@
#ifndef RFL_DEFINELITERAL_HPP_
#define RFL_DEFINELITERAL_HPP_
#include "Literal.hpp"
#include "internal/define_literal.hpp"
namespace rfl {
/// Allows you to combine several literal types.
template <class... LiteralTypes>
using define_literal_t =
typename internal::define_literal<LiteralTypes...>::type;
} // namespace rfl
#endif // RFL_DEFINELITERAL_HPP_

View File

@@ -0,0 +1,15 @@
#ifndef RFL_DEFINENAMEDTUPLE_HPP_
#define RFL_DEFINENAMEDTUPLE_HPP_
#include "NamedTuple.hpp"
#include "internal/define_named_tuple.hpp"
namespace rfl {
template <class... FieldTypes>
using define_named_tuple_t =
typename internal::define_named_tuple<FieldTypes...>::type;
} // namespace rfl
#endif // RFL_DEFINENAMEDTUPLE_HPP_

View File

@@ -0,0 +1,17 @@
#ifndef RFL_DEFINETAGGEDUNION_HPP_
#define RFL_DEFINETAGGEDUNION_HPP_
#include "TaggedUnion.hpp"
#include "internal/StringLiteral.hpp"
#include "internal/define_tagged_union.hpp"
namespace rfl {
template <internal::StringLiteral _discriminator, class... TaggedUnionTypes>
using define_tagged_union_t =
typename internal::define_tagged_union<_discriminator,
TaggedUnionTypes...>::type;
} // namespace rfl
#endif

View File

@@ -0,0 +1,15 @@
#ifndef RFL_DEFINEVARIANT_HPP_
#define RFL_DEFINEVARIANT_HPP_
#include <variant>
#include "internal/define_variant.hpp"
namespace rfl {
template <class... Vars>
using define_variant_t = typename internal::define_variant<Vars...>::type;
} // namespace rfl
#endif // RFL_DEFINEVARIANT_HPP_

View File

@@ -0,0 +1,118 @@
#ifndef RFL_ENUMS_HPP_
#define RFL_ENUMS_HPP_
#include <string>
#include "Result.hpp"
#include "internal/enums/get_enum_names.hpp"
#include "internal/strings/strings.hpp"
#include "thirdparty/enchantum/enchantum.hpp"
#include "thirdparty/enchantum/bitflags.hpp"
namespace rfl {
template <enchantum::Enum EnumType>
std::string enum_to_string(const EnumType _enum) {
const auto to_string_or_number = [](const EnumType e) {
const auto s = enchantum::to_string(e);
return s.empty() ? std::to_string(
static_cast<std::underlying_type_t<EnumType>>(e))
: std::string(s);
};
if constexpr (enchantum::is_bitflag<EnumType>) {
// Iterates through the enum bit by bit and matches it against the flags.
using T = std::underlying_type_t<EnumType>;
auto val = static_cast<T>(_enum);
int i = 0;
std::vector<std::string> flags;
while (val != 0) {
const auto bit = val & static_cast<T>(1);
if (bit == 1) {
auto str =
to_string_or_number(static_cast<EnumType>(static_cast<T>(1) << i));
flags.emplace_back(std::move(str));
}
++i;
val >>= 1;
}
return internal::strings::join("|", flags);
} else {
return to_string_or_number(_enum);
}
}
// Converts a string to a value of the given enum type.
template <enchantum::Enum EnumType>
Result<EnumType> string_to_enum(const std::string& _str) {
const auto cast_numbers_or_names =
[](const std::string& name) -> Result<EnumType> {
const auto r = enchantum::cast<EnumType>(name);
if (r) return *r;
try {
return static_cast<EnumType>(std::stoi(name));
} catch (std::exception& exp) {
return error(exp.what());
}
};
if constexpr (enchantum::is_bitflag<EnumType>) {
using T = std::underlying_type_t<EnumType>;
const auto split = internal::strings::split(_str, "|");
auto res = static_cast<T>(0);
for (const auto& s : split) {
const auto r = cast_numbers_or_names(s);
if (r) {
res |= static_cast<T>(*r);
} else {
return r;
}
}
return static_cast<EnumType>(res);
} else {
return cast_numbers_or_names(_str);
}
}
// Returns a named tuple mapping names of enumerators of the given enum type to
// their values.
template <enchantum::Enum EnumType>
auto get_enumerators() {
return internal::enums::names_to_enumerator_named_tuple(
internal::enums::get_enum_names<EnumType>());
}
// Returns a named tuple mapping names of enumerators of the given enum type to
// their underlying values.
template <enchantum::Enum EnumType>
auto get_underlying_enumerators() {
return internal::enums::names_to_underlying_enumerator_named_tuple(
internal::enums::get_enum_names<EnumType>());
}
// Returns an std::array containing pairs of enumerator names (as
// std::string_view) and values.
template <enchantum::Enum EnumType>
constexpr auto get_enumerator_array() {
return internal::enums::names_to_enumerator_array(
internal::enums::get_enum_names<EnumType>());
}
// Returns an std::array containing pairs of enumerator names (as
// std::string_view) and underlying values.
template <enchantum::Enum EnumType>
constexpr auto get_underlying_enumerator_array() {
return internal::enums::names_to_underlying_enumerator_array(
internal::enums::get_enum_names<EnumType>());
}
// Returns the range of the given enum type as a pair of the minimum and maximum
template <enchantum::Enum EnumType>
constexpr auto get_enum_range() {
return std::make_pair(enchantum::enum_traits<EnumType>::min,
enchantum::enum_traits<EnumType>::max);
}
} // namespace rfl
#endif // RFL_ENUMS_HPP_

View File

@@ -0,0 +1,20 @@
#ifndef RFL_EXTRACTDISTRIMINATORS_HPP_
#define RFL_EXTRACTDISTRIMINATORS_HPP_
#include <type_traits>
#include "TaggedUnion.hpp"
#include "define_literal.hpp"
#include "field_type.hpp"
#include "internal/extract_discriminators.hpp"
namespace rfl {
/// Extracts a Literal containing all of the discriminators from a TaggedUnion.
template <class TaggedUnionType>
using extract_discriminators_t =
typename internal::extract_discriminators<TaggedUnionType>::type;
} // namespace rfl
#endif // RFL_EXTRACTDISTRIMINATORS_HPP_

View File

@@ -0,0 +1,18 @@
#ifndef RFL_FIELD_NAMES_T_HPP_
#define RFL_FIELD_NAMES_T_HPP_
#include <functional>
#include <type_traits>
#include "internal/get_field_names.hpp"
namespace rfl {
/// Returns a rfl::Literal containing the field names of struct T.
template <class T>
using field_names_t = typename std::invoke_result<
decltype(internal::get_field_names<std::remove_cvref_t<T>>)>::type;
} // namespace rfl
#endif

View File

@@ -0,0 +1,18 @@
#ifndef RFL_FIELD_TYPE_HPP_
#define RFL_FIELD_TYPE_HPP_
#include <tuple>
#include <type_traits>
#include <variant>
#include "internal/StringLiteral.hpp"
#include "internal/field_type.hpp"
namespace rfl {
template <internal::StringLiteral _field_name, class T>
using field_type_t = typename internal::FieldType<_field_name, T>::Type;
} // namespace rfl
#endif

View File

@@ -0,0 +1,17 @@
#ifndef RFL_FIELDS_HPP_
#define RFL_FIELDS_HPP_
#include "internal/get_meta_fields.hpp"
#include "named_tuple_t.hpp"
namespace rfl {
/// Returns meta-information about the fields.
template <class T>
auto fields() {
return internal::get_meta_fields<named_tuple_t<T>>();
}
} // namespace rfl
#endif

View File

@@ -0,0 +1,17 @@
#ifndef RFL_FROM_GENERIC_HPP_
#define RFL_FROM_GENERIC_HPP_
#include "Generic.hpp"
#include "generic/read.hpp"
namespace rfl {
/// Generates the struct T from a named tuple.
template <class T, class... Ps>
auto from_generic(const Generic& _g) {
return rfl::generic::read<T, Ps...>(_g);
}
} // namespace rfl
#endif

View File

@@ -0,0 +1,53 @@
#ifndef RFL_FROM_NAMED_TUPLE_HPP_
#define RFL_FROM_NAMED_TUPLE_HPP_
#include <type_traits>
#include "internal/copy_from_named_tuple.hpp"
#include "internal/copy_from_tuple.hpp"
#include "internal/has_fields.hpp"
#include "internal/move_from_named_tuple.hpp"
#include "internal/move_from_tuple.hpp"
#include "named_tuple_t.hpp"
namespace rfl {
/// Generates the struct T from a named tuple.
template <class T, class NamedTupleType>
auto from_named_tuple(NamedTupleType&& _n) {
using RequiredType = std::remove_cvref_t<rfl::named_tuple_t<T>>;
if constexpr (!std::is_same<std::remove_cvref_t<NamedTupleType>,
RequiredType>()) {
return from_named_tuple<T>(RequiredType(std::forward<NamedTupleType>(_n)));
} else if constexpr (internal::has_fields<T>()) {
if constexpr (std::is_lvalue_reference<NamedTupleType>{}) {
return internal::copy_from_named_tuple<T>(_n);
} else {
return internal::move_from_named_tuple<T>(_n);
}
} else {
if constexpr (std::is_lvalue_reference<NamedTupleType>{}) {
return internal::copy_from_tuple<T>(_n.values());
} else {
return internal::move_from_tuple<T>(std::move(_n.values()));
}
}
}
/// Generates the struct T from a named tuple.
template <class T, class NamedTupleType>
auto from_named_tuple(const NamedTupleType& _n) {
using RequiredType = std::remove_cvref_t<rfl::named_tuple_t<T>>;
if constexpr (!std::is_same<std::remove_cvref_t<NamedTupleType>,
RequiredType>()) {
return from_named_tuple<T>(RequiredType(_n));
} else if constexpr (internal::has_fields<T>()) {
return internal::copy_from_named_tuple<T>(_n);
} else {
return internal::copy_from_tuple<T>(_n.values());
}
}
} // namespace rfl
#endif

View File

@@ -0,0 +1,17 @@
#ifndef GENERIC_PARSER_HPP_
#define GENERIC_PARSER_HPP_
#include "../parsing/Parser.hpp"
#include "Reader.hpp"
#include "Writer.hpp"
namespace rfl {
namespace generic {
template <class T, class ProcessorsType>
using Parser = parsing::Parser<Reader, Writer, T, ProcessorsType>;
}
} // namespace rfl
#endif

View File

@@ -0,0 +1,102 @@
#ifndef GENERIC_READER_HPP_
#define GENERIC_READER_HPP_
#include <cstddef>
#include <string>
#include <string_view>
#include <type_traits>
#include "../Generic.hpp"
#include "../Result.hpp"
#include "../always_false.hpp"
namespace rfl::generic {
struct Reader {
using InputArrayType = Generic::Array;
using InputObjectType = Generic::Object;
using InputVarType = Generic;
template <class T>
static constexpr bool has_custom_constructor = false;
rfl::Result<InputVarType> get_field_from_array(
const size_t _idx, const InputArrayType& _arr) const noexcept {
if (_idx >= _arr.size()) {
return error("Index " + std::to_string(_idx) + " of of bounds.");
}
return _arr[_idx];
}
rfl::Result<InputVarType> get_field_from_object(
const std::string& _name, const InputObjectType& _obj) const noexcept {
return _obj.get(_name);
}
bool is_empty(const InputVarType& _var) const noexcept {
return _var.is_null();
}
template <class T>
rfl::Result<T> to_basic_type(const InputVarType& _var) const noexcept {
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
return _var.to_string();
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
return _var.to_bool();
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>()) {
return _var.to_double().transform(
[](const auto& _v) { return static_cast<T>(_v); });
} else if constexpr (std::is_integral<std::remove_cvref_t<T>>()) {
if constexpr (sizeof(T) > sizeof(int)) {
return _var.to_int64().transform(
[](const auto& _v) { return static_cast<T>(_v); });
} else {
return _var.to_int().transform(
[](const auto& _v) { return static_cast<T>(_v); });
}
} else {
static_assert(rfl::always_false_v<T>, "Unsupported type.");
}
}
template <class ArrayReader>
std::optional<Error> read_array(const ArrayReader& _array_reader,
const InputArrayType& _arr) const noexcept {
for (const auto& v : _arr) {
const auto err = _array_reader.read(InputVarType(v));
if (err) {
return err;
}
}
return std::nullopt;
}
template <class ObjectReader>
std::optional<Error> read_object(const ObjectReader& _object_reader,
const InputObjectType& _obj) const noexcept {
for (const auto& [k, v] : _obj) {
_object_reader.read(std::string_view(k), v);
}
return std::nullopt;
}
rfl::Result<InputArrayType> to_array(
const InputVarType& _var) const noexcept {
return _var.to_array();
}
rfl::Result<InputObjectType> to_object(
const InputVarType& _var) const noexcept {
return _var.to_object();
}
template <class T>
rfl::Result<T> use_custom_constructor(
const InputVarType /*_var*/) const noexcept {
return error("Not supported for generic types");
}
};
} // namespace rfl::generic
#endif

View File

@@ -0,0 +1,110 @@
#ifndef GENERIC_WRITER_HPP_
#define GENERIC_WRITER_HPP_
#include <cstddef>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#include "../Generic.hpp"
#include "../always_false.hpp"
#include "../common.hpp"
namespace rfl::generic {
struct RFL_API Writer {
struct OutputArray {
Generic::Array* val_;
};
struct OutputObject {
Generic::Object* val_;
};
using OutputArrayType = OutputArray;
using OutputObjectType = OutputObject;
using OutputVarType = Generic;
Writer() {}
~Writer() = default;
OutputArrayType array_as_root(const size_t _size) const noexcept;
OutputObjectType object_as_root(const size_t _size) const noexcept;
OutputVarType null_as_root() const noexcept;
template <class T>
OutputVarType value_as_root(const T& _var) const noexcept {
root_ = to_generic(_var);
return root_;
}
OutputArrayType add_array_to_array(const size_t _size,
OutputArrayType* _parent) const noexcept;
OutputArrayType add_array_to_object(const std::string_view& _name,
const size_t _size,
OutputObjectType* _parent) const noexcept;
OutputObjectType add_object_to_array(const size_t _size,
OutputArrayType* _parent) const noexcept;
OutputObjectType add_object_to_object(
const std::string_view& _name, const size_t _size,
OutputObjectType* _parent) const noexcept;
template <class T>
OutputVarType add_value_to_array(const T& _var,
OutputArrayType* _parent) const noexcept {
const auto g = to_generic(_var);
_parent->val_->push_back(g);
return g;
}
template <class T>
OutputVarType add_value_to_object(const std::string_view& _name,
const T& _var,
OutputObjectType* _parent) const noexcept {
const auto g = to_generic(_var);
_parent->val_->insert(_name, g);
return g;
}
OutputVarType add_null_to_array(OutputArrayType* _parent) const noexcept;
OutputVarType add_null_to_object(const std::string_view& _name,
OutputObjectType* _parent) const noexcept;
void end_array(OutputArrayType*) const noexcept {}
void end_object(OutputObjectType*) const noexcept {}
OutputVarType& root() { return root_; }
private:
template <class T>
OutputVarType to_generic(const T& _var) const noexcept {
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
return OutputVarType(_var);
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
return OutputVarType(_var);
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>()) {
return OutputVarType(static_cast<double>(_var));
} else if constexpr (std::is_integral<std::remove_cvref_t<T>>()) {
return OutputVarType(static_cast<int64_t>(_var));
} else {
static_assert(always_false_v<T>, "Unsupported type");
}
return OutputVarType{};
}
private:
mutable OutputVarType root_;
};
} // namespace rfl::generic
#endif

View File

@@ -0,0 +1,22 @@
#ifndef GENERIC_READ_HPP_
#define GENERIC_READ_HPP_
#include "../Generic.hpp"
#include "../Processors.hpp"
#include "Parser.hpp"
namespace rfl {
namespace generic {
/// Parses an object from a generic type.
template <class T, class... Ps>
auto read(const Generic& _g) {
const auto r = Reader();
return Parser<T, Processors<Ps...>>::read(r, _g);
}
} // namespace generic
} // namespace rfl
#endif

View File

@@ -0,0 +1,24 @@
#ifndef GENERIC_WRITE_HPP_
#define GENERIC_WRITE_HPP_
#include "../Generic.hpp"
#include "../parsing/Parent.hpp"
#include "Parser.hpp"
namespace rfl {
namespace generic {
/// Writes an object to a generic.
template <class... Ps>
Generic write(const auto& _t) {
using T = std::remove_cvref_t<decltype(_t)>;
using ParentType = parsing::Parent<Writer>;
auto w = Writer();
Parser<T, Processors<Ps...>>::write(w, _t, typename ParentType::Root{});
return w.root();
}
} // namespace generic
} // namespace rfl
#endif

View File

@@ -0,0 +1,48 @@
#ifndef RFL_GET_HPP_
#define RFL_GET_HPP_
#include "internal/Getter.hpp"
#include "internal/StringLiteral.hpp"
namespace rfl {
/// Gets a field by index.
template <int _index, class NamedTupleType>
inline auto& get(NamedTupleType& _tup) {
return internal::Getter<NamedTupleType>::template get<_index>(_tup);
}
/// Gets a field by name.
template <internal::StringLiteral _field_name, class NamedTupleType>
inline auto& get(NamedTupleType& _tup) {
return internal::Getter<NamedTupleType>::template get<_field_name>(_tup);
}
/// Gets a field by the field type.
template <class Field, class NamedTupleType>
inline auto& get(NamedTupleType& _tup) {
return internal::Getter<NamedTupleType>::template get<Field>(_tup);
}
/// Gets a field by index.
template <int _index, class NamedTupleType>
inline const auto& get(const NamedTupleType& _tup) {
return internal::Getter<NamedTupleType>::template get_const<_index>(_tup);
}
/// Gets a field by name.
template <internal::StringLiteral _field_name, class NamedTupleType>
inline const auto& get(const NamedTupleType& _tup) {
return internal::Getter<NamedTupleType>::template get_const<_field_name>(
_tup);
}
/// Gets a field by the field type.
template <class Field, class NamedTupleType>
inline const auto& get(const NamedTupleType& _tup) {
return internal::Getter<NamedTupleType>::template get_const<Field>(_tup);
}
} // namespace rfl
#endif

View File

@@ -0,0 +1,32 @@
#ifndef RFL_INTERNAL_ARRAY_HPP_
#define RFL_INTERNAL_ARRAY_HPP_
#include <cstddef>
#include <type_traits>
#include "to_std_array.hpp"
namespace rfl {
namespace internal {
template <class T>
requires std::is_array_v<T>
struct Array {
using Type = T;
using StdArrayType = to_std_array_t<T>;
Array() = default;
Array(const StdArrayType &_arr) : arr_(_arr) {}
Array(StdArrayType &&_arr) : arr_(std::move(_arr)) {}
Array(const T &_arr) : arr_(to_std_array(_arr)) {}
Array(T &&_arr) : arr_(to_std_array(_arr)) {}
~Array() = default;
StdArrayType arr_;
};
} // namespace internal
} // namespace rfl
#endif

View File

@@ -0,0 +1,18 @@
#ifndef RFL_INTERNAL_FIELD_TUPLE_T_HPP_
#define RFL_INTERNAL_FIELD_TUPLE_T_HPP_
#include <type_traits>
#include "copy_to_field_tuple.hpp"
namespace rfl {
namespace internal {
template <class T>
using field_tuple_t =
typename std::invoke_result<decltype(copy_to_field_tuple<T>), T>::type;
}
} // namespace rfl
#endif

View File

@@ -0,0 +1,23 @@
#ifndef RFL_INTERNAL_FIELDS_HPP_
#define RFL_INTERNAL_FIELDS_HPP_
#include <array>
#include <cstdint>
#include <string>
#include <string_view>
#include <unordered_map>
namespace rfl {
namespace internal {
template <int N>
struct Fields {
std::array<std::string, N> names_;
std::unordered_map<std::string_view, std::int16_t> indices_;
};
} // namespace internal
} // namespace rfl
#endif

View File

@@ -0,0 +1,84 @@
#ifndef RFL_INTERNAL_GETTER_HPP_
#define RFL_INTERNAL_GETTER_HPP_
#include "../Tuple.hpp"
#include "StringLiteral.hpp"
#include "find_index.hpp"
namespace rfl::internal {
// ----------------------------------------------------------------------------
template <class NamedTupleType>
struct Getter;
// ----------------------------------------------------------------------------
/// Default case - anything that cannot be explicitly matched.
template <class NamedTupleType>
struct Getter {
public:
/// Retrieves the indicated value from the tuple.
template <int _index>
static inline auto& get(NamedTupleType& _tup) {
return rfl::get<_index>(_tup.values());
}
/// Gets a field by name.
template <StringLiteral _field_name>
static inline auto& get(NamedTupleType& _tup) {
constexpr auto index =
find_index<_field_name, typename NamedTupleType::Fields>();
return Getter<NamedTupleType>::template get<index>(_tup);
}
/// Gets a field by the field type.
template <class Field>
static inline auto& get(NamedTupleType& _tup) {
constexpr auto index =
find_index<Field::name_, typename NamedTupleType::Fields>();
static_assert(
std::is_same<typename tuple_element_t<
index, typename NamedTupleType::Fields>::Type,
typename Field::Type>(),
"If two fields have the same name, "
"their type must be the same as "
"well.");
return Getter<NamedTupleType>::template get<index>(_tup);
}
/// Retrieves the indicated value from the tuple.
template <int _index>
static inline const auto& get_const(const NamedTupleType& _tup) {
return rfl::get<_index>(_tup.values());
}
/// Gets a field by name.
template <StringLiteral _field_name>
static inline const auto& get_const(const NamedTupleType& _tup) {
constexpr auto index =
find_index<_field_name, typename NamedTupleType::Fields>();
return Getter<NamedTupleType>::template get_const<index>(_tup);
}
/// Gets a field by the field type.
template <class Field>
static inline const auto& get_const(const NamedTupleType& _tup) {
constexpr auto index =
find_index<Field::name_, typename NamedTupleType::Fields>();
static_assert(
std::is_same<typename tuple_element_t<
index, typename NamedTupleType::Fields>::Type,
typename Field::Type>(),
"If two fields have the same name, "
"their type must be the same as "
"well.");
return Getter<NamedTupleType>::template get_const<index>(_tup);
}
};
// ----------------------------------------------------------------------------
} // namespace rfl::internal
#endif

View File

@@ -0,0 +1,17 @@
#ifndef RFL_INTERNAL_HASVALIDATION_HPP_
#define RFL_INTERNAL_HASVALIDATION_HPP_
#include "../Result.hpp"
namespace rfl {
namespace internal {
template <class Class, typename T>
concept HasValidation = requires(Class obj, T value) {
{ Class::validate(value) } -> std::same_as<rfl::Result<T>>;
};
} // namespace internal
} // namespace rfl
#endif

View File

@@ -0,0 +1,149 @@
#ifndef RFL_INTERNAL_SKIP_HPP_
#define RFL_INTERNAL_SKIP_HPP_
#include <optional>
#include <type_traits>
#include "../default.hpp"
namespace rfl::internal {
template <class T, bool _skip_serialization, bool _skip_deserialization>
class Skip {
private:
using SelfType = Skip<T, _skip_serialization, _skip_deserialization>;
public:
static constexpr bool skip_serialization_ = _skip_serialization;
static constexpr bool skip_deserialization_ = _skip_deserialization;
/// The underlying type.
using Type = T;
using ReflectionType = std::optional<T>;
Skip() : value_(Type()) {}
Skip(const Type& _value) : value_(_value) {}
Skip(ReflectionType&& _value) noexcept
: value_(_value ? std::move(*_value) : Type()) {}
Skip(const ReflectionType& _value) : value_(_value ? *_value : Type()) {}
Skip(Type&& _value) noexcept : value_(std::move(_value)) {}
Skip(SelfType&& _skip) noexcept = default;
Skip(const SelfType& _skip) = default;
template <class U, bool _skip_s, bool _skip_d>
Skip(const Skip<U, _skip_s, _skip_d>& _other) : value_(_other.get()) {}
template <class U, bool _skip_s, bool _skip_d>
Skip(Skip<U, _skip_s, _skip_d>&& _other) : value_(_other.get()) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Skip(const U& _value) : value_(_value) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Skip(U&& _value) noexcept : value_(std::forward<U>(_value)) {}
template <class U, bool _skip_s, bool _skip_d,
typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
Skip(const Skip<U, _skip_s, _skip_d>& _skip) : value_(_skip.value()) {}
/// Assigns the underlying object to its default value.
template <class U = Type,
typename std::enable_if<std::is_default_constructible_v<U>,
bool>::type = true>
Skip(const Default&) : value_(Type()) {}
~Skip() = default;
/// Returns the underlying object.
Type& get() { return value_; }
/// Returns the underlying object.
const Type& get() const { return value_; }
/// Returns the underlying object.
Type& operator()() { return value_; }
/// Returns the underlying object.
const Type& operator()() const { return value_; }
/// Assigns the underlying object.
auto& operator=(const Type& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object.
auto& operator=(Type&& _value) noexcept {
value_ = std::move(_value);
return *this;
}
/// Assigns the underlying object.
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
auto& operator=(const U& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object to its default value.
template <class U = Type,
typename std::enable_if<std::is_default_constructible_v<U>,
bool>::type = true>
auto& operator=(const Default&) {
value_ = Type();
return *this;
}
/// Assigns the underlying object.
SelfType& operator=(const SelfType& _other) = default;
/// Assigns the underlying object.
SelfType& operator=(SelfType&& _other) = default;
/// Assigns the underlying object.
template <class U, bool _skip_s, bool _skip_d>
auto& operator=(const Skip<U, _skip_s, _skip_d>& _skip) {
value_ = _skip.get();
return *this;
}
/// Assigns the underlying object.
template <class U, bool _skip_s, bool _skip_d>
auto& operator=(Skip<U, _skip_s, _skip_d>&& _skip) {
value_ = std::forward<T>(_skip.value_);
return *this;
}
/// Returns the ReflectionType - necessary for the serialization to work.
ReflectionType reflection() const { return value_; }
/// Assigns the underlying object.
void set(const Type& _value) { value_ = _value; }
/// Assigns the underlying object.
void set(Type&& _value) { value_ = std::move(_value); }
/// Returns the underlying object.
Type& value() { return value_; }
/// Returns the underlying object.
const Type& value() const { return value_; }
private:
/// The underlying value
T value_;
};
} // namespace rfl::internal
#endif

View File

@@ -0,0 +1,56 @@
#ifndef RFL_INTERNAL_STRINGLITERAL_HPP_
#define RFL_INTERNAL_STRINGLITERAL_HPP_
#include <algorithm>
#include <array>
#include <string>
#include <string_view>
namespace rfl {
namespace internal {
/// Normal strings cannot be used as template
/// parameters, but this can. This is needed
/// for the parameters names in the NamedTuples.
template <size_t N>
struct StringLiteral {
constexpr StringLiteral(const auto... _chars) : arr_{_chars..., '\0'} {}
constexpr StringLiteral(const std::array<char, N> _arr) : arr_(_arr) {}
constexpr StringLiteral(const char (&_str)[N]) {
std::copy_n(_str, N, std::data(arr_));
}
/// Returns the value as a string.
std::string str() const { return std::string(string_view()); }
/// Returns the value as a string.
constexpr std::string_view string_view() const {
return std::string_view(std::data(arr_), N - 1);
}
static constexpr size_t length = N - 1;
std::array<char, N> arr_{};
};
template <size_t N1, size_t N2>
constexpr inline bool operator==(const StringLiteral<N1>& _first,
const StringLiteral<N2>& _second) {
if constexpr (N1 != N2) {
return false;
}
return _first.string_view() == _second.string_view();
}
template <size_t N1, size_t N2>
constexpr inline bool operator!=(const StringLiteral<N1>& _first,
const StringLiteral<N2>& _second) {
return !(_first == _second);
}
} // namespace internal
} // namespace rfl
#endif

View File

@@ -0,0 +1,29 @@
#ifndef RFL_INTERNAL_VISITTREE_HPP_
#define RFL_INTERNAL_VISITTREE_HPP_
namespace rfl {
namespace internal {
struct VisitTree {
/// Evaluates a visitor pattern using a tree-like structure.
template <int _begin, int _end, class Visitor, class... Args>
static inline auto visit(const auto& _v, const int _i,
const Args&... _args) {
static_assert(_end > _begin, "_end needs to be greater than _begin.");
if constexpr (_end - _begin == 1) {
return _v.template visit<_begin>(_args...);
} else {
constexpr int middle = (_begin + _end) / 2;
if (_i < middle) {
return visit<_begin, middle, Visitor>(_v, _i, _args...);
} else {
return visit<middle, _end, Visitor>(_v, _i, _args...);
}
}
}
};
} // namespace internal
} // namespace rfl
#endif

View File

@@ -0,0 +1,27 @@
#ifndef RFL_INTERNAL_VISITORWRAPPER_HPP_
#define RFL_INTERNAL_VISITORWRAPPER_HPP_
#include "../Literal.hpp"
#include "../TaggedUnion.hpp"
#include "StringLiteral.hpp"
namespace rfl {
namespace internal {
/// Necessary for the VisitTree structure.
template <class Visitor, internal::StringLiteral... _fields>
struct VisitorWrapper {
/// Calls the underlying visitor when required to do so.
template <int _i, class... Args>
inline auto visit(const Args&... _args) const {
return (*visitor_)(name_of<Literal<_fields...>, _i>(), _args...);
}
/// The underlying visitor.
const Visitor* visitor_;
};
} // namespace internal
} // namespace rfl
#endif // RFL_VISIT_HPP_

View File

@@ -0,0 +1,23 @@
#ifndef RFL_INTERNAL_ALLFIELDS_HPP_
#define RFL_INTERNAL_ALLFIELDS_HPP_
#include "../Tuple.hpp"
#include "is_field.hpp"
namespace rfl {
namespace internal {
template <class TupleType, int _i = 0>
constexpr bool all_fields() {
if constexpr (_i == rfl::tuple_size_v<TupleType>) {
return true;
} else {
using T = tuple_element_t<_i, TupleType>;
return is_field_v<T> && all_fields<TupleType, _i + 1>();
}
}
} // namespace internal
} // namespace rfl
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
#ifndef RFL_INTERNAL_COPY_FLATTENED_TUPLE_TO_NAMED_TUPLE_HPP_
#define RFL_INTERNAL_COPY_FLATTENED_TUPLE_TO_NAMED_TUPLE_HPP_
#include <type_traits>
#include <utility>
#include "../Field.hpp"
#include "../make_named_tuple.hpp"
#include "lit_name.hpp"
namespace rfl {
namespace internal {
template <class FieldNames, int _i>
auto copy_one_element(const auto& _flattened_tuple) {
const auto name_literal = FieldNames::template name_of<_i>();
return rfl::make_field<
lit_name_v<std::remove_cvref_t<decltype(name_literal)>>>(
std::get<_i>(_flattened_tuple));
}
template <class FieldNames, class... Fields>
auto copy_flattened_tuple_to_named_tuple(const auto& _flattened_tuple) {
constexpr auto size =
rfl::tuple_size_v<std::remove_cvref_t<decltype(_flattened_tuple)>>;
return [&]<int... _i>(std::integer_sequence<int, _i...>) {
return make_named_tuple(
copy_one_element<FieldNames, _i>(_flattened_tuple)...);
}
(std::make_integer_sequence<int, size>());
}
} // namespace internal
} // namespace rfl
#endif

View File

@@ -0,0 +1,20 @@
#ifndef RFL_INTERNAL_COPY_FROM_NAMED_TUPLE_HPP_
#define RFL_INTERNAL_COPY_FROM_NAMED_TUPLE_HPP_
#include "move_from_named_tuple.hpp"
namespace rfl {
namespace internal {
/// Creates a struct of type T from a named tuple.
/// All fields of the struct must be an rfl::Field.
template <class T, class NamedTupleType>
T copy_from_named_tuple(const NamedTupleType& _n) {
auto n = _n;
return move_from_named_tuple<T>(std::move(n));
}
} // namespace internal
} // namespace rfl
#endif

View File

@@ -0,0 +1,20 @@
#ifndef RFL_COPY_FROM_TUPLE_HPP_
#define RFL_COPY_FROM_TUPLE_HPP_
#include "move_from_tuple.hpp"
namespace rfl {
namespace internal {
/// Creates a struct of type T from a tuple by copying the underlying
/// fields.
template <class T, class TupleType>
T copy_from_tuple(const TupleType& _t) {
auto t = _t;
return move_from_tuple<T, TupleType>(std::move(t));
}
} // namespace internal
} // namespace rfl
#endif

View File

@@ -0,0 +1,18 @@
#ifndef RFL_INTERNAL_COPY_TO_FIELD_TUPLE_HPP_
#define RFL_INTERNAL_COPY_TO_FIELD_TUPLE_HPP_
#include "move_to_field_tuple.hpp"
namespace rfl {
namespace internal {
template <class T>
auto copy_to_field_tuple(const T& _t) {
auto t = _t;
return move_to_field_tuple(std::move(t));
}
} // namespace internal
} // namespace rfl
#endif

View File

@@ -0,0 +1,29 @@
#ifndef RFL_INTERNAL_DEFINELITERAL_HPP_
#define RFL_INTERNAL_DEFINELITERAL_HPP_
#include "../Literal.hpp"
namespace rfl {
namespace internal {
/// Allows you to combine several literals.
template <class... LiteralTypes>
struct define_literal;
/// General case
template <StringLiteral... _content1, StringLiteral... _content2, class... Tail>
struct define_literal<Literal<_content1...>, Literal<_content2...>, Tail...> {
using type = typename define_literal<Literal<_content1..., _content2...>,
Tail...>::type;
};
/// Special case - only a single literal is left
template <StringLiteral... _content>
struct define_literal<Literal<_content...>> {
using type = Literal<_content...>;
};
} // namespace internal
} // namespace rfl
#endif

View File

@@ -0,0 +1,46 @@
#ifndef RFL_INTERNAL_DEFINENAMEDTUPLE_HPP_
#define RFL_INTERNAL_DEFINENAMEDTUPLE_HPP_
#include "../NamedTuple.hpp"
namespace rfl {
namespace internal {
template <class... FieldTypes>
struct define_named_tuple;
/// Allows you to combine several named tuples and/or additional fields.
/// Recursive case - all types are fields.
template <class Head, class... Tail>
struct define_named_tuple<Head, Tail...> {
using type = typename define_named_tuple<NamedTuple<Head>, Tail...>::type;
};
/// Allows you to combine several named tuples and/or additional fields.
/// Recursive case - first type is NamedTuple, second type is field.
template <class Head, class... TupContent, class... Tail>
struct define_named_tuple<NamedTuple<TupContent...>, Head, Tail...> {
using type = typename define_named_tuple<NamedTuple<TupContent..., Head>,
Tail...>::type;
};
/// Allows you to combine several named tuples and/or additional fields.
/// Recursive case - first type is NamedTuple, second type is also NamedTuple.
template <class... TupContent, class... TupContent2, class... Tail>
struct define_named_tuple<NamedTuple<TupContent...>, NamedTuple<TupContent2...>,
Tail...> {
using type =
typename define_named_tuple<NamedTuple<TupContent..., TupContent2...>,
Tail...>::type;
};
/// Allows you to combine several named tuples and/or additional fields.
template <class... TupContent>
struct define_named_tuple<NamedTuple<TupContent...>> {
using type = NamedTuple<TupContent...>;
};
} // namespace internal
} // namespace rfl
#endif

View File

@@ -0,0 +1,57 @@
#ifndef RFL_INTERNAL_DEFINETAGGEDUNION_HPP_
#define RFL_INTERNAL_DEFINETAGGEDUNION_HPP_
#include "../TaggedUnion.hpp"
#include "StringLiteral.hpp"
namespace rfl {
namespace internal {
/// Allows you to combine several tagged unions.
template <StringLiteral _discriminator, class... TaggedUnionTypes>
struct define_tagged_union;
/// Recursive case - both tagged union.
template <StringLiteral _discriminator, class... NamedTupleTypes1,
class... NamedTupleTypes2, class... Tail>
struct define_tagged_union<
_discriminator, TaggedUnion<_discriminator, NamedTupleTypes1...>,
TaggedUnion<_discriminator, NamedTupleTypes2...>, Tail...> {
using type = typename define_tagged_union<
_discriminator,
TaggedUnion<_discriminator, NamedTupleTypes1..., NamedTupleTypes2...>,
Tail...>::type;
};
/// Recursive case - tagged union plus named tuple.
template <StringLiteral _discriminator, class... NamedTupleTypes,
class... FieldTypes, class... Tail>
struct define_tagged_union<_discriminator,
TaggedUnion<_discriminator, NamedTupleTypes...>,
NamedTuple<FieldTypes...>, Tail...> {
using type = typename define_tagged_union<
_discriminator,
TaggedUnion<_discriminator, NamedTupleTypes...,
NamedTuple<FieldTypes...>>,
Tail...>::type;
};
/// Recursive case - named tuple.
template <StringLiteral _discriminator, class... FieldTypes, class... Tail>
struct define_tagged_union<_discriminator, NamedTuple<FieldTypes...>, Tail...> {
using type = typename define_tagged_union<
_discriminator, TaggedUnion<_discriminator, NamedTuple<FieldTypes...>>,
Tail...>::type;
};
/// Special case - only a single TaggedUnion is left.
template <StringLiteral _discriminator, class... NamedTupleTypes>
struct define_tagged_union<_discriminator,
TaggedUnion<_discriminator, NamedTupleTypes...>> {
using type = TaggedUnion<_discriminator, NamedTupleTypes...>;
};
} // namespace internal
} // namespace rfl
#endif

View File

@@ -0,0 +1,42 @@
#ifndef RFL_INTERNAL_DEFINEVARIANT_HPP_
#define RFL_INTERNAL_DEFINEVARIANT_HPP_
#include <variant>
namespace rfl {
namespace internal {
/// Allows you to combine several variants.
template <class... Vars>
struct define_variant;
/// Recursive case - both variants.
template <class... Vars1, class... Vars2, class... Tail>
struct define_variant<std::variant<Vars1...>, std::variant<Vars2...>, Tail...> {
using type = typename define_variant<std::variant<Vars1..., Vars2...>,
Tail...>::type;
};
/// Recursive case - variant plus other type.
template <class... Vars, class Head, class... Tail>
struct define_variant<std::variant<Vars...>, Head, Tail...> {
using type =
typename define_variant<std::variant<Vars..., Head>, Tail...>::type;
};
/// Recursive case - other type.
template <class Head, class... Tail>
struct define_variant<Head, Tail...> {
using type = typename define_variant<std::variant<Head>, Tail...>::type;
};
/// Special case - only a single variant is left.
template <class... Vars>
struct define_variant<std::variant<Vars...>> {
using type = std::variant<Vars...>;
};
} // namespace internal
} // namespace rfl
#endif

View File

@@ -0,0 +1,29 @@
#ifndef RFL_INTERNAL_ELEMENT_INDEX_HPP_
#define RFL_INTERNAL_ELEMENT_INDEX_HPP_
#include <type_traits>
namespace rfl::internal {
template <int _i, class T>
consteval int find_element_index() {
return -1;
}
template <int _i, class T, class Head, class... Tail>
consteval int find_element_index() {
if constexpr (std::is_same_v<T, Head>) {
return _i;
} else {
return find_element_index<_i + 1, T, Tail...>();
}
}
template <class T, class... AlternativeTypes>
consteval int element_index() {
return find_element_index<0, T, AlternativeTypes...>();
}
} // namespace rfl::internal
#endif

View File

@@ -0,0 +1,69 @@
#ifndef RFL_INTERNAL_ENUMS_NAMES_HPP_
#define RFL_INTERNAL_ENUMS_NAMES_HPP_
#include <array>
#include <string_view>
#include <type_traits>
#include <utility>
#include "../../Literal.hpp"
//#include "../../config.hpp"
//#include "../../define_literal.hpp"
#include "../../make_named_tuple.hpp"
//#include "../../thirdparty/enchantum/enchantum.hpp"
#include "../StringLiteral.hpp"
namespace rfl::internal::enums {
template <class EnumType, class LiteralType, size_t N, bool _is_flag,
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;
/// A list of all the possible enums
constexpr static auto enums_ = std::array<EnumType, N>{_enums...};
};
template <class EnumType, size_t N, bool _is_flag, StringLiteral... _names,
auto... _enums>
auto names_to_enumerator_named_tuple(
Names<EnumType, Literal<_names...>, N, _is_flag, _enums...>) {
return make_named_tuple(Field<_names, EnumType>{_enums}...);
}
template <class EnumType, size_t N, bool _is_flag, StringLiteral... _names,
auto... _enums>
auto names_to_underlying_enumerator_named_tuple(
Names<EnumType, Literal<_names...>, N, _is_flag, _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, bool _is_flag, StringLiteral... _names,
auto... _enums>
constexpr std::array<std::pair<std::string_view, EnumType>, N>
names_to_enumerator_array(
Names<EnumType, Literal<_names...>, N, _is_flag, _enums...>) {
return {
std::make_pair(LiteralHelper<_names>::name_.string_view(), _enums)...};
}
template <class EnumType, size_t N, bool _is_flag, 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, _is_flag, _enums...>) {
return {
std::make_pair(LiteralHelper<_names>::name_.string_view(),
static_cast<std::underlying_type_t<EnumType>>(_enums))...};
}
} // namespace rfl::internal::enums
#endif

View File

@@ -0,0 +1,129 @@
#ifndef RFL_INTERNAL_ENUMS_GET_ENUM_NAMES_HPP_
#define RFL_INTERNAL_ENUMS_GET_ENUM_NAMES_HPP_
// Enum values must be greater than or equal to RFL_ENUM_RANGE_MIN.
// By default, RFL_ENUM_RANGE_MIN is set to -256.
// To change the default minimum range for all enum types, redefine the macro
// RFL_ENUM_RANGE_MIN.
#if !defined(RFL_ENUM_RANGE_MIN)
#define RFL_ENUM_RANGE_MIN -256
#endif
// Enum values must be less than or equal to RFL_ENUM_RANGE_MAX.
// By default, RFL_ENUM_RANGE_MAX is set to 256.
// To change the default maximum range for all enum types, redefine the macro
// RFL_ENUM_RANGE_MAX.
#if !defined(RFL_ENUM_RANGE_MAX)
#define RFL_ENUM_RANGE_MAX 256
#endif
#ifdef ENCHANTUM_MIN_RANGE
#undef ENCHANTUM_MIN_RANGE
#endif
#define ENCHANTUM_MIN_RANGE RFL_ENUM_RANGE_MIN
#ifdef ENCHANTUM_MAX_RANGE
#undef ENCHANTUM_MAX_RANGE
#endif
#define ENCHANTUM_MAX_RANGE RFL_ENUM_RANGE_MAX
#include <concepts>
#include <utility>
#include "../../thirdparty/enchantum/enchantum.hpp"// NOLINT(unused-includes)
#include "../../Literal.hpp"
#include "Names.hpp"
#include "range_defined.hpp"
// https://en.cppreference.com/w/cpp/language/static_cast:
// 8) A value of integer or enumeration type can be converted to any complete
// enumeration type.
// If the underlying type is not fixed, the behavior is undefined if the value
// of expression is out of range (the range is all values possible for the
// smallest bit-field large enough to hold all enumerators of the target
// enumeration). If the underlying type is fixed, the result is the same as
// converting the original value first to the underlying type of the enumeration
// and then to the enumeration type.
// https://en.cppreference.com/w/cpp/language/enum
// enum struct|class name { enumerator = constexpr , enumerator = constexpr ,
// ... } (1)
// ...
// 1) declares a scoped enumeration type whose underlying type is int (the
// keywords class and struct are exactly equivalent)
//
// --> These rules taken together imply that if you EITHER fix the type OR you
// use a scoped integer, static_cast<MyEnum>(some_integer_value) will always be
// defined.
template <enchantum::Enum E>
requires requires(E e) {
{ e | e } -> std::same_as<E>;
}
constexpr inline bool enchantum::is_bitflag<E> = true;
// Specialize the enchantum EnumTraits further, so rfl::config::enum_range
// works.
namespace enchantum {
template <SignedEnum E>
requires rfl::internal::enums::range_defined<E>
struct enum_traits<E> {
static constexpr std::size_t prefix_length = 0;
static constexpr auto min = rfl::config::enum_range<E>::min;
static constexpr auto max = rfl::config::enum_range<E>::max;
};
template <UnsignedEnum E>
requires rfl::internal::enums::range_defined<E>
struct enum_traits<E> {
static constexpr std::size_t prefix_length = 0;
static constexpr auto min = rfl::config::enum_range<E>::min;
static constexpr auto max = rfl::config::enum_range<E>::max;
};
template <UnscopedEnum E>
requires SignedEnum<E> &&
(!EnumFixedUnderlying<E>) && rfl::internal::enums::range_defined<E>
struct enum_traits<E> {
static constexpr auto min = rfl::config::enum_range<E>::min;
static constexpr auto max = rfl::config::enum_range<E>::max;
};
template <UnscopedEnum E>
requires UnsignedEnum<E> &&
(!EnumFixedUnderlying<E>) && rfl::internal::enums::range_defined<E>
struct enum_traits<E> {
static constexpr auto min = rfl::config::enum_range<E>::min;
static constexpr auto max = rfl::config::enum_range<E>::max;
};
} // namespace enchantum
namespace rfl::internal::enums {
template <enchantum::Enum EnumType>
consteval auto get_enum_names() {
return []<std::size_t... Is>(std::index_sequence<Is...>) {
constexpr auto& entries = enchantum::entries<EnumType>;
constexpr auto to_str_lit =
[]<std::size_t... Js>(const char* name, std::index_sequence<Js...>) {
return StringLiteral<sizeof...(Js) + 1>{name[Js]...};
};
return Names<EnumType,
Literal<to_str_lit(
entries[Is].second.data(),
std::make_index_sequence<entries[Is].second.size()>{})...>,
entries.size(), enchantum::is_bitflag<EnumType>,
entries[Is].first...>{};
}(std::make_index_sequence<enchantum::count<EnumType>>{});
}
} // namespace rfl::internal::enums
#endif

View File

@@ -0,0 +1,17 @@
#ifndef RFL_INTERNAL_ENUMS_RANGE_DEFINED_HPP_
#define RFL_INTERNAL_ENUMS_RANGE_DEFINED_HPP_
#include "../../config.hpp"
namespace rfl::internal::enums {
template <class E>
concept range_defined = requires {
{ config::enum_range<E>::min };
{ config::enum_range<E>::max };
};
} // namespace rfl::internal::enums
#endif

View File

@@ -0,0 +1,25 @@
#ifndef RFL_INTERNAL_EXTRACTDISTRIMINATORS_HPP_
#define RFL_INTERNAL_EXTRACTDISTRIMINATORS_HPP_
#include <type_traits>
#include "../TaggedUnion.hpp"
#include "../define_literal.hpp"
#include "../field_type.hpp"
namespace rfl {
namespace internal {
template <class TaggedUnionType>
struct extract_discriminators;
template <StringLiteral _discriminator, class... NamedTupleType>
struct extract_discriminators<TaggedUnion<_discriminator, NamedTupleType...>> {
using type = define_literal_t<
std::remove_cvref_t<field_type_t<_discriminator, NamedTupleType>>...>;
};
} // namespace internal
} // namespace rfl
#endif

View File

@@ -0,0 +1,18 @@
#ifndef RFL_INTERNAL_FIELD_TUPLE_T_HPP_
#define RFL_INTERNAL_FIELD_TUPLE_T_HPP_
#include <type_traits>
#include "copy_to_field_tuple.hpp"
namespace rfl {
namespace internal {
template <class T>
using field_tuple_t =
typename std::invoke_result<decltype(copy_to_field_tuple<T>), T>::type;
}
} // namespace rfl
#endif

View File

@@ -0,0 +1,64 @@
#ifndef RFL_INTERNAL_FIELD_TYPE_HPP_
#define RFL_INTERNAL_FIELD_TYPE_HPP_
#include <type_traits>
#include <variant>
//#include "../NamedTuple.hpp"
#include "../TaggedUnion.hpp"
#include "../Tuple.hpp"
#include "../named_tuple_t.hpp"
#include "StringLiteral.hpp"
#include "find_index.hpp"
namespace rfl {
namespace internal {
template <class T, class... Ts>
struct are_same : std::conjunction<std::is_same<T, Ts>...> {};
/// Finds the type of the field signified by _field_name
template <StringLiteral _field_name, class T>
struct FieldType;
/// Default option - for named tuples.
template <StringLiteral _field_name, class T>
struct FieldType {
using NamedTupleType = named_tuple_t<T>;
static constexpr int field_ix_ =
internal::find_index<_field_name, typename NamedTupleType::Fields>();
using Type = typename tuple_element_t<field_ix_,
typename NamedTupleType::Fields>::Type;
};
/// For variants - in this case the FieldType returned by all options must be
/// the same.
template <StringLiteral _field_name, class FirstAlternativeType,
class... OtherAlternativeTypes>
struct FieldType<_field_name,
std::variant<FirstAlternativeType, OtherAlternativeTypes...>> {
constexpr static bool all_types_match = std::conjunction_v<std::is_same<
typename FieldType<_field_name, FirstAlternativeType>::Type,
typename FieldType<_field_name, OtherAlternativeTypes>::Type>...>;
static_assert(all_types_match, "All field types must be the same.");
using Type = typename FieldType<_field_name, FirstAlternativeType>::Type;
};
/// For tagged union - just defers to the variant.
template <StringLiteral _field_name, StringLiteral _discriminator_name,
class... VarTypes>
struct FieldType<_field_name, TaggedUnion<_discriminator_name, VarTypes...>> {
using Type =
typename FieldType<_field_name,
typename TaggedUnion<_discriminator_name,
VarTypes...>::VariantType>::Type;
};
} // namespace internal
} // namespace rfl
#endif

View File

@@ -0,0 +1,66 @@
#ifndef RFL_FIND_INDEX_HPP_
#define RFL_FIND_INDEX_HPP_
#include "../Tuple.hpp"
#include "StringLiteral.hpp"
namespace rfl {
namespace internal {
template <class FieldType, StringLiteral _field_name, int _i>
struct FieldWrapper {
constexpr static int i_ = _i;
};
template <StringLiteral _field_name, class F1, int _i1, class F2, int _i2>
constexpr auto operator|(const FieldWrapper<F1, _field_name, _i1>& _f1,
const FieldWrapper<F2, _field_name, _i2>& _f2) {
if constexpr (F1::name_ == _field_name) {
return _f1;
} else {
return _f2;
}
}
template <class Head, class... Tail>
constexpr auto find_matching_field(const Head& _head, const Tail&... _tail) {
return (_head | ... | _tail);
};
template <StringLiteral _field_name, class Fields, int... _is>
constexpr auto wrap_fields(std::integer_sequence<int, _is...>) {
return find_matching_field(FieldWrapper<rfl::tuple_element_t<_is, Fields>,
_field_name, _is>{}...)
.i_;
}
/// Finds the index of the field signified by _field_name
template <StringLiteral _field_name, class Fields>
constexpr static int find_index() {
constexpr int ix = wrap_fields<_field_name, Fields>(
std::make_integer_sequence<int, rfl::tuple_size_v<Fields>>());
static_assert(rfl::tuple_element_t<ix, Fields>::name_ == _field_name,
"No matching field found.");
return ix;
}
/// Finds the index of the field signified by _field_name or -1.
template <StringLiteral _field_name, class Fields>
constexpr static int find_index_or_minus_one() {
if constexpr (rfl::tuple_size_v<Fields> == 0) {
return -1;
} else {
constexpr int ix = wrap_fields<_field_name, Fields>(
std::make_integer_sequence<int, rfl::tuple_size_v<Fields>>());
if constexpr (rfl::tuple_element_t<ix, Fields>::name_ == _field_name) {
return ix;
} else {
return -1;
}
}
}
} // namespace internal
} // namespace rfl
#endif

View File

@@ -0,0 +1,18 @@
#ifndef RFL_INTERNAL_FLATTENED_PTR_TUPLE_T_HPP_
#define RFL_INTERNAL_FLATTENED_PTR_TUPLE_T_HPP_
#include <type_traits>
#include "to_flattened_ptr_tuple.hpp"
namespace rfl {
namespace internal {
template <class T>
using flattened_ptr_tuple_t =
typename std::invoke_result<decltype(to_flattened_ptr_tuple<T>), T>::type;
} // namespace internal
} // namespace rfl
#endif

Some files were not shown because too many files have changed in this diff Show More