This commit is contained in:
Mars 2024-06-08 14:10:59 -04:00
parent a743cdabe5
commit bd402f57f5
Signed by: pupbrained
GPG key ID: 874E22DF2F9DFCB5
276 changed files with 37936 additions and 22932 deletions

View file

@ -2,6 +2,8 @@
AlignConsecutiveAssignments: true AlignConsecutiveAssignments: true
AllowShortBlocksOnASingleLine: Always AllowShortBlocksOnASingleLine: Always
AllowShortCompoundRequirementOnASingleLine: true AllowShortCompoundRequirementOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: true
AllowShortIfStatementsOnASingleLine: WithoutElse AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLoopsOnASingleLine: true AllowShortLoopsOnASingleLine: true
BasedOnStyle: Chromium BasedOnStyle: Chromium

View file

@ -16,9 +16,26 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs_2": {
"locked": {
"lastModified": 1708475490,
"narHash": "sha256-g1v0TsWBQPX97ziznfJdWhgMyMGtoBFs102xSYO4syU=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "0e74ca98a74bc7270d28838369593635a5db3260",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": { "root": {
"inputs": { "inputs": {
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"treefmt-nix": "treefmt-nix",
"utils": "utils" "utils": "utils"
} }
}, },
@ -37,6 +54,24 @@
"type": "github" "type": "github"
} }
}, },
"treefmt-nix": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1717850719,
"narHash": "sha256-npYqVg+Wk4oxnWrnVG7416fpfrlRhp/lQ6wQ4DHI8YE=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "4fc1c45a5f50169f9f29f6a98a438fb910b834ed",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"utils": { "utils": {
"inputs": { "inputs": {
"systems": "systems" "systems": "systems"

View file

@ -3,12 +3,14 @@
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
treefmt-nix.url = "github:numtide/treefmt-nix";
utils.url = "github:numtide/flake-utils"; utils.url = "github:numtide/flake-utils";
}; };
outputs = { outputs = {
self, self,
nixpkgs, nixpkgs,
treefmt-nix,
utils, utils,
... ...
}: }:
@ -18,7 +20,7 @@
inherit system; inherit system;
overlays = [ overlays = [
(self: super: { (_self: super: {
ccacheWrapper = super.ccacheWrapper.override { ccacheWrapper = super.ccacheWrapper.override {
extraConfig = '' extraConfig = ''
export CCACHE_COMPRESS=1 export CCACHE_COMPRESS=1
@ -54,9 +56,8 @@
deps = with ( deps = with (
if !stdenv.isDarwin if !stdenv.isDarwin
then pkgs.pkgsStatic then pkgs.pkgsStatic
else pkgs else pkgs # TODO: Remove when fixed on darwin
); # TODO: Remove when fixed on darwin );
[ [
curl curl
fmt fmt
@ -110,7 +111,18 @@
default = draconisplusplus; default = draconisplusplus;
}; };
formatter = alejandra; formatter = treefmt-nix.lib.mkWrapper pkgs {
projectRootFile = "flake.nix";
programs = {
alejandra.enable = true;
deadnix.enable = true;
clang-format = {
enable = true;
package = pkgs.clang-tools_18;
};
};
};
devShell = mkShell.override {inherit stdenv;} { devShell = mkShell.override {inherit stdenv;} {
packages = packages =

File diff suppressed because it is too large Load diff

View file

@ -12,21 +12,22 @@
namespace rfl { namespace rfl {
template <internal::StringLiteral field_name_> template <internal::StringLiteral field_name_>
struct AddStructName { struct AddStructName {
/// Adds the name of the struct as a new field. /// Adds the name of the struct as a new field.
template <class StructType> template <class StructType>
static auto process(auto&& _view) { static auto process(auto&& _view) {
using LiteralType = Literal< using LiteralType = Literal<
internal::remove_namespaces<internal::get_type_name<StructType>()>()>; internal::remove_namespaces<internal::get_type_name<StructType>()>()>;
using FieldType = Field<field_name_, LiteralType>; using FieldType = Field<field_name_, LiteralType>;
const auto add_new_field = [](auto&&... _fields) { const auto add_new_field = [](auto&&... _fields) {
return make_named_tuple(FieldType(LiteralType()), std::move(_fields)...); return make_named_tuple(FieldType(LiteralType()),
}; std::move(_fields)...);
return std::apply(add_new_field, std::move(_view.fields())); };
} return std::apply(add_new_field, std::move(_view.fields()));
}; }
};
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -8,33 +8,33 @@
namespace rfl { namespace rfl {
/// Requires that all of the contraints C and Cs be true. /// Requires that all of the contraints C and Cs be true.
template <class C, class... Cs> template <class C, class... Cs>
struct AllOf { struct AllOf {
template <class T> template <class T>
static rfl::Result<T> validate(T _value) noexcept { static rfl::Result<T> validate(T _value) noexcept {
return validate_impl<T, C, Cs...>(_value); 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 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 #endif

View file

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

View file

@ -12,125 +12,129 @@
namespace rfl { namespace rfl {
template <class T> template <class T>
struct Attribute { struct Attribute {
using Type = T; using Type = T;
using ReflectionType = T; using ReflectionType = T;
Attribute() : value_(Type()) {} Attribute() : value_(Type()) {}
Attribute(const Type& _value) : value_(_value) {} Attribute(const Type& _value) : value_(_value) {}
Attribute(Type&& _value) noexcept : value_(std::move(_value)) {} Attribute(Type&& _value) noexcept : value_(std::move(_value)) {}
Attribute(Attribute<T>&& _attr) noexcept = default; Attribute(Attribute<T>&& _attr) noexcept = default;
Attribute(const Attribute<Type>& _attr) = default; Attribute(const Attribute<Type>& _attr) = default;
template <class U> template <class U>
Attribute(const Attribute<U>& _attr) : value_(_attr.get()) {} Attribute(const Attribute<U>& _attr) : value_(_attr.get()) {}
template <class U> template <class U>
Attribute(Attribute<U>&& _attr) : value_(_attr.get()) {} Attribute(Attribute<U>&& _attr) : value_(_attr.get()) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>, template <class U,
bool>::type = true> typename std::enable_if<std::is_convertible_v<U, Type>,
Attribute(const U& _value) : value_(_value) {} bool>::type = true>
Attribute(const U& _value) : value_(_value) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>, template <class U,
bool>::type = true> typename std::enable_if<std::is_convertible_v<U, Type>,
Attribute(U&& _value) noexcept : value_(std::forward<U>(_value)) {} 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>, template <class U,
bool>::type = true> typename std::enable_if<std::is_convertible_v<U, Type>,
Attribute(const Attribute<U>& _attr) : value_(_attr.value()) {} bool>::type = true>
Attribute(const Attribute<U>& _attr) : value_(_attr.value()) {}
/// Assigns the underlying object to its default value. /// Assigns the underlying object to its default value.
template <class U = Type, template <class U = Type,
typename std::enable_if<std::is_default_constructible_v<U>, typename std::enable_if<std::is_default_constructible_v<U>,
bool>::type = true> bool>::type = true>
Attribute(const Default& _default) : value_(Type()) {} Attribute(const Default& _default) : value_(Type()) {}
~Attribute() = default; ~Attribute() = default;
/// Returns the underlying object. /// Returns the underlying object.
const Type& get() const { return value_; } const Type& get() const { return value_; }
/// Returns the underlying object. /// Returns the underlying object.
Type& operator()() { return value_; } Type& operator()() { return value_; }
/// Returns the underlying object. /// Returns the underlying object.
const Type& operator()() const { return value_; } const Type& operator()() const { return value_; }
/// Assigns the underlying object. /// Assigns the underlying object.
auto& operator=(const Type& _value) { auto& operator=(const Type& _value) {
value_ = _value; value_ = _value;
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
auto& operator=(Type&& _value) noexcept { auto& operator=(Type&& _value) noexcept {
value_ = std::move(_value); value_ = std::move(_value);
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>, template <class U,
bool>::type = true> typename std::enable_if<std::is_convertible_v<U, Type>,
auto& operator=(const U& _value) { bool>::type = true>
value_ = _value; auto& operator=(const U& _value) {
return *this; value_ = _value;
} return *this;
}
/// Assigns the underlying object to its default value. /// Assigns the underlying object to its default value.
template <class U = Type, template <class U = Type,
typename std::enable_if<std::is_default_constructible_v<U>, typename std::enable_if<std::is_default_constructible_v<U>,
bool>::type = true> bool>::type = true>
auto& operator=(const Default& _default) { auto& operator=(const Default& _default) {
value_ = Type(); value_ = Type();
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
Attribute<T>& operator=(const Attribute<T>& _attr) = default; Attribute<T>& operator=(const Attribute<T>& _attr) = default;
/// Assigns the underlying object. /// Assigns the underlying object.
Attribute<T>& operator=(Attribute<T>&& _attr) = default; Attribute<T>& operator=(Attribute<T>&& _attr) = default;
/// Assigns the underlying object. /// Assigns the underlying object.
template <class U> template <class U>
auto& operator=(const Attribute<U>& _attr) { auto& operator=(const Attribute<U>& _attr) {
value_ = _attr.get(); value_ = _attr.get();
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
template <class U> template <class U>
auto& operator=(Attribute<U>&& _attr) { auto& operator=(Attribute<U>&& _attr) {
value_ = std::forward<T>(_attr.value_); value_ = std::forward<T>(_attr.value_);
return *this; return *this;
} }
/// We want all parsers other than the XML parser to treat attributes like /// We want all parsers other than the XML parser to treat attributes like
/// normal fields, so we just implement the reflection interface. /// normal fields, so we just implement the reflection interface.
const ReflectionType& reflection() const { return value_; } const ReflectionType& reflection() const { return value_; }
/// Assigns the underlying object. /// Assigns the underlying object.
void set(const Type& _value) { value_ = _value; } void set(const Type& _value) { value_ = _value; }
/// Assigns the underlying object. /// Assigns the underlying object.
void set(Type&& _value) { value_ = std::move(_value); } void set(Type&& _value) { value_ = std::move(_value); }
/// Returns the underlying object. /// Returns the underlying object.
Type& value() { return value_; } Type& value() { return value_; }
/// Returns the underlying object. /// Returns the underlying object.
const Type& value() const { return value_; } const Type& value() const { return value_; }
/// The underlying value. /// The underlying value.
Type value_; Type value_;
}; };
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -8,118 +8,117 @@
namespace rfl { namespace rfl {
/// The Box class behaves very similarly to the unique_ptr, but unlike the /// 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 /// unique_ptr, it is 100% guaranteed to be filled at all times (unless the
/// tries to access it after calling std::move does something else that is /// user tries to access it after calling std::move does something else that
/// clearly bad practice). /// is clearly bad practice).
template <class T> template <class T>
class Box { class Box {
public: public:
/// The only way of creating new boxes is /// The only way of creating new boxes is
/// Box<T>::make(...). /// Box<T>::make(...).
template <class... Args> template <class... Args>
static Box<T> make(Args&&... _args) { static Box<T> make(Args&&... _args) {
return Box<T>(std::make_unique<T>(std::forward<Args>(_args)...)); return Box<T>(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<T>> make(std::unique_ptr<T>&& _ptr) {
if (!_ptr) {
return Error("std::unique_ptr was a nullptr.");
} }
return Box<T>(std::move(_ptr));
/// 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<T>> make(std::unique_ptr<T>&& _ptr) {
if (!_ptr) { return Error("std::unique_ptr was a nullptr."); }
return Box<T>(std::move(_ptr));
}
Box() : ptr_(std::make_unique<T>()) {}
Box(const Box<T>& _other) = delete;
Box(Box<T>&& _other) = default;
template <class U>
Box(Box<U>&& _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
Box<T>& operator=(const Box<T>& _other) = delete;
/// Move assignment operator
Box<T>& operator=(Box<T>&& _other) noexcept = default;
/// Move assignment operator
template <class U>
Box<T>& 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)...);
} }
Box() : ptr_(std::make_unique<T>()) {} template <class T1, class T2>
inline auto operator<=>(const Box<T1>& _b1, const Box<T2>& _b2) {
Box(const Box<T>& _other) = delete; return _b1.ptr() <=> _b2.ptr();
Box(Box<T>&& _other) = default;
template <class U>
Box(Box<U>&& _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
Box<T>& operator=(const Box<T>& _other) = delete;
/// Move assignment operator
Box<T>& operator=(Box<T>&& _other) noexcept = default;
/// Move assignment operator
template <class U>
Box<T>& operator=(Box<U>&& _other) noexcept {
ptr_ = std::forward<std::unique_ptr<U>>(_other.ptr());
return *this;
} }
/// Returns the underlying object. template <class CharT, class Traits, class T>
T& operator*() { return *ptr_; } inline std::basic_ostream<CharT, Traits>& operator<<(
std::basic_ostream<CharT, Traits>& _os,
const Box<T>& _b) {
_os << _b.get();
return _os;
}
/// Returns the underlying object. } // namespace rfl
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 <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 { namespace std {
template <class T> template <class T>
struct hash<rfl::Box<T>> { struct hash<rfl::Box<T>> {
size_t operator()(const rfl::Box<T>& _b) const { size_t operator()(const rfl::Box<T>& _b) const {
return hash<unique_ptr<T>>()(_b.ptr()); return hash<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());
} }
};
template <class T> } // namespace std
inline void swap(rfl::Box<T>& _b1, rfl::Box<T>& _b2) {
return swap(_b1.ptr(), _b2.ptr());
}
} // namespace std
#endif #endif

View file

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

View file

@ -16,139 +16,143 @@
namespace rfl { namespace rfl {
/// Used to define a field in the NamedTuple. /// Used to define a field in the NamedTuple.
template <internal::StringLiteral _name, class T> template <internal::StringLiteral _name, class T>
struct Field { struct Field {
/// The underlying type. /// The underlying type.
using Type = internal::wrap_in_rfl_array_t<T>; using Type = internal::wrap_in_rfl_array_t<T>;
/// The name of the field. /// The name of the field.
using Name = rfl::Literal<_name>; using Name = rfl::Literal<_name>;
Field(const Type& _value) : value_(_value) {} Field(const Type& _value) : value_(_value) {}
Field(Type&& _value) noexcept : value_(std::move(_value)) {} Field(Type&& _value) noexcept : value_(std::move(_value)) {}
Field(Field<_name, T>&& _field) noexcept = default; Field(Field<_name, T>&& _field) noexcept = default;
Field(const Field<_name, T>& _field) = default; Field(const Field<_name, T>& _field) = default;
template <class U> template <class U>
Field(const Field<_name, U>& _field) : value_(_field.get()) {} Field(const Field<_name, U>& _field) : value_(_field.get()) {}
template <class U> template <class U>
Field(Field<_name, U>&& _field) : value_(_field.get()) {} Field(Field<_name, U>&& _field) : value_(_field.get()) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>, template <class U,
bool>::type = true> typename std::enable_if<std::is_convertible_v<U, Type>,
Field(const U& _value) : value_(_value) {} bool>::type = true>
Field(const U& _value) : value_(_value) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>, template <class U,
bool>::type = true> typename std::enable_if<std::is_convertible_v<U, Type>,
Field(U&& _value) noexcept : value_(std::forward<U>(_value)) {} 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>, template <class U,
bool>::type = true> typename std::enable_if<std::is_convertible_v<U, Type>,
Field(const Field<_name, U>& _field) : value_(_field.value()) {} bool>::type = true>
Field(const Field<_name, U>& _field) : value_(_field.value()) {}
/// Assigns the underlying object to its default value. /// Assigns the underlying object to its default value.
template <class U = Type, template <class U = Type,
typename std::enable_if<std::is_default_constructible_v<U>, typename std::enable_if<std::is_default_constructible_v<U>,
bool>::type = true> bool>::type = true>
Field(const Default& _default) : value_(Type()) {} Field(const Default& _default) : value_(Type()) {}
~Field() = default; ~Field() = default;
/// The name of the field, for internal use. /// The name of the field, for internal use.
constexpr static const internal::StringLiteral name_ = _name; constexpr static const internal::StringLiteral name_ = _name;
/// Returns the underlying object. /// Returns the underlying object.
const Type& get() const { return value_; } const Type& get() const { return value_; }
/// The name of the field. /// The name of the field.
constexpr static std::string_view name() { return name_.string_view(); } constexpr static std::string_view name() { return name_.string_view(); }
/// Returns the underlying object. /// Returns the underlying object.
Type& operator()() { return value_; } Type& operator()() { return value_; }
/// Returns the underlying object. /// Returns the underlying object.
const Type& operator()() const { return value_; } const Type& operator()() const { return value_; }
/// Assigns the underlying object. /// Assigns the underlying object.
auto& operator=(const Type& _value) { auto& operator=(const Type& _value) {
value_ = _value; value_ = _value;
return *this; 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& _default) {
value_ = Type();
return *this;
}
/// Assigns the underlying object.
Field<_name, T>& operator=(const Field<_name, T>& _field) = default;
/// Assigns the underlying object.
Field<_name, T>& operator=(Field<_name, T>&& _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));
}
} }
/// Assigns the underlying object. } // namespace rfl
auto& operator=(Type&& _value) noexcept {
value_ = std::move(_value);
return *this;
}
/// Assigns the underlying object. #endif // RFL_FIELD_HPP_
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& _default) {
value_ = Type();
return *this;
}
/// Assigns the underlying object.
Field<_name, T>& operator=(const Field<_name, T>& _field) = default;
/// Assigns the underlying object.
Field<_name, T>& operator=(Field<_name, T>&& _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

@ -9,98 +9,101 @@
namespace rfl { namespace rfl {
/// Used to embed another struct into the generated output. /// Used to embed another struct into the generated output.
template <class T> template <class T>
struct Flatten { struct Flatten {
/// The underlying type. /// The underlying type.
using Type = std::remove_cvref_t<T>; using Type = std::remove_cvref_t<T>;
Flatten(const Type& _value) : value_(_value) {} Flatten(const Type& _value) : value_(_value) {}
Flatten(Type&& _value) noexcept : value_(std::forward<Type>(_value)) {} Flatten(Type&& _value) noexcept : value_(std::forward<Type>(_value)) {}
Flatten(const Flatten<T>& _f) = default; Flatten(const Flatten<T>& _f) = default;
Flatten(Flatten<T>&& _f) noexcept = default; Flatten(Flatten<T>&& _f) noexcept = default;
template <class U> template <class U>
Flatten(const Flatten<U>& _f) : value_(_f.get()) {} Flatten(const Flatten<U>& _f) : value_(_f.get()) {}
template <class U> template <class U>
Flatten(Flatten<U>&& _f) : value_(_f.get()) {} Flatten(Flatten<U>&& _f) : value_(_f.get()) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>, template <class U,
bool>::type = true> typename std::enable_if<std::is_convertible_v<U, Type>,
Flatten(const U& _value) : value_(_value) {} bool>::type = true>
Flatten(const U& _value) : value_(_value) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>, template <class U,
bool>::type = true> typename std::enable_if<std::is_convertible_v<U, Type>,
Flatten(U&& _value) : value_(_value) {} bool>::type = true>
Flatten(U&& _value) : value_(_value) {}
~Flatten() = default; ~Flatten() = default;
/// Returns the underlying object. /// Returns the underlying object.
Type& get() { return value_; } Type& get() { return value_; }
/// Returns the underlying object. /// Returns the underlying object.
const Type& get() const { return value_; } const Type& get() const { return value_; }
/// Returns the underlying object. /// Returns the underlying object.
Type& operator()() { return value_; } Type& operator()() { return value_; }
/// Returns the underlying object. /// Returns the underlying object.
const Type& operator()() const { return value_; } const Type& operator()() const { return value_; }
/// Assigns the underlying object. /// Assigns the underlying object.
Flatten<T>& operator=(const T& _value) { Flatten<T>& operator=(const T& _value) {
value_ = _value; value_ = _value;
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
Flatten<T>& operator=(T&& _value) { Flatten<T>& operator=(T&& _value) {
value_ = std::forward<Type>(_value); value_ = std::forward<Type>(_value);
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>, template <class U,
bool>::type = true> typename std::enable_if<std::is_convertible_v<U, Type>,
Flatten<T>& operator=(const U& _value) { bool>::type = true>
value_ = _value; Flatten<T>& operator=(const U& _value) {
return *this; value_ = _value;
} return *this;
}
/// Assigns the underlying object. /// Assigns the underlying object.
Flatten<T>& operator=(const Flatten<T>& _f) = default; Flatten<T>& operator=(const Flatten<T>& _f) = default;
/// Assigns the underlying object. /// Assigns the underlying object.
Flatten<T>& operator=(Flatten<T>&& _f) = default; Flatten<T>& operator=(Flatten<T>&& _f) = default;
/// Assigns the underlying object. /// Assigns the underlying object.
template <class U> template <class U>
Flatten<T>& operator=(const Flatten<U>& _f) { Flatten<T>& operator=(const Flatten<U>& _f) {
value_ = _f.get(); value_ = _f.get();
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
template <class U> template <class U>
Flatten<T>& operator=(Flatten<U>&& _f) { Flatten<T>& operator=(Flatten<U>&& _f) {
value_ = std::forward<U>(_f); value_ = std::forward<U>(_f);
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
void set(const Type& _value) { value_ = _value; } void set(const Type& _value) { value_ = _value; }
/// Assigns the underlying object. /// Assigns the underlying object.
void set(Type&& _value) { value_ = std::forward<Type>(_value); } void set(Type&& _value) { value_ = std::forward<Type>(_value); }
/// The underlying value. /// The underlying value.
Type value_; Type value_;
}; };
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -15,360 +15,361 @@
namespace rfl { namespace rfl {
template <internal::StringLiteral _field> template <internal::StringLiteral _field>
struct LiteralHelper { struct LiteralHelper {
constexpr static internal::StringLiteral field_ = _field; constexpr static internal::StringLiteral field_ = _field;
};
template <internal::StringLiteral... fields_>
class Literal {
using FieldsType = std::tuple<LiteralHelper<fields_>...>;
public:
using ValueType =
std::conditional_t<sizeof...(fields_) <=
std::numeric_limits<std::uint8_t>::max(),
std::uint8_t, std::uint16_t>;
/// The number of different fields or different options that the literal
/// can assume.
static constexpr ValueType num_fields_ = sizeof...(fields_);
using ReflectionType = std::string;
/// Constructs a Literal from another literal.
Literal(const Literal<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()) {}
/// A single-field literal is special because it
/// can also have a default constructor.
template <ValueType num_fields = num_fields_,
typename = std::enable_if_t<num_fields <= 1>>
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, int _i = 0>
static constexpr bool contains_any() {
if constexpr (_i == num_fields_) {
return false;
} else {
constexpr auto name = find_name_within_own_fields<_i>();
return OtherLiteralType::template contains<name>() ||
contains_any<OtherLiteralType, _i + 1>();
}
}
/// Determines whether the literal contains all of the strings in the other
/// literal at compile time.
template <class OtherLiteralType, int _i = 0, int _n_found = 0>
static constexpr bool contains_all() {
if constexpr (_i == num_fields_) {
return _n_found == OtherLiteralType::num_fields_;
} else {
constexpr auto name = find_name_within_own_fields<_i>();
if constexpr (OtherLiteralType::template contains<name>()) {
return contains_all<OtherLiteralType, _i + 1, _n_found + 1>();
} else {
return contains_all<OtherLiteralType, _i + 1, _n_found>();
}
}
}
/// Determines whether the literal has duplicate strings at compile time.
/// These is useful for checking collections of strings in other contexts.
static constexpr bool has_duplicates() { return has_duplicate_strings(); }
/// Constructs a Literal from a string. Returns an error if the string
/// cannot be found.
static Result<Literal> from_string(const std::string& _str) {
const auto to_literal = [](const auto& _v) {
return Literal<fields_...>(_v);
};
return find_value(_str).transform(to_literal);
}; };
/// The name defined by the Literal. template <internal::StringLiteral... fields_>
std::string name() const { return find_name(); } class Literal {
using FieldsType = std::tuple<LiteralHelper<fields_>...>;
/// Returns all possible values of the literal as a std::vector<std::string>. public:
static std::vector<std::string> names() { return allowed_strings_vec(); } using ValueType =
std::conditional_t<sizeof...(fields_) <=
std::numeric_limits<std::uint8_t>::max(),
std::uint8_t,
std::uint16_t>;
/// The number of different fields or different options that the literal
/// can assume.
static constexpr ValueType num_fields_ = sizeof...(fields_);
using ReflectionType = std::string;
/// Constructs a Literal from another literal.
Literal(const Literal<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()) {}
/// A single-field literal is special because it
/// can also have a default constructor.
template <ValueType num_fields = num_fields_,
typename = std::enable_if_t<num_fields <= 1>>
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, int _i = 0>
static constexpr bool contains_any() {
if constexpr (_i == num_fields_) {
return false;
} else {
constexpr auto name = find_name_within_own_fields<_i>();
return OtherLiteralType::template contains<name>() ||
contains_any<OtherLiteralType, _i + 1>();
}
}
/// Determines whether the literal contains all of the strings in the other
/// literal at compile time.
template <class OtherLiteralType, int _i = 0, int _n_found = 0>
static constexpr bool contains_all() {
if constexpr (_i == num_fields_) {
return _n_found == OtherLiteralType::num_fields_;
} else {
constexpr auto name = find_name_within_own_fields<_i>();
if constexpr (OtherLiteralType::template contains<name>()) {
return contains_all<OtherLiteralType, _i + 1, _n_found + 1>();
} else {
return contains_all<OtherLiteralType, _i + 1, _n_found>();
}
}
}
/// Determines whether the literal has duplicate strings at compile time.
/// These is useful for checking collections of strings in other contexts.
static constexpr bool has_duplicates() { return has_duplicate_strings(); }
/// Constructs a Literal from a string. Returns an error if the string
/// cannot be found.
static Result<Literal> from_string(const std::string& _str) {
const auto to_literal = [](const auto& _v) {
return Literal<fields_...>(_v);
};
return find_value(_str).transform(to_literal);
};
/// 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(); }
/// 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);
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 {
return name() <=> _str;
}
/// <=> for const char*.
template <internal::StringLiteral... other_fields>
inline auto operator<=>(const char* _str) const {
return name() <=> _str;
}
/// 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(); }
/// 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::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 _i = 0>
static std::vector<std::string> allowed_strings_vec(
std::vector<std::string> _values = {}) {
using FieldType = typename std::tuple_element<_i, FieldsType>::type;
const auto head = FieldType::field_.str();
_values.push_back(head);
if constexpr (_i + 1 < num_fields_) {
return allowed_strings_vec<_i + 1>(std::move(_values));
} else {
return _values;
}
}
/// Whether the Literal contains duplicate strings.
template <int _i = 1>
constexpr static bool has_duplicate_strings() {
if constexpr (_i >= num_fields_) {
return false;
} else {
return is_duplicate<_i>() || has_duplicate_strings<_i + 1>();
}
}
template <int _i, int _j = _i - 1>
constexpr static bool is_duplicate() {
using FieldType1 = typename std::tuple_element<_i, FieldsType>::type;
using FieldType2 = typename std::tuple_element<_j, FieldsType>::type;
if constexpr (FieldType1::field_ == FieldType2::field_) {
return true;
} else if constexpr (_j > 0) {
return is_duplicate<_i, _j - 1>();
} else {
return false;
}
}
/// Finds the correct index associated with
/// the string at run time.
template <int _i = 0>
std::string find_name() const {
if (_i == value_) {
using FieldType = typename std::tuple_element<_i, FieldsType>::type;
return FieldType::field_.str();
}
if constexpr (_i + 1 == num_fields_) {
return "";
} else {
return find_name<_i + 1>();
}
}
/// Finds the correct index associated with
/// the string at compile time within the Literal's own fields.
template <int _i>
constexpr static auto find_name_within_own_fields() {
using FieldType = typename std::tuple_element<_i, FieldsType>::type;
return FieldType::field_;
}
/// Finds the correct value associated with
/// the string at run time.
template <int _i = 0>
static Result<int> find_value(const std::string& _str) {
using FieldType = typename std::tuple_element<_i, FieldsType>::type;
if (FieldType::field_.str() == _str) { return _i; }
if constexpr (_i + 1 == num_fields_) {
return Error("Literal does not support string '" + _str +
"'. The following strings are supported: " +
allowed_strings() + ".");
} else {
return find_value<_i + 1>(_str);
}
}
/// Finds the value of a string literal at compile time.
template <internal::StringLiteral _name, int _i = 0>
static constexpr int find_value_of() {
if constexpr (_i == num_fields_) {
return -1;
} else {
using FieldType = typename std::tuple_element<_i, FieldsType>::type;
if constexpr (FieldType::field_ == _name) {
return _i;
} else {
return find_value_of<_name, _i + 1>();
}
}
}
/// Whether the literal contains this string.
template <int _i = 0>
static void has_value(const std::string& _str, bool* _found) {
if constexpr (_i == num_fields_) {
*_found = false;
return;
} else {
using FieldType = typename std::tuple_element<_i, FieldsType>::type;
if (FieldType::field_.str() == _str) {
*_found = true;
return;
}
return has_value<_i + 1>(_str, _found);
}
}
static_assert(sizeof...(fields_) <= std::numeric_limits<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. /// Helper function to retrieve a name at compile time.
template <int _value> template <class LiteralType, int _value>
constexpr static auto name_of() { inline constexpr auto name_of() {
constexpr auto name = find_name_within_own_fields<_value>(); return LiteralType::template name_of<_value>();
return Literal<name>();
} }
/// Assigns from another literal. /// Helper function to retrieve a value at compile time.
Literal<fields_...>& operator=(const Literal<fields_...>& _other) = default; template <class LiteralType, internal::StringLiteral _name>
inline constexpr auto value_of() {
/// Assigns from another literal. return LiteralType::template value_of<_name>();
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);
return *this;
} }
/// <=> for other Literals with the same fields. } // namespace rfl
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 {
return name() <=> _str;
}
/// <=> for const char*.
template <internal::StringLiteral... other_fields>
inline auto operator<=>(const char* _str) const {
return name() <=> _str;
}
/// 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(); }
/// 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::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 _i = 0>
static std::vector<std::string> allowed_strings_vec(
std::vector<std::string> _values = {}) {
using FieldType = typename std::tuple_element<_i, FieldsType>::type;
const auto head = FieldType::field_.str();
_values.push_back(head);
if constexpr (_i + 1 < num_fields_) {
return allowed_strings_vec<_i + 1>(std::move(_values));
} else {
return _values;
}
}
/// Whether the Literal contains duplicate strings.
template <int _i = 1>
constexpr static bool has_duplicate_strings() {
if constexpr (_i >= num_fields_) {
return false;
} else {
return is_duplicate<_i>() || has_duplicate_strings<_i + 1>();
}
}
template <int _i, int _j = _i - 1>
constexpr static bool is_duplicate() {
using FieldType1 = typename std::tuple_element<_i, FieldsType>::type;
using FieldType2 = typename std::tuple_element<_j, FieldsType>::type;
if constexpr (FieldType1::field_ == FieldType2::field_) {
return true;
} else if constexpr (_j > 0) {
return is_duplicate<_i, _j - 1>();
} else {
return false;
}
}
/// Finds the correct index associated with
/// the string at run time.
template <int _i = 0>
std::string find_name() const {
if (_i == value_) {
using FieldType = typename std::tuple_element<_i, FieldsType>::type;
return FieldType::field_.str();
}
if constexpr (_i + 1 == num_fields_) {
return "";
} else {
return find_name<_i + 1>();
}
}
/// Finds the correct index associated with
/// the string at compile time within the Literal's own fields.
template <int _i>
constexpr static auto find_name_within_own_fields() {
using FieldType = typename std::tuple_element<_i, FieldsType>::type;
return FieldType::field_;
}
/// Finds the correct value associated with
/// the string at run time.
template <int _i = 0>
static Result<int> find_value(const std::string& _str) {
using FieldType = typename std::tuple_element<_i, FieldsType>::type;
if (FieldType::field_.str() == _str) {
return _i;
}
if constexpr (_i + 1 == num_fields_) {
return Error(
"Literal does not support string '" + _str +
"'. The following strings are supported: " + allowed_strings() + ".");
} else {
return find_value<_i + 1>(_str);
}
}
/// Finds the value of a string literal at compile time.
template <internal::StringLiteral _name, int _i = 0>
static constexpr int find_value_of() {
if constexpr (_i == num_fields_) {
return -1;
} else {
using FieldType = typename std::tuple_element<_i, FieldsType>::type;
if constexpr (FieldType::field_ == _name) {
return _i;
} else {
return find_value_of<_name, _i + 1>();
}
}
}
/// Whether the literal contains this string.
template <int _i = 0>
static void has_value(const std::string& _str, bool* _found) {
if constexpr (_i == num_fields_) {
*_found = false;
return;
} else {
using FieldType = typename std::tuple_element<_i, FieldsType>::type;
if (FieldType::field_.str() == _str) {
*_found = true;
return;
}
return has_value<_i + 1>(_str, _found);
}
}
static_assert(sizeof...(fields_) <= std::numeric_limits<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 { namespace std {
template <rfl::internal::StringLiteral... fields> template <rfl::internal::StringLiteral... fields>
struct hash<rfl::Literal<fields...>> { struct hash<rfl::Literal<fields...>> {
size_t operator()(const rfl::Literal<fields...>& _l) const { size_t operator()(const rfl::Literal<fields...>& _l) const {
return hash<int>()(static_cast<int>(_l.value())); return hash<int>()(static_cast<int>(_l.value()));
} }
}; };
} // namespace std } // namespace std
#endif // RFL_LITERAL_HPP_ #endif // RFL_LITERAL_HPP_

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

@ -10,63 +10,61 @@
namespace rfl { namespace rfl {
/// Requires that all of the contraints C and Cs be true. /// Requires that all of the contraints C and Cs be true.
template <class C, class... Cs> template <class C, class... Cs>
struct OneOf { struct OneOf {
template <class T> template <class T>
static rfl::Result<T> validate(const T& _value) noexcept { static rfl::Result<T> validate(const T& _value) noexcept {
return validate_impl<T, C, Cs...>(_value, {}); 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 Error make_error_message(const std::vector<Error>& _errors) {
std::string msg = "Expected exactly 1 out of " +
std::to_string(sizeof...(Cs) + 1) +
" validations to pass, but " +
std::to_string(sizeof...(Cs) + 1 - _errors.size()) +
" of them did. The following errors were generated: ";
for (size_t i = 0; i < _errors.size(); ++i) {
msg += "\n" + std::to_string(i + 1) + ") " + _errors.at(i).what();
} }
return Error(msg);
}
template <class T, class Head, class... Tail> template <class T>
static rfl::Result<T> validate_impl(const T& _value, static parsing::schema::ValidationType to_schema() {
std::vector<Error> _errors) { using ValidationType = parsing::schema::ValidationType;
const auto push_back = [&](Error&& _err) -> rfl::Result<T> { const auto types = std::vector<ValidationType>(
_errors.emplace_back(std::forward<Error>(_err)); {C::template to_schema<T>(), Cs::template to_schema<T>()...});
return _err; return ValidationType {ValidationType::OneOf {.types_ = types}};
}; }
const auto next_validation = [&](rfl::Result<T>&& _r) -> rfl::Result<T> { private:
_r.or_else(push_back); static Error make_error_message(const std::vector<Error>& _errors) {
std::string msg = "Expected exactly 1 out of " +
if constexpr (sizeof...(Tail) == 0) { std::to_string(sizeof...(Cs) + 1) +
if (_errors.size() == sizeof...(Cs)) { " validations to pass, but " +
return _value; std::to_string(sizeof...(Cs) + 1 - _errors.size()) +
} " of them did. The following errors were generated: ";
return make_error_message(_errors); for (size_t i = 0; i < _errors.size(); ++i) {
} else { msg += "\n" + std::to_string(i + 1) + ") " + _errors.at(i).what();
return validate_impl<T, Tail...>(
_value, std::forward<std::vector<Error>>(_errors));
} }
}; return Error(msg);
}
return Head::validate(_value) template <class T, class Head, class... Tail>
.and_then(next_validation) static rfl::Result<T> validate_impl(const T& _value,
.or_else(next_validation); std::vector<Error> _errors) {
} const auto push_back = [&](Error&& _err) -> rfl::Result<T> {
}; _errors.emplace_back(std::forward<Error>(_err));
return _err;
};
} // namespace rfl const auto next_validation = [&](rfl::Result<T>&& _r) -> rfl::Result<T> {
_r.or_else(push_back);
if constexpr (sizeof...(Tail) == 0) {
if (_errors.size() == sizeof...(Cs)) { return _value; }
return make_error_message(_errors);
} else {
return validate_impl<T, Tail...>(
_value, std::forward<std::vector<Error>>(_errors));
}
};
return Head::validate(_value)
.and_then(next_validation)
.or_else(next_validation);
}
};
} // namespace rfl
#endif #endif

View file

@ -6,9 +6,9 @@
namespace rfl { namespace rfl {
template <internal::StringLiteral _regex, internal::StringLiteral _name> template <internal::StringLiteral _regex, internal::StringLiteral _name>
using Pattern = Validator<std::string, PatternValidator<_regex, _name>>; using Pattern = Validator<std::string, PatternValidator<_regex, _name>>;
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -11,27 +11,27 @@
namespace rfl { namespace rfl {
template <internal::StringLiteral _regex, internal::StringLiteral _name> template <internal::StringLiteral _regex, internal::StringLiteral _name>
struct PatternValidator { struct PatternValidator {
using Name = Literal<_name>; using Name = Literal<_name>;
using Regex = Literal<_regex>; using Regex = Literal<_regex>;
static Result<std::string> validate(const std::string& _str) noexcept { static Result<std::string> validate(const std::string& _str) noexcept {
if (ctre::match<_regex.arr_>(_str)) { if (ctre::match<_regex.arr_>(_str)) {
return _str; return _str;
} else { } else {
return rfl::Error("String '" + _str + "' did not match format '" + return rfl::Error("String '" + _str + "' did not match format '" +
_name.str() + "': '" + _regex.str() + "'."); _name.str() + "': '" + _regex.str() + "'.");
}
} }
}
template <class T> template <class T>
static parsing::schema::ValidationType to_schema() { static parsing::schema::ValidationType to_schema() {
using ValidationType = parsing::schema::ValidationType; using ValidationType = parsing::schema::ValidationType;
return ValidationType{ValidationType::Regex{.pattern_ = Regex().str()}}; return ValidationType {ValidationType::Regex {.pattern_ = Regex().str()}};
} }
}; };
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -7,32 +7,32 @@
namespace rfl { namespace rfl {
template <class... Ps> template <class... Ps>
struct Processors; struct Processors;
template <> template <>
struct Processors<> { struct Processors<> {
static constexpr bool all_required_ = false; static constexpr bool all_required_ = false;
template <class T, class NamedTupleType> template <class T, class NamedTupleType>
static auto process(NamedTupleType&& _named_tuple) { static auto process(NamedTupleType&& _named_tuple) {
return _named_tuple; return _named_tuple;
} }
}; };
template <class Head, class... Tail> template <class Head, class... Tail>
struct Processors<Head, Tail...> { struct Processors<Head, Tail...> {
static constexpr bool all_required_ = static constexpr bool all_required_ =
std::disjunction_v<internal::is_no_optionals<Head>, std::disjunction_v<internal::is_no_optionals<Head>,
internal::is_no_optionals<Tail>...>; internal::is_no_optionals<Tail>...>;
template <class T, class NamedTupleType> template <class T, class NamedTupleType>
static auto process(NamedTupleType&& _named_tuple) { static auto process(NamedTupleType&& _named_tuple) {
return Processors<Tail...>::template process<T>( return Processors<Tail...>::template process<T>(
Head::template process<T>(std::move(_named_tuple))); Head::template process<T>(std::move(_named_tuple)));
} }
}; };
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -8,141 +8,137 @@
namespace rfl { namespace rfl {
/// The Ref class behaves very similarly to the shared_ptr, but unlike the /// 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 /// unique_ptr, it is 100% guaranteed to be filled at all times (unless the
/// tries to access it after calling std::move does something else that is /// user tries to access it after calling std::move does something else that
/// clearly bad practice). /// is clearly bad practice).
template <class T> template <class T>
class Ref { class Ref {
public: public:
/// The default way of creating new references is /// The default way of creating new references is
/// Ref<T>::make(...) or make_ref<T>(...). /// Ref<T>::make(...) or make_ref<T>(...).
template <class... Args> template <class... Args>
static Ref<T> make(Args&&... _args) { static Ref<T> make(Args&&... _args) {
return Ref<T>(std::make_shared<T>(std::forward<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 /// You can generate them from shared_ptrs as well, in which case it will
/// return an Error, if the shared_ptr is not set. /// return an Error, if the shared_ptr is not set.
static Result<Ref<T>> make(const std::shared_ptr<T>& _ptr) { static Result<Ref<T>> make(std::shared_ptr<T>&& _ptr) {
if (!_ptr) { if (!_ptr) { return Error("std::shared_ptr was a nullptr."); }
return Error("std::shared_ptr was a nullptr."); return Ref<T>(std::move(_ptr));
} }
return Ref<T>(_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)...);
} }
Ref() : ptr_(std::make_shared<T>()) {} template <class T1, class T2>
inline auto operator<=>(const Ref<T1>& _t1, const Ref<T2>& _t2) {
Ref(const Ref<T>& _other) = default; return _t1.ptr() <=> _t2.ptr();
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 CharT, class Traits, class T>
template <class U> inline std::basic_ostream<CharT, Traits>& operator<<(
Ref<T>& operator=(Ref<U>&& _other) noexcept { std::basic_ostream<CharT, Traits>& _os,
ptr_ = std::forward<std::shared_ptr<U>>(_other.ptr()); const Ref<T>& _b) {
return *this; _os << _b.get();
return _os;
} }
/// Move assignment operator } // namespace rfl
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 { namespace std {
template <class T> template <class T>
struct hash<rfl::Ref<T>> { struct hash<rfl::Ref<T>> {
size_t operator()(const rfl::Ref<T>& _r) const { size_t operator()(const rfl::Ref<T>& _r) const {
return hash<shared_ptr<T>>()(_r.ptr()); return hash<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());
} }
};
template <class T> } // namespace std
inline void swap(rfl::Ref<T>& _r1, rfl::Ref<T>& _r2) {
return swap(_r1.ptr(), _r2.ptr());
}
} // namespace std
#endif // RFL_REF_HPP_
#endif // RFL_REF_HPP_

View file

@ -13,129 +13,133 @@
namespace rfl { namespace rfl {
/// Used to assign a new name to a field, which is different from the name /// Used to assign a new name to a field, which is different from the name
/// inside the struct. /// inside the struct.
template <internal::StringLiteral _name, class T> template <internal::StringLiteral _name, class T>
struct Rename { struct Rename {
/// The underlying type. /// The underlying type.
using Type = T; using Type = T;
/// The name of the field. /// The name of the field.
using Name = rfl::Literal<_name>; using Name = rfl::Literal<_name>;
Rename() : value_(Type()) {} Rename() : value_(Type()) {}
Rename(const Type& _value) : value_(_value) {} Rename(const Type& _value) : value_(_value) {}
Rename(Type&& _value) noexcept : value_(std::move(_value)) {} Rename(Type&& _value) noexcept : value_(std::move(_value)) {}
Rename(Rename<_name, T>&& _field) noexcept = default; Rename(Rename<_name, T>&& _field) noexcept = default;
Rename(const Rename<_name, Type>& _field) = default; Rename(const Rename<_name, Type>& _field) = default;
template <class U> template <class U>
Rename(const Rename<_name, U>& _field) : value_(_field.get()) {} Rename(const Rename<_name, U>& _field) : value_(_field.get()) {}
template <class U> template <class U>
Rename(Rename<_name, U>&& _field) : value_(_field.get()) {} Rename(Rename<_name, U>&& _field) : value_(_field.get()) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>, template <class U,
bool>::type = true> typename std::enable_if<std::is_convertible_v<U, Type>,
Rename(const U& _value) : value_(_value) {} bool>::type = true>
Rename(const U& _value) : value_(_value) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>, template <class U,
bool>::type = true> typename std::enable_if<std::is_convertible_v<U, Type>,
Rename(U&& _value) noexcept : value_(std::forward<U>(_value)) {} 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>, template <class U,
bool>::type = true> typename std::enable_if<std::is_convertible_v<U, Type>,
Rename(const Rename<_name, U>& _field) : value_(_field.value()) {} bool>::type = true>
Rename(const Rename<_name, U>& _field) : value_(_field.value()) {}
/// Assigns the underlying object to its default value. /// Assigns the underlying object to its default value.
template <class U = Type, template <class U = Type,
typename std::enable_if<std::is_default_constructible_v<U>, typename std::enable_if<std::is_default_constructible_v<U>,
bool>::type = true> bool>::type = true>
Rename(const Default& _default) : value_(Type()) {} Rename(const Default& _default) : value_(Type()) {}
~Rename() = default; ~Rename() = default;
/// The name of the field, for internal use. /// The name of the field, for internal use.
constexpr static const internal::StringLiteral name_ = _name; constexpr static const internal::StringLiteral name_ = _name;
/// Returns the underlying object. /// Returns the underlying object.
const Type& get() const { return value_; } const Type& get() const { return value_; }
/// Returns the underlying object. /// Returns the underlying object.
Type& operator()() { return value_; } Type& operator()() { return value_; }
/// Returns the underlying object. /// Returns the underlying object.
const Type& operator()() const { return value_; } const Type& operator()() const { return value_; }
/// Assigns the underlying object. /// Assigns the underlying object.
auto& operator=(const Type& _value) { auto& operator=(const Type& _value) {
value_ = _value; value_ = _value;
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
auto& operator=(Type&& _value) noexcept { auto& operator=(Type&& _value) noexcept {
value_ = std::move(_value); value_ = std::move(_value);
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>, template <class U,
bool>::type = true> typename std::enable_if<std::is_convertible_v<U, Type>,
auto& operator=(const U& _value) { bool>::type = true>
value_ = _value; auto& operator=(const U& _value) {
return *this; value_ = _value;
} return *this;
}
/// Assigns the underlying object to its default value. /// Assigns the underlying object to its default value.
template <class U = Type, template <class U = Type,
typename std::enable_if<std::is_default_constructible_v<U>, typename std::enable_if<std::is_default_constructible_v<U>,
bool>::type = true> bool>::type = true>
auto& operator=(const Default& _default) { auto& operator=(const Default& _default) {
value_ = Type(); value_ = Type();
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
Rename<_name, T>& operator=(const Rename<_name, T>& _field) = default; Rename<_name, T>& operator=(const Rename<_name, T>& _field) = default;
/// Assigns the underlying object. /// Assigns the underlying object.
Rename<_name, T>& operator=(Rename<_name, T>&& _field) = default; Rename<_name, T>& operator=(Rename<_name, T>&& _field) = default;
/// Assigns the underlying object. /// Assigns the underlying object.
template <class U> template <class U>
auto& operator=(const Rename<_name, U>& _field) { auto& operator=(const Rename<_name, U>& _field) {
value_ = _field.get(); value_ = _field.get();
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
template <class U> template <class U>
auto& operator=(Rename<_name, U>&& _field) { auto& operator=(Rename<_name, U>&& _field) {
value_ = std::forward<T>(_field.value_); value_ = std::forward<T>(_field.value_);
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
void set(const Type& _value) { value_ = _value; } void set(const Type& _value) { value_ = _value; }
/// Assigns the underlying object. /// Assigns the underlying object.
void set(Type&& _value) { value_ = std::move(_value); } void set(Type&& _value) { value_ = std::move(_value); }
/// Returns the underlying object. /// Returns the underlying object.
Type& value() { return value_; } Type& value() { return value_; }
/// Returns the underlying object. /// Returns the underlying object.
const Type& value() const { return value_; } const Type& value() const { return value_; }
/// The underlying value. /// The underlying value.
Type value_; Type value_;
}; };
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -18,336 +18,340 @@
namespace rfl { namespace rfl {
/// To be returned /// To be returned
class Error { class Error {
public: public:
Error(const std::string& _what) : what_(_what) {} Error(const std::string& _what) : what_(_what) {}
~Error() = default; ~Error() = default;
/// Returns the error message, equivalent to .what() in std::exception. /// Returns the error message, equivalent to .what() in std::exception.
const std::string& what() const { return what_; } const std::string& what() const { return what_; }
private: private:
/// Documents what went wrong /// Documents what went wrong
std::string what_; std::string what_;
}; };
/// Can be used when we are simply interested in whether an operation was /// Can be used when we are simply interested in whether an operation was
/// successful. /// successful.
struct Nothing {}; struct Nothing {};
/// The Result class is used for monadic error handling. /// The Result class is used for monadic error handling.
template <class T> template <class T>
class Result { class Result {
static_assert(!std::is_same<T, Error>(), "The result type cannot be Error."); 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))>; using TOrErr =
std::array<unsigned char, std::max(sizeof(T), sizeof(Error))>;
public: public:
using Type = T; using Type = T;
Result(const T& _val) : success_(true) { new (&get_t()) T(_val); } Result(const T& _val) : success_(true) { new (&get_t()) T(_val); }
Result(T&& _val) noexcept : success_(true) { Result(T&& _val) noexcept : success_(true) {
new (&get_t()) T(std::move(_val)); new (&get_t()) T(std::move(_val));
}
Result(const Error& _err) : success_(false) { new (&get_err()) Error(_err); }
Result(Error&& _err) noexcept : success_(false) {
new (&get_err()) Error(std::move(_err));
}
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(); }
/// Returns Result<U>, if successful and error otherwise.
/// Inspired by .and(...) in the Rust std::result type.
template <class U>
Result<U> and_other(const Result<U>& _r) const noexcept {
const auto f = [&](const auto& _) { return _r; };
return and_then(f);
}
/// 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::forward<T>(get_t())));
} else {
return Result_U(std::forward<Error>(get_err()));
} }
}
/// Monadic operation - F must be a function of type T -> Result<U>. Result(const Error& _err) : success_(false) {
template <class F> new (&get_err()) Error(_err);
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());
} }
}
/// Results types can be iterated over, which even make it possible to use Result(Error&& _err) noexcept : success_(false) {
/// them within a std::range. new (&get_err()) Error(std::move(_err));
T* begin() noexcept {
if (success_) {
return &get_t();
} else {
return nullptr;
} }
}
/// Results types can be iterated over, which even make it possible to use Result(Result<T>&& _other) noexcept : success_(_other.success_) {
/// them within a std::range. move_from_other(_other);
const T* begin() const noexcept {
if (success_) {
return &get_t();
} else {
return nullptr;
} }
}
/// Results types can be iterated over, which even make it possible to use Result(const Result<T>& _other) : success_(_other.success_) {
/// them within a std::range. copy_from_other(_other);
T* end() noexcept {
if (success_) {
return &get_t() + 1;
} else {
return nullptr;
} }
}
/// Results types can be iterated over, which even make it possible to use template <
/// them within a std::range. class U,
const T* end() const noexcept { typename std::enable_if<std::is_convertible_v<U, T>, bool>::type = true>
if (success_) { Result(Result<U>&& _other) : success_(_other && true) {
return &get_t() + 1; auto temp = std::forward<Result<U>>(_other).transform(
} else { [](U&& _u) { return T(std::forward<U>(_u)); });
return nullptr; move_from_other(temp);
} }
}
/// Returns an std::optional<error> if this does in fact contain an error template <
/// or std::nullopt otherwise. class U,
std::optional<Error> error() const noexcept { typename std::enable_if<std::is_convertible_v<U, T>, bool>::type = true>
if (success_) { Result(const Result<U>& _other) : success_(_other && true) {
return std::nullopt; auto temp = _other.transform([](const U& _u) { return T(_u); });
} else { move_from_other(temp);
return get_err();
} }
}
/// Returns true if the result contains a value, false otherwise. ~Result() { destroy(); }
operator bool() const noexcept { return success_; }
/// Allows access to the underlying value. Careful: Will result in undefined /// Returns Result<U>, if successful and error otherwise.
/// behavior, if the result contains an error. /// Inspired by .and(...) in the Rust std::result type.
T& operator*() noexcept { return get_t(); } template <class U>
Result<U> and_other(const Result<U>& _r) const noexcept {
/// Allows read access to the underlying value. Careful: Will result in const auto f = [&](const auto& _) { return _r; };
/// undefined behavior, if the result contains an error. return and_then(f);
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. /// Monadic operation - F must be a function of type T -> Result<U>.
Result<T>& operator=(Result<T>&& _other) noexcept { template <class F>
if (this == &_other) { auto and_then(const F& _f) {
return *this; /// Result_U is expected to be of type Result<U>.
} using Result_U = typename std::invoke_result<F, T>::type;
destroy(); if (success_) {
success_ = _other.success_; return Result_U(_f(std::forward<T>(get_t())));
move_from_other(_other); } else {
return *this; return Result_U(std::forward<Error>(get_err()));
}
/// 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::forward<T>(get_t());
} else {
return _f(std::forward<Error>(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());
}
}
/// Returns the value contained if successful or the provided result r if
/// not.
Result<T> or_other(const Result<T>& _r) const noexcept {
const auto f = [&](const auto& _) { return _r; };
return or_else(f);
}
/// 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 = typename std::invoke_result<F, T>::type;
if (success_) {
return rfl::Result<U>(_f(std::forward<T>(get_t())));
} else {
return rfl::Result<U>(std::forward<Error>(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 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::forward<T>(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;
}
}
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<T> /*&& !internal::is_array_v<T>*/) {
get_t().~T();
} }
} else {
get_err().~Error();
} }
}
T& get_t() noexcept { return *(reinterpret_cast<T*>(t_or_err_.data())); } /// Monadic operation - F must be a function of type T -> Result<U>.
template <class F>
const T& get_t() const noexcept { auto and_then(const F& _f) const {
return *(reinterpret_cast<const T*>(t_or_err_.data())); /// Result_U is expected to be of type Result<U>.
} using Result_U = typename std::invoke_result<F, T>::type;
if (success_) {
Error& get_err() noexcept { return Result_U(_f(get_t()));
return *(reinterpret_cast<Error*>(t_or_err_.data())); } else {
} return Result_U(get_err());
}
const Error& get_err() const noexcept {
return *(reinterpret_cast<const Error*>(t_or_err_.data()));
}
void move_from_other(Result<T>& _other) {
if (success_) {
new (&get_t()) T(std::move(_other.get_t()));
} else {
new (&get_err()) Error(std::move(_other.get_err()));
} }
}
private: /// Results types can be iterated over, which even make it possible to use
/// Signifies whether this was a success. /// them within a std::range.
bool success_; T* begin() noexcept {
if (success_) {
return &get_t();
} else {
return nullptr;
}
}
/// The underlying data, can either be T or Error. /// Results types can be iterated over, which even make it possible to use
alignas(std::max(alignof(T), alignof(Error))) TOrErr t_or_err_; /// them within a std::range.
}; const T* begin() const noexcept {
if (success_) {
return &get_t();
} else {
return nullptr;
}
}
} // namespace rfl /// Results types can be iterated over, which even make it possible to use
/// them within a std::range.
T* end() noexcept {
if (success_) {
return &get_t() + 1;
} else {
return nullptr;
}
}
/// Results types can be iterated over, which even make it possible to use
/// them within a std::range.
const T* end() const noexcept {
if (success_) {
return &get_t() + 1;
} else {
return nullptr;
}
}
/// Returns an std::optional<error> if this does in fact contain an error
/// or std::nullopt otherwise.
std::optional<Error> error() const noexcept {
if (success_) {
return std::nullopt;
} else {
return 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 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;
}
/// 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::forward<T>(get_t());
} else {
return _f(std::forward<Error>(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());
}
}
/// Returns the value contained if successful or the provided result r if
/// not.
Result<T> or_other(const Result<T>& _r) const noexcept {
const auto f = [&](const auto& _) { return _r; };
return or_else(f);
}
/// 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 = typename std::invoke_result<F, T>::type;
if (success_) {
return rfl::Result<U>(_f(std::forward<T>(get_t())));
} else {
return rfl::Result<U>(std::forward<Error>(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 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::forward<T>(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;
}
}
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<
T> /*&& !internal::is_array_v<T>*/) {
get_t().~T();
}
} else {
get_err().~Error();
}
}
T& get_t() noexcept { return *(reinterpret_cast<T*>(t_or_err_.data())); }
const T& get_t() const noexcept {
return *(reinterpret_cast<const T*>(t_or_err_.data()));
}
Error& get_err() noexcept {
return *(reinterpret_cast<Error*>(t_or_err_.data()));
}
const Error& get_err() const noexcept {
return *(reinterpret_cast<const Error*>(t_or_err_.data()));
}
void move_from_other(Result<T>& _other) {
if (success_) {
new (&get_t()) T(std::move(_other.get_t()));
} else {
new (&get_err()) Error(std::move(_other.get_err()));
}
}
private:
/// 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_;
};
} // namespace rfl
#endif #endif

View file

@ -10,23 +10,23 @@
namespace rfl { namespace rfl {
template <class V> template <class V>
struct Size { struct Size {
template <class T> template <class T>
static rfl::Result<T> validate(const T& _t) { static rfl::Result<T> validate(const T& _t) {
const auto to_t = [&](const auto& _v) { return _t; }; const auto to_t = [&](const auto& _v) { return _t; };
const auto embellish_error = [](const auto& _err) { const auto embellish_error = [](const auto& _err) {
return Error("Size validation failed: " + _err.what()); return Error("Size validation failed: " + _err.what());
}; };
return V::validate(_t.size()).transform(to_t).or_else(embellish_error); return V::validate(_t.size()).transform(to_t).or_else(embellish_error);
} }
template <class T> template <class T>
static parsing::schema::ValidationType to_schema() { static parsing::schema::ValidationType to_schema() {
return V::template to_schema<size_t>(); return V::template to_schema<size_t>();
} }
}; };
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -5,16 +5,15 @@
namespace rfl { namespace rfl {
template <class T> template <class T>
using Skip = internal::Skip<T, true, true>; using Skip = internal::Skip<T, true, true>;
template <class T> template <class T>
using SkipSerialization = internal::Skip<T, true, false>; using SkipSerialization = internal::Skip<T, true, false>;
template <class T> template <class T>
using SkipDeserialization = internal::Skip<T, false, true>; using SkipDeserialization = internal::Skip<T, false, true>;
} // namespace rfl } // namespace rfl
#endif #endif

View file

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

View file

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

View file

@ -10,148 +10,149 @@
namespace rfl { namespace rfl {
// https://serde.rs/enum-representations.html // https://serde.rs/enum-representations.html
template <internal::StringLiteral _discriminator, class... Ts> template <internal::StringLiteral _discriminator, class... Ts>
struct TaggedUnion { struct TaggedUnion {
static constexpr internal::StringLiteral discrimininator_ = _discriminator; static constexpr internal::StringLiteral discrimininator_ = _discriminator;
/// The type of the underlying variant. /// The type of the underlying variant.
using VariantType = std::variant<Ts...>; using VariantType = std::variant<Ts...>;
/// A literal containing all the tags that are possible /// A literal containing all the tags that are possible
using PossibleTags = define_literal_t<internal::tag_t<_discriminator, Ts>...>; using PossibleTags =
define_literal_t<internal::tag_t<_discriminator, Ts>...>;
TaggedUnion(const VariantType& _variant) : variant_(_variant) {} TaggedUnion(const VariantType& _variant) : variant_(_variant) {}
TaggedUnion(VariantType&& _variant) noexcept TaggedUnion(VariantType&& _variant) noexcept
: variant_(std::move(_variant)) {} : variant_(std::move(_variant)) {}
TaggedUnion(const TaggedUnion<_discriminator, Ts...>& _tagged_union) = TaggedUnion(const TaggedUnion<_discriminator, Ts...>& _tagged_union) =
default; default;
TaggedUnion(TaggedUnion<_discriminator, Ts...>&& _tagged_union) noexcept = TaggedUnion(TaggedUnion<_discriminator, Ts...>&& _tagged_union) noexcept =
default; default;
template <class T, template <class T,
typename std::enable_if<std::is_convertible_v<T, VariantType>, typename std::enable_if<std::is_convertible_v<T, VariantType>,
bool>::type = true> bool>::type = true>
TaggedUnion(const T& _t) : variant_(_t) {} TaggedUnion(const T& _t) : variant_(_t) {}
template <class T, template <class T,
typename std::enable_if<std::is_convertible_v<T, VariantType>, typename std::enable_if<std::is_convertible_v<T, VariantType>,
bool>::type = true> bool>::type = true>
TaggedUnion(T&& _t) noexcept : variant_(std::forward<T>(_t)) {} TaggedUnion(T&& _t) noexcept : variant_(std::forward<T>(_t)) {}
~TaggedUnion() = default; ~TaggedUnion() = default;
/// Assigns the underlying object. /// Assigns the underlying object.
TaggedUnion<_discriminator, Ts...>& operator=(const VariantType& _variant) { TaggedUnion<_discriminator, Ts...>& operator=(const VariantType& _variant) {
variant_ = _variant; variant_ = _variant;
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
TaggedUnion<_discriminator, Ts...>& operator=(VariantType&& _variant) { TaggedUnion<_discriminator, Ts...>& operator=(VariantType&& _variant) {
variant_ = std::move(_variant); variant_ = std::move(_variant);
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
template <class T, template <class T,
typename std::enable_if<std::is_convertible_v<T, VariantType>, typename std::enable_if<std::is_convertible_v<T, VariantType>,
bool>::type = true> bool>::type = true>
TaggedUnion<_discriminator, Ts...>& operator=(T&& _variant) { TaggedUnion<_discriminator, Ts...>& operator=(T&& _variant) {
variant_ = std::forward<T>(_variant); variant_ = std::forward<T>(_variant);
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
template <class T, template <class T,
typename std::enable_if<std::is_convertible_v<T, VariantType>, typename std::enable_if<std::is_convertible_v<T, VariantType>,
bool>::type = true> bool>::type = true>
TaggedUnion<_discriminator, Ts...>& operator=(const T& _variant) { TaggedUnion<_discriminator, Ts...>& operator=(const T& _variant) {
variant_ = _variant; variant_ = _variant;
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
TaggedUnion<_discriminator, Ts...>& operator=( TaggedUnion<_discriminator, Ts...>& operator=(
const TaggedUnion<_discriminator, Ts...>& _other) = default; const TaggedUnion<_discriminator, Ts...>& _other) = default;
/// Assigns the underlying object. /// Assigns the underlying object.
TaggedUnion<_discriminator, Ts...>& operator=( TaggedUnion<_discriminator, Ts...>& operator=(
TaggedUnion<_discriminator, Ts...>&& _other) = default; TaggedUnion<_discriminator, Ts...>&& _other) = default;
/// Returns the underlying variant. /// Returns the underlying variant.
VariantType& variant() { return variant_; } VariantType& variant() { return variant_; }
/// Returns the underlying variant. /// Returns the underlying variant.
const VariantType& variant() const { return variant_; } const VariantType& variant() const { return variant_; }
static_assert(!PossibleTags::has_duplicates(), static_assert(!PossibleTags::has_duplicates(),
"Duplicate tags are not allowed inside tagged unions."); "Duplicate tags are not allowed inside tagged unions.");
/// The underlying variant - a TaggedUnion is a thin wrapper /// The underlying variant - a TaggedUnion is a thin wrapper
/// around a variant that is mainly used for parsing. /// around a variant that is mainly used for parsing.
VariantType variant_; VariantType variant_;
}; };
namespace internal { namespace internal {
template <StringLiteral _discriminator, class... NamedTupleTypes> template <StringLiteral _discriminator, class... NamedTupleTypes>
struct Getter<TaggedUnion<_discriminator, NamedTupleTypes...>> { struct Getter<TaggedUnion<_discriminator, NamedTupleTypes...>> {
public: public:
/// Retrieves the indicated value from the tuple. /// Retrieves the indicated value from the tuple.
template <int _index> template <int _index>
static inline auto& get( static inline auto& get(
TaggedUnion<_discriminator, NamedTupleTypes...>& _tu) { TaggedUnion<_discriminator, NamedTupleTypes...>& _tu) {
return Getter<std::variant<NamedTupleTypes...>>::template get<_index>( return Getter<std::variant<NamedTupleTypes...>>::template get<_index>(
_tu.variant_); _tu.variant_);
} }
/// Gets a field by name. /// Gets a field by name.
template <StringLiteral _field_name> template <StringLiteral _field_name>
static inline auto& get( static inline auto& get(
TaggedUnion<_discriminator, NamedTupleTypes...>& _tu) { TaggedUnion<_discriminator, NamedTupleTypes...>& _tu) {
return Getter<std::variant<NamedTupleTypes...>>::template get<_field_name>( return Getter<std::variant<NamedTupleTypes...>>::template get<
_tu.variant_); _field_name>(_tu.variant_);
} }
/// Gets a field by the field type. /// Gets a field by the field type.
template <class Field> template <class Field>
static inline auto& get( static inline auto& get(
TaggedUnion<_discriminator, NamedTupleTypes...>& _tu) { TaggedUnion<_discriminator, NamedTupleTypes...>& _tu) {
return Getter<std::variant<NamedTupleTypes...>>::template get<Field>( return Getter<std::variant<NamedTupleTypes...>>::template get<Field>(
_tu.variant_); _tu.variant_);
} }
/// Retrieves the indicated value from the tuple. /// Retrieves the indicated value from the tuple.
template <int _index> template <int _index>
static inline const auto& get_const( static inline const auto& get_const(
const TaggedUnion<_discriminator, NamedTupleTypes...>& _tu) { const TaggedUnion<_discriminator, NamedTupleTypes...>& _tu) {
return Getter<std::variant<NamedTupleTypes...>>::template get_const<_index>( return Getter<std::variant<NamedTupleTypes...>>::template get_const<
_tu.variant_); _index>(_tu.variant_);
} }
/// Gets a field by name. /// Gets a field by name.
template <StringLiteral _field_name> template <StringLiteral _field_name>
static inline const auto& get_const( static inline const auto& get_const(
const TaggedUnion<_discriminator, NamedTupleTypes...>& _tu) { const TaggedUnion<_discriminator, NamedTupleTypes...>& _tu) {
return Getter<std::variant<NamedTupleTypes...>>::template get_const< return Getter<std::variant<NamedTupleTypes...>>::template get_const<
_field_name>(_tu.variant_); _field_name>(_tu.variant_);
} }
/// Gets a field by the field type. /// Gets a field by the field type.
template <class Field> template <class Field>
static inline const auto& get_const( static inline const auto& get_const(
const TaggedUnion<_discriminator, NamedTupleTypes...>& _tu) { const TaggedUnion<_discriminator, NamedTupleTypes...>& _tu) {
return Getter<std::variant<NamedTupleTypes...>>::template get_const<Field>( return Getter<std::variant<NamedTupleTypes...>>::template get_const<
_tu.variant_); Field>(_tu.variant_);
} }
}; };
} // namespace internal } // namespace internal
} // namespace rfl } // namespace rfl
#endif // RFL_TAGGEDUNION_HPP_ #endif // RFL_TAGGEDUNION_HPP_

View file

@ -15,82 +15,78 @@
namespace rfl { namespace rfl {
/// For serializing and deserializing time stamps. /// For serializing and deserializing time stamps.
template <internal::StringLiteral _format> template <internal::StringLiteral _format>
class Timestamp { class Timestamp {
constexpr static const internal::StringLiteral format_ = _format; constexpr static const internal::StringLiteral format_ = _format;
public: public:
using Format = rfl::Literal<_format>; using Format = rfl::Literal<_format>;
using ReflectionType = std::string; using ReflectionType = std::string;
Timestamp(const char* _str) : tm_(std::tm{}) { Timestamp(const char* _str) : tm_(std::tm {}) {
const auto r = strptime(_str, _format.str().c_str(), &tm_); const auto r = strptime(_str, _format.str().c_str(), &tm_);
if (r == NULL) { if (r == NULL) {
throw std::runtime_error("String '" + std::string(_str) + throw std::runtime_error("String '" + std::string(_str) +
"' did not match format '" + Format().str() + "' did not match format '" + Format().str() +
"'."); "'.");
}
} }
}
Timestamp(const std::string& _str) : Timestamp(_str.c_str()) {} Timestamp(const std::string& _str) : Timestamp(_str.c_str()) {}
Timestamp(const std::tm& _tm) : tm_(_tm) {} Timestamp(const std::tm& _tm) : tm_(_tm) {}
~Timestamp() = default; ~Timestamp() = default;
/// Returns a result containing the timestamp when successful or an Error /// Returns a result containing the timestamp when successful or an Error
/// otherwise. /// otherwise.
static Result<Timestamp> from_string(const char* _str) noexcept { static Result<Timestamp> from_string(const char* _str) noexcept {
try { try {
return Timestamp(_str); return Timestamp(_str);
} catch (std::exception& e) { } catch (std::exception& e) { return Error(e.what()); }
return Error(e.what());
} }
}
/// Returns a result containing the timestamp when successful or an Error /// Returns a result containing the timestamp when successful or an Error
/// otherwise. /// otherwise.
static Result<Timestamp> from_string(const std::string& _str) { static Result<Timestamp> from_string(const std::string& _str) {
return from_string(_str.c_str()); return from_string(_str.c_str());
} }
/// Necessary for the serialization to work. /// Necessary for the serialization to work.
ReflectionType reflection() const { ReflectionType reflection() const {
char outstr[200]; char outstr[200];
strftime(outstr, 200, format_.str().c_str(), &tm_); strftime(outstr, 200, format_.str().c_str(), &tm_);
return std::string(outstr); return std::string(outstr);
} }
/// Expresses the underlying timestamp as a string. /// Expresses the underlying timestamp as a string.
std::string str() const { return reflection(); } std::string str() const { return reflection(); }
/// Trivial accessor to the underlying time stamp. /// Trivial accessor to the underlying time stamp.
std::tm& tm() { return tm_; } std::tm& tm() { return tm_; }
/// Trivial (const) accessor to the underlying time stamp. /// Trivial (const) accessor to the underlying time stamp.
const std::tm& tm() const { return tm_; } const std::tm& tm() const { return tm_; }
private: private:
#ifdef _MSC_VER #ifdef _MSC_VER
// This workaround is necessary, because MSVC doesn't support strptime. // This workaround is necessary, because MSVC doesn't support strptime.
char* strptime(const char* _s, const char* _f, std::tm* _tm) { char* strptime(const char* _s, const char* _f, std::tm* _tm) {
std::istringstream input(_s); std::istringstream input(_s);
input.imbue(std::locale(setlocale(LC_ALL, nullptr))); input.imbue(std::locale(setlocale(LC_ALL, nullptr)));
input >> std::get_time(_tm, _f); input >> std::get_time(_tm, _f);
if (input.fail()) { if (input.fail()) { return NULL; }
return NULL; return (char*)(_s + input.tellg());
} }
return (char*)(_s + input.tellg());
}
#endif #endif
private: private:
/// The underlying time stamp. /// The underlying time stamp.
std::tm tm_; std::tm tm_;
}; };
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -16,123 +16,126 @@
namespace rfl { namespace rfl {
template <class T, class V, class... Vs> template <class T, class V, class... Vs>
requires internal::HasValidation<AllOf<V, Vs...>, T> requires internal::HasValidation<AllOf<V, Vs...>, T>
struct Validator { struct Validator {
public: public:
using ReflectionType = T; using ReflectionType = T;
using ValidationType = using ValidationType =
std::conditional_t<sizeof...(Vs) == 0, V, AllOf<V, Vs...>>; std::conditional_t<sizeof...(Vs) == 0, V, AllOf<V, Vs...>>;
/// Exception-free validation. /// Exception-free validation.
static Result<Validator<T, V, Vs...>> from_value(const T& _value) noexcept { static Result<Validator<T, V, Vs...>> from_value(const T& _value) noexcept {
try { try {
return Validator<T, V, Vs...>(_value); return Validator<T, V, Vs...>(_value);
} catch (std::exception& e) { } catch (std::exception& e) { return Error(e.what()); }
return Error(e.what());
} }
Validator() : value_(ValidationType::validate(T()).value()) {}
Validator(Validator<T, V, Vs...>&& _other) noexcept = default;
Validator(const Validator<T, V, Vs...>& _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<T, V, Vs...>& operator=(const Validator<T, V, Vs...>& _other) =
default;
/// Assigns the underlying object.
Validator<T, V, Vs...>& operator=(
Validator<T, V, Vs...>&& _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<T, V, Vs...>& _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();
} }
Validator() : value_(ValidationType::validate(T()).value()) {} template <class T, class V, class... Vs>
inline auto operator<=>(const Validator<T, V, Vs...>& _v, const T& _t) {
Validator(Validator<T, V, Vs...>&& _other) noexcept = default; return _v.value() <=> _t;
Validator(const Validator<T, V, Vs...>& _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. } // namespace rfl
auto& operator=(T&& _value) {
value_ = ValidationType::validate(std::forward<T>(_value)).value();
return *this;
}
/// Assigns the underlying object.
Validator<T, V, Vs...>& operator=(const Validator<T, V, Vs...>& _other) =
default;
/// Assigns the underlying object.
Validator<T, V, Vs...>& operator=(Validator<T, V, Vs...>&& _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<T, V, Vs...>& _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 { namespace std {
template <class T, class V, class... Vs> template <class T, class V, class... Vs>
struct hash<rfl::Validator<T, V, Vs...>> { struct hash<rfl::Validator<T, V, Vs...>> {
size_t operator()(const rfl::Validator<T, V, Vs...>& _v) const { size_t operator()(const rfl::Validator<T, V, Vs...>& _v) const {
return hash<T>()(_v.value()); return hash<T>()(_v.value());
} }
}; };
} // namespace std } // namespace std
#endif #endif

View file

@ -5,80 +5,80 @@
namespace rfl { namespace rfl {
template <class... AlternativeTypes> template <class... AlternativeTypes>
struct Variant { struct Variant {
/// The type of the underlying variant. /// The type of the underlying variant.
using VariantType = std::variant<AlternativeTypes...>; using VariantType = std::variant<AlternativeTypes...>;
Variant(const VariantType& _variant) : variant_(_variant) {} Variant(const VariantType& _variant) : variant_(_variant) {}
Variant(VariantType&& _variant) noexcept : variant_(std::move(_variant)) {} Variant(VariantType&& _variant) noexcept : variant_(std::move(_variant)) {}
Variant(const Variant<AlternativeTypes...>& _variant) = default; Variant(const Variant<AlternativeTypes...>& _variant) = default;
Variant(Variant<AlternativeTypes...>&& _variant) noexcept = default; Variant(Variant<AlternativeTypes...>&& _variant) noexcept = default;
template <class T, template <class T,
typename std::enable_if<std::is_convertible_v<T, VariantType>, typename std::enable_if<std::is_convertible_v<T, VariantType>,
bool>::type = true> bool>::type = true>
Variant(const T& _t) : variant_(_t) {} Variant(const T& _t) : variant_(_t) {}
template <class T, template <class T,
typename std::enable_if<std::is_convertible_v<T, VariantType>, typename std::enable_if<std::is_convertible_v<T, VariantType>,
bool>::type = true> bool>::type = true>
Variant(T&& _t) noexcept : variant_(std::forward<T>(_t)) {} Variant(T&& _t) noexcept : variant_(std::forward<T>(_t)) {}
~Variant() = default; ~Variant() = default;
/// Assigns the underlying object. /// Assigns the underlying object.
Variant<AlternativeTypes...>& operator=(const VariantType& _variant) { Variant<AlternativeTypes...>& operator=(const VariantType& _variant) {
variant_ = _variant; variant_ = _variant;
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
Variant<AlternativeTypes...>& operator=(VariantType&& _variant) { Variant<AlternativeTypes...>& operator=(VariantType&& _variant) {
variant_ = std::move(_variant); variant_ = std::move(_variant);
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
template <class T, template <class T,
typename std::enable_if<std::is_convertible_v<T, VariantType>, typename std::enable_if<std::is_convertible_v<T, VariantType>,
bool>::type = true> bool>::type = true>
Variant<AlternativeTypes...>& operator=(T&& _variant) { Variant<AlternativeTypes...>& operator=(T&& _variant) {
variant_ = std::forward<T>(_variant); variant_ = std::forward<T>(_variant);
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
template <class T, template <class T,
typename std::enable_if<std::is_convertible_v<T, VariantType>, typename std::enable_if<std::is_convertible_v<T, VariantType>,
bool>::type = true> bool>::type = true>
Variant<AlternativeTypes...>& operator=(const T& _variant) { Variant<AlternativeTypes...>& operator=(const T& _variant) {
variant_ = _variant; variant_ = _variant;
return *this; return *this;
} }
/// Assigns the underlying object. /// Assigns the underlying object.
Variant<AlternativeTypes...>& operator=( Variant<AlternativeTypes...>& operator=(
const Variant<AlternativeTypes...>& _other) = default; const Variant<AlternativeTypes...>& _other) = default;
/// Assigns the underlying object. /// Assigns the underlying object.
Variant<AlternativeTypes...>& operator=( Variant<AlternativeTypes...>& operator=(
Variant<AlternativeTypes...>&& _other) = default; Variant<AlternativeTypes...>&& _other) = default;
/// Returns the underlying variant. /// Returns the underlying variant.
VariantType& variant() { return variant_; } VariantType& variant() { return variant_; }
/// Returns the underlying variant. /// Returns the underlying variant.
const VariantType& variant() const { return variant_; } const VariantType& variant() const { return variant_; }
/// The underlying variant - a Variant is a thin wrapper /// The underlying variant - a Variant is a thin wrapper
/// around a variant that is mainly used for parsing. /// around a variant that is mainly used for parsing.
VariantType variant_; VariantType variant_;
}; };
} // namespace rfl } // namespace rfl
#endif #endif

View file

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

View file

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

View file

@ -9,42 +9,43 @@
namespace rfl::parsing { namespace rfl::parsing {
/// bson_oid_t needs to be treated as a special case, otherwise it will be read /// bson_oid_t needs to be treated as a special case, otherwise it will be
/// as a struct. /// read as a struct.
template <class R, class W, class ProcessorsType> template <class R, class W, class ProcessorsType>
requires AreReaderAndWriter<R, W, bson_oid_t> requires AreReaderAndWriter<R, W, bson_oid_t>
struct Parser<R, W, ProcessorsType, bson_oid_t> { struct Parser<R, W, ProcessorsType, bson_oid_t> {
using InputVarType = typename R::InputVarType; using InputVarType = typename R::InputVarType;
using OutputVarType = typename W::OutputVarType; using OutputVarType = typename W::OutputVarType;
using ParentType = Parent<W>; using ParentType = Parent<W>;
static Result<bson_oid_t> read(const R& _r, static Result<bson_oid_t> read(const R& _r,
const InputVarType& _var) noexcept { const InputVarType& _var) noexcept {
return _r.template to_basic_type<bson_oid_t>(_var); return _r.template to_basic_type<bson_oid_t>(_var);
} }
template <class P> template <class P>
static void write(const W& _w, const bson_oid_t& _oid, static void write(const W& _w,
const P& _parent) noexcept { const bson_oid_t& _oid,
ParentType::add_value(_w, _oid, _parent); const P& _parent) noexcept {
} ParentType::add_value(_w, _oid, _parent);
}
static schema::Type to_schema( static schema::Type to_schema(
std::map<std::string, schema::Type>* _definitions) { std::map<std::string, schema::Type>* _definitions) {
static_assert(rfl::always_false_v<R>, static_assert(rfl::always_false_v<R>,
"bson_oid_t cannot be expressed inside a JSON schema."); "bson_oid_t cannot be expressed inside a JSON schema.");
return schema::Type{schema::Type::String{}}; return schema::Type {schema::Type::String {}};
} }
}; };
} // namespace rfl::parsing } // namespace rfl::parsing
namespace rfl::bson { namespace rfl::bson {
template <class T, class ProcessorsType> template <class T, class ProcessorsType>
using Parser = parsing::Parser<Reader, Writer, T, ProcessorsType>; using Parser = parsing::Parser<Reader, Writer, T, ProcessorsType>;
} // namespace rfl::bson } // namespace rfl::bson
#endif #endif

View file

@ -1,9 +1,8 @@
#ifndef RFL_BSON_READER_HPP_ #ifndef RFL_BSON_READER_HPP_
#define RFL_BSON_READER_HPP_ #define RFL_BSON_READER_HPP_
#include <bson/bson.h>
#include <array> #include <array>
#include <bson/bson.h>
#include <concepts> #include <concepts>
#include <exception> #include <exception>
#include <map> #include <map>
@ -22,196 +21,191 @@
#include "../always_false.hpp" #include "../always_false.hpp"
namespace rfl { namespace rfl {
namespace bson { namespace bson {
/// Please refer to https://mongoc.org/libbson/current/api.html /// Please refer to https://mongoc.org/libbson/current/api.html
struct Reader { struct Reader {
struct BSONValue { struct BSONValue {
bson_value_t val_; bson_value_t val_;
}; };
struct BSONInputArray { struct BSONInputArray {
BSONValue* val_; BSONValue* val_;
}; };
struct BSONInputObject { struct BSONInputObject {
BSONValue* val_; BSONValue* val_;
}; };
struct BSONInputVar { struct BSONInputVar {
BSONValue* val_; BSONValue* val_;
}; };
using InputArrayType = BSONInputArray; using InputArrayType = BSONInputArray;
using InputObjectType = BSONInputObject; using InputObjectType = BSONInputObject;
using InputVarType = BSONInputVar; using InputVarType = BSONInputVar;
template <class T> template <class T>
static constexpr bool has_custom_constructor = (requires(InputVarType var) { static constexpr bool has_custom_constructor =
T::from_bson_obj(var); (requires(InputVarType var) { T::from_bson_obj(var); });
});
rfl::Result<InputVarType> get_field( rfl::Result<InputVarType> get_field(
const std::string& _name, const InputObjectType& _obj) const noexcept { const std::string& _name,
bson_t b; const InputObjectType& _obj) const noexcept {
bson_iter_t iter; bson_t b;
const auto doc = _obj.val_->val_.value.v_doc; bson_iter_t iter;
if (bson_init_static(&b, doc.data, doc.data_len)) { const auto doc = _obj.val_->val_.value.v_doc;
if (bson_iter_init(&iter, &b)) { if (bson_init_static(&b, doc.data, doc.data_len)) {
while (bson_iter_next(&iter)) { if (bson_iter_init(&iter, &b)) {
auto key = std::string(bson_iter_key(&iter)); while (bson_iter_next(&iter)) {
if (key == _name) { auto key = std::string(bson_iter_key(&iter));
return to_input_var(&iter); if (key == _name) { return to_input_var(&iter); }
}
} }
} }
return Error("No field named '" + _name + "' was found.");
} }
}
return Error("No field named '" + _name + "' was found.");
}
bool is_empty(const InputVarType& _var) const noexcept { bool is_empty(const InputVarType& _var) const noexcept {
return _var.val_->val_.value_type == BSON_TYPE_NULL; return _var.val_->val_.value_type == BSON_TYPE_NULL;
}
template <class T>
rfl::Result<T> to_basic_type(const InputVarType& _var) const noexcept {
const auto btype = _var.val_->val_.value_type;
const auto value = _var.val_->val_.value;
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
switch (btype) {
case BSON_TYPE_UTF8:
return std::string(value.v_utf8.str, value.v_utf8.len);
case BSON_TYPE_SYMBOL:
return std::string(value.v_symbol.symbol, value.v_symbol.len);
default:
return rfl::Error(
"Could not cast to string. The type must be UTF8 or symbol.");
} }
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
if (btype != BSON_TYPE_BOOL) { template <class T>
return rfl::Error("Could not cast to boolean."); rfl::Result<T> to_basic_type(const InputVarType& _var) const noexcept {
const auto btype = _var.val_->val_.value_type;
const auto value = _var.val_->val_.value;
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
switch (btype) {
case BSON_TYPE_UTF8:
return std::string(value.v_utf8.str, value.v_utf8.len);
case BSON_TYPE_SYMBOL:
return std::string(value.v_symbol.symbol, value.v_symbol.len);
default:
return rfl::Error(
"Could not cast to string. The type must be UTF8 or symbol.");
}
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
if (btype != BSON_TYPE_BOOL) {
return rfl::Error("Could not cast to boolean.");
}
return value.v_bool;
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>() ||
std::is_integral<std::remove_cvref_t<T>>()) {
switch (btype) {
case BSON_TYPE_DOUBLE:
return static_cast<T>(value.v_double);
case BSON_TYPE_INT32:
return static_cast<T>(value.v_int32);
case BSON_TYPE_INT64:
return static_cast<T>(value.v_int64);
case BSON_TYPE_DATE_TIME:
return static_cast<T>(value.v_datetime);
default:
return rfl::Error(
"Could not cast to numeric value. The type must be double, "
"int32, int64 or date_time.");
}
} else if constexpr (std::is_same<std::remove_cvref_t<T>,
bson_oid_t>()) {
if (btype != BSON_TYPE_OID) {
return rfl::Error("Could not cast to OID.");
}
return value.v_oid;
} else {
static_assert(rfl::always_false_v<T>, "Unsupported type.");
}
} }
return value.v_bool;
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>() ||
std::is_integral<std::remove_cvref_t<T>>()) {
switch (btype) {
case BSON_TYPE_DOUBLE:
return static_cast<T>(value.v_double);
case BSON_TYPE_INT32: rfl::Result<InputArrayType> to_array(
return static_cast<T>(value.v_int32); const InputVarType& _var) const noexcept {
const auto btype = _var.val_->val_.value_type;
case BSON_TYPE_INT64: if (btype != BSON_TYPE_ARRAY && btype != BSON_TYPE_DOCUMENT) {
return static_cast<T>(value.v_int64); return Error("Could not cast to an array.");
}
case BSON_TYPE_DATE_TIME: return InputArrayType {_var.val_};
return static_cast<T>(value.v_datetime);
default:
return rfl::Error(
"Could not cast to numeric value. The type must be double, "
"int32, int64 or date_time.");
} }
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bson_oid_t>()) {
if (btype != BSON_TYPE_OID) {
return rfl::Error("Could not cast to OID.");
}
return value.v_oid;
} else {
static_assert(rfl::always_false_v<T>, "Unsupported type.");
}
}
rfl::Result<InputArrayType> to_array( template <class ArrayReader>
const InputVarType& _var) const noexcept { std::optional<Error> read_array(
const auto btype = _var.val_->val_.value_type; const ArrayReader& _array_reader,
if (btype != BSON_TYPE_ARRAY && btype != BSON_TYPE_DOCUMENT) { const InputArrayType& _arr) const noexcept {
return Error("Could not cast to an array."); bson_t b;
} bson_iter_t iter;
return InputArrayType{_var.val_}; const auto doc = _arr.val_->val_.value.v_doc;
} if (bson_init_static(&b, doc.data, doc.data_len)) {
if (bson_iter_init(&iter, &b)) {
template <class ArrayReader> while (bson_iter_next(&iter)) {
std::optional<Error> read_array(const ArrayReader& _array_reader, const auto err = _array_reader.read(to_input_var(&iter));
const InputArrayType& _arr) const noexcept { if (err) { return err; }
bson_t b; }
bson_iter_t iter;
const auto doc = _arr.val_->val_.value.v_doc;
if (bson_init_static(&b, doc.data, doc.data_len)) {
if (bson_iter_init(&iter, &b)) {
while (bson_iter_next(&iter)) {
const auto err = _array_reader.read(to_input_var(&iter));
if (err) {
return err;
} }
} }
return std::nullopt;
} }
}
return std::nullopt;
}
template <class ObjectReader> template <class ObjectReader>
std::optional<Error> read_object(const ObjectReader& _object_reader, std::optional<Error> read_object(
const InputObjectType& _obj) const noexcept { const ObjectReader& _object_reader,
bson_t b; const InputObjectType& _obj) const noexcept {
bson_iter_t iter; bson_t b;
const auto doc = _obj.val_->val_.value.v_doc; bson_iter_t iter;
if (bson_init_static(&b, doc.data, doc.data_len)) { const auto doc = _obj.val_->val_.value.v_doc;
if (bson_iter_init(&iter, &b)) { if (bson_init_static(&b, doc.data, doc.data_len)) {
while (bson_iter_next(&iter)) { if (bson_iter_init(&iter, &b)) {
const char* k = bson_iter_key(&iter); while (bson_iter_next(&iter)) {
_object_reader.read(std::string_view(k), to_input_var(&iter)); const char* k = bson_iter_key(&iter);
_object_reader.read(std::string_view(k), to_input_var(&iter));
}
}
} }
return std::nullopt;
} }
}
return std::nullopt;
}
rfl::Result<InputObjectType> to_object( rfl::Result<InputObjectType> to_object(
const InputVarType& _var) const noexcept { const InputVarType& _var) const noexcept {
const auto btype = _var.val_->val_.value_type; const auto btype = _var.val_->val_.value_type;
if (btype != BSON_TYPE_DOCUMENT) { if (btype != BSON_TYPE_DOCUMENT) {
return Error("Could not cast to a document."); return Error("Could not cast to a document.");
} }
return InputObjectType{_var.val_}; return InputObjectType {_var.val_};
}
template <class T>
rfl::Result<T> use_custom_constructor(
const InputVarType& _var) const noexcept {
try {
return T::from_bson_obj(_var);
} catch (std::exception& e) {
return rfl::Error(e.what());
}
}
private:
struct BSONValues {
std::vector<rfl::Box<BSONValue>> vec_;
~BSONValues() {
for (auto& v : vec_) {
bson_value_destroy(&(v->val_));
} }
}
};
private: template <class T>
InputVarType to_input_var(bson_iter_t* _iter) const noexcept { rfl::Result<T> use_custom_constructor(
values_->vec_.emplace_back(rfl::Box<BSONValue>::make()); const InputVarType& _var) const noexcept {
auto* last_value = values_->vec_.back().get(); try {
bson_value_copy(bson_iter_value(_iter), &last_value->val_); return T::from_bson_obj(_var);
return InputVarType{last_value}; } catch (std::exception& e) { return rfl::Error(e.what()); }
} }
private: private:
/// Contains the values inside the object. struct BSONValues {
rfl::Ref<BSONValues> values_; std::vector<rfl::Box<BSONValue>> vec_;
}; ~BSONValues() {
for (auto& v : vec_) { bson_value_destroy(&(v->val_)); }
}
};
} // namespace bson private:
} // namespace rfl InputVarType to_input_var(bson_iter_t* _iter) const noexcept {
values_->vec_.emplace_back(rfl::Box<BSONValue>::make());
auto* last_value = values_->vec_.back().get();
bson_value_copy(bson_iter_value(_iter), &last_value->val_);
return InputVarType {last_value};
}
#endif // JSON_PARSER_HPP_ private:
/// Contains the values inside the object.
rfl::Ref<BSONValues> values_;
};
} // namespace bson
} // namespace rfl
#endif // JSON_PARSER_HPP_

View file

@ -2,7 +2,6 @@
#define RFL_BSON_WRITER_HPP_ #define RFL_BSON_WRITER_HPP_
#include <bson/bson.h> #include <bson/bson.h>
#include <exception> #include <exception>
#include <map> #include <map>
#include <sstream> #include <sstream>
@ -19,209 +18,220 @@
#include "../always_false.hpp" #include "../always_false.hpp"
namespace rfl { namespace rfl {
namespace bson { namespace bson {
/// Please refer to https://mongoc.org/libbson/current/api.html /// Please refer to https://mongoc.org/libbson/current/api.html
class Writer { class Writer {
struct BSONType { struct BSONType {
bson_t val_; bson_t val_;
}; };
struct IsArray { struct IsArray {
bson_array_builder_t* ptr_; bson_array_builder_t* ptr_;
}; };
struct IsObject { struct IsObject {
bson_t* ptr_; bson_t* ptr_;
}; };
struct IsRoot {}; struct IsRoot {};
using ParentType = std::variant<IsArray, IsObject, IsRoot>; using ParentType = std::variant<IsArray, IsObject, IsRoot>;
public: public:
struct BSONOutputArray { struct BSONOutputArray {
BSONOutputArray(bson_array_builder_t* _val, ParentType _parent) BSONOutputArray(bson_array_builder_t* _val, ParentType _parent)
: parent_(_parent), val_(_val) {} : parent_(_parent), val_(_val) {}
ParentType parent_; ParentType parent_;
bson_array_builder_t* val_; bson_array_builder_t* val_;
}; };
struct BSONOutputObject { struct BSONOutputObject {
BSONOutputObject(bson_t* _val, ParentType _parent) BSONOutputObject(bson_t* _val, ParentType _parent)
: parent_(_parent), val_(_val) {} : parent_(_parent), val_(_val) {}
ParentType parent_; ParentType parent_;
bson_t* val_; bson_t* val_;
}; };
struct BSONOutputVar {}; struct BSONOutputVar {};
using OutputArrayType = BSONOutputArray; using OutputArrayType = BSONOutputArray;
using OutputObjectType = BSONOutputObject; using OutputObjectType = BSONOutputObject;
using OutputVarType = BSONOutputVar; using OutputVarType = BSONOutputVar;
Writer(bson_t* _doc) : doc_(_doc) {} Writer(bson_t* _doc) : doc_(_doc) {}
~Writer() = default; ~Writer() = default;
OutputArrayType array_as_root(const size_t _size) const noexcept { OutputArrayType array_as_root(const size_t _size) const noexcept {
bson_array_builder_t* val = bson_array_builder_new(); bson_array_builder_t* val = bson_array_builder_new();
return OutputArrayType(val, IsRoot{}); return OutputArrayType(val, IsRoot {});
}
OutputObjectType object_as_root(const size_t _size) const noexcept {
return OutputObjectType(doc_, IsRoot{});
}
OutputVarType null_as_root() const noexcept {
// Appears to be unsupported by the BSON C API.
return OutputVarType{};
}
template <class T>
OutputVarType value_as_root(const T& _var) const noexcept {
static_assert(rfl::always_false_v<T>,
"BSON only allows arrays or objects as its root.");
return OutputVarType{};
}
OutputArrayType add_array_to_array(const size_t _size,
OutputArrayType* _parent) const noexcept {
bson_array_builder_t* val;
bson_array_builder_append_array_builder_begin(_parent->val_, &val);
return OutputArrayType(val, IsArray{_parent->val_});
}
OutputArrayType add_array_to_object(
const std::string_view& _name, const size_t _size,
OutputObjectType* _parent) const noexcept {
bson_array_builder_t* val;
bson_append_array_builder_begin(_parent->val_, _name.data(),
static_cast<int>(_name.size()), &val);
return OutputArrayType(val, IsObject{_parent->val_});
}
OutputObjectType add_object_to_array(
const size_t _size, OutputArrayType* _parent) const noexcept {
subdocs_->emplace_back(rfl::Box<BSONType>());
bson_array_builder_append_document_begin(_parent->val_,
&(subdocs_->back()->val_));
return OutputObjectType(&subdocs_->back()->val_, IsArray{_parent->val_});
}
OutputObjectType add_object_to_object(
const std::string_view& _name, const size_t _size,
OutputObjectType* _parent) const noexcept {
subdocs_->emplace_back(rfl::Box<BSONType>());
bson_append_document_begin(_parent->val_, _name.data(),
static_cast<int>(_name.size()),
&(subdocs_->back()->val_));
return OutputObjectType(&subdocs_->back()->val_, IsObject{_parent->val_});
}
template <class T>
OutputVarType add_value_to_array(const T& _var,
OutputArrayType* _parent) const noexcept {
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
bson_array_builder_append_utf8(_parent->val_, _var.c_str(),
static_cast<int>(_var.size()));
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
bson_array_builder_append_bool(_parent->val_, _var);
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>()) {
bson_array_builder_append_double(_parent->val_,
static_cast<double>(_var));
} else if constexpr (std::is_integral<std::remove_cvref_t<T>>()) {
bson_array_builder_append_int64(_parent->val_,
static_cast<std::int64_t>(_var));
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bson_oid_t>()) {
bson_array_builder_append_oid(_parent->val_, &_var);
} else {
static_assert(rfl::always_false_v<T>, "Unsupported type.");
}
return OutputVarType{};
}
template <class T>
OutputVarType add_value_to_object(const std::string_view& _name,
const T& _var,
OutputObjectType* _parent) const noexcept {
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
bson_append_utf8(_parent->val_, _name.data(),
static_cast<int>(_name.size()), _var.c_str(),
static_cast<int>(_var.size()));
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
bson_append_bool(_parent->val_, _name.data(),
static_cast<int>(_name.size()), _var);
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>()) {
bson_append_double(_parent->val_, _name.data(),
static_cast<int>(_name.size()),
static_cast<double>(_var));
} else if constexpr (std::is_integral<std::remove_cvref_t<T>>()) {
bson_append_int64(_parent->val_, _name.data(),
static_cast<int>(_name.size()),
static_cast<std::int64_t>(_var));
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bson_oid_t>()) {
bson_append_oid(_parent->val_, _name.data(),
static_cast<int>(_name.size()), &_var);
} else {
static_assert(rfl::always_false_v<T>, "Unsupported type.");
}
return OutputVarType{};
}
OutputVarType add_null_to_array(OutputArrayType* _parent) const noexcept {
bson_array_builder_append_null(_parent->val_);
return OutputVarType{};
}
OutputVarType add_null_to_object(const std::string_view& _name,
OutputObjectType* _parent) const noexcept {
bson_append_null(_parent->val_, _name.data(),
static_cast<int>(_name.size()));
return OutputVarType{};
}
void end_array(OutputArrayType* _arr) const noexcept {
const auto handle = [&](const auto _parent) {
using Type = std::remove_cvref_t<decltype(_parent)>;
if constexpr (std::is_same<Type, IsArray>()) {
bson_array_builder_append_array_builder_end(_parent.ptr_, _arr->val_);
} else if constexpr (std::is_same<Type, IsObject>()) {
bson_append_array_builder_end(_parent.ptr_, _arr->val_);
} else if constexpr (std::is_same<Type, IsRoot>()) {
bson_array_builder_build(_arr->val_, doc_);
} else {
static_assert(rfl::always_false_v<Type>, "Unsupported type.");
} }
};
std::visit(handle, _arr->parent_);
}
void end_object(OutputObjectType* _obj) const noexcept { OutputObjectType object_as_root(const size_t _size) const noexcept {
const auto handle = [&](const auto _parent) { return OutputObjectType(doc_, IsRoot {});
using Type = std::remove_cvref_t<decltype(_parent)>;
if constexpr (std::is_same<Type, IsArray>()) {
bson_array_builder_append_document_end(_parent.ptr_, _obj->val_);
} else if constexpr (std::is_same<Type, IsObject>()) {
bson_append_document_end(_parent.ptr_, _obj->val_);
} else if constexpr (std::is_same<Type, IsRoot>()) {
} else {
static_assert(rfl::always_false_v<Type>, "Unsupported type.");
} }
OutputVarType null_as_root() const noexcept {
// Appears to be unsupported by the BSON C API.
return OutputVarType {};
}
template <class T>
OutputVarType value_as_root(const T& _var) const noexcept {
static_assert(rfl::always_false_v<T>,
"BSON only allows arrays or objects as its root.");
return OutputVarType {};
}
OutputArrayType add_array_to_array(
const size_t _size,
OutputArrayType* _parent) const noexcept {
bson_array_builder_t* val;
bson_array_builder_append_array_builder_begin(_parent->val_, &val);
return OutputArrayType(val, IsArray {_parent->val_});
}
OutputArrayType add_array_to_object(
const std::string_view& _name,
const size_t _size,
OutputObjectType* _parent) const noexcept {
bson_array_builder_t* val;
bson_append_array_builder_begin(_parent->val_, _name.data(),
static_cast<int>(_name.size()), &val);
return OutputArrayType(val, IsObject {_parent->val_});
}
OutputObjectType add_object_to_array(
const size_t _size,
OutputArrayType* _parent) const noexcept {
subdocs_->emplace_back(rfl::Box<BSONType>());
bson_array_builder_append_document_begin(_parent->val_,
&(subdocs_->back()->val_));
return OutputObjectType(&subdocs_->back()->val_,
IsArray {_parent->val_});
}
OutputObjectType add_object_to_object(
const std::string_view& _name,
const size_t _size,
OutputObjectType* _parent) const noexcept {
subdocs_->emplace_back(rfl::Box<BSONType>());
bson_append_document_begin(_parent->val_, _name.data(),
static_cast<int>(_name.size()),
&(subdocs_->back()->val_));
return OutputObjectType(&subdocs_->back()->val_,
IsObject {_parent->val_});
}
template <class T>
OutputVarType add_value_to_array(const T& _var, OutputArrayType* _parent)
const noexcept {
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
bson_array_builder_append_utf8(_parent->val_, _var.c_str(),
static_cast<int>(_var.size()));
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
bson_array_builder_append_bool(_parent->val_, _var);
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>()) {
bson_array_builder_append_double(_parent->val_,
static_cast<double>(_var));
} else if constexpr (std::is_integral<std::remove_cvref_t<T>>()) {
bson_array_builder_append_int64(_parent->val_,
static_cast<std::int64_t>(_var));
} else if constexpr (std::is_same<std::remove_cvref_t<T>,
bson_oid_t>()) {
bson_array_builder_append_oid(_parent->val_, &_var);
} else {
static_assert(rfl::always_false_v<T>, "Unsupported type.");
}
return OutputVarType {};
}
template <class T>
OutputVarType add_value_to_object(
const std::string_view& _name,
const T& _var,
OutputObjectType* _parent) const noexcept {
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
bson_append_utf8(_parent->val_, _name.data(),
static_cast<int>(_name.size()), _var.c_str(),
static_cast<int>(_var.size()));
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
bson_append_bool(_parent->val_, _name.data(),
static_cast<int>(_name.size()), _var);
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>()) {
bson_append_double(_parent->val_, _name.data(),
static_cast<int>(_name.size()),
static_cast<double>(_var));
} else if constexpr (std::is_integral<std::remove_cvref_t<T>>()) {
bson_append_int64(_parent->val_, _name.data(),
static_cast<int>(_name.size()),
static_cast<std::int64_t>(_var));
} else if constexpr (std::is_same<std::remove_cvref_t<T>,
bson_oid_t>()) {
bson_append_oid(_parent->val_, _name.data(),
static_cast<int>(_name.size()), &_var);
} else {
static_assert(rfl::always_false_v<T>, "Unsupported type.");
}
return OutputVarType {};
}
OutputVarType add_null_to_array(OutputArrayType* _parent) const noexcept {
bson_array_builder_append_null(_parent->val_);
return OutputVarType {};
}
OutputVarType add_null_to_object(
const std::string_view& _name,
OutputObjectType* _parent) const noexcept {
bson_append_null(_parent->val_, _name.data(),
static_cast<int>(_name.size()));
return OutputVarType {};
}
void end_array(OutputArrayType* _arr) const noexcept {
const auto handle = [&](const auto _parent) {
using Type = std::remove_cvref_t<decltype(_parent)>;
if constexpr (std::is_same<Type, IsArray>()) {
bson_array_builder_append_array_builder_end(_parent.ptr_,
_arr->val_);
} else if constexpr (std::is_same<Type, IsObject>()) {
bson_append_array_builder_end(_parent.ptr_, _arr->val_);
} else if constexpr (std::is_same<Type, IsRoot>()) {
bson_array_builder_build(_arr->val_, doc_);
} else {
static_assert(rfl::always_false_v<Type>, "Unsupported type.");
}
};
std::visit(handle, _arr->parent_);
}
void end_object(OutputObjectType* _obj) const noexcept {
const auto handle = [&](const auto _parent) {
using Type = std::remove_cvref_t<decltype(_parent)>;
if constexpr (std::is_same<Type, IsArray>()) {
bson_array_builder_append_document_end(_parent.ptr_, _obj->val_);
} else if constexpr (std::is_same<Type, IsObject>()) {
bson_append_document_end(_parent.ptr_, _obj->val_);
} else if constexpr (std::is_same<Type, IsRoot>()) {
} else {
static_assert(rfl::always_false_v<Type>, "Unsupported type.");
}
};
std::visit(handle, _obj->parent_);
}
private:
/// Pointer to the main document. In BSON, documents are what are usually
/// called objects.
bson_t* const doc_;
/// Contain all of the subdocuments.
const rfl::Ref<std::vector<rfl::Box<BSONType>>> subdocs_;
}; };
std::visit(handle, _obj->parent_);
}
private: } // namespace bson
/// Pointer to the main document. In BSON, documents are what are usually } // namespace rfl
/// called objects.
bson_t* const doc_;
/// Contain all of the subdocuments. #endif // BSON_PARSER_HPP_
const rfl::Ref<std::vector<rfl::Box<BSONType>>> subdocs_;
};
} // namespace bson
} // namespace rfl
#endif // BSON_PARSER_HPP_

View file

@ -6,17 +6,17 @@
#include "read.hpp" #include "read.hpp"
namespace rfl { namespace rfl {
namespace bson { namespace bson {
template <class T, class... Ps> template <class T, class... Ps>
Result<T> load(const std::string& _fname) { Result<T> load(const std::string& _fname) {
const auto read_bytes = [](const auto& _bytes) { const auto read_bytes = [](const auto& _bytes) {
return read<T, Ps...>(_bytes); return read<T, Ps...>(_bytes);
}; };
return rfl::io::load_bytes(_fname).and_then(read_bytes); return rfl::io::load_bytes(_fname).and_then(read_bytes);
} }
} // namespace bson } // namespace bson
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -2,7 +2,6 @@
#define RFL_BSON_READ_HPP_ #define RFL_BSON_READ_HPP_
#include <bson/bson.h> #include <bson/bson.h>
#include <istream> #include <istream>
#include <string> #include <string>
@ -12,50 +11,50 @@
#include "Reader.hpp" #include "Reader.hpp"
namespace rfl { namespace rfl {
namespace bson { namespace bson {
using InputObjectType = typename Reader::InputObjectType; using InputObjectType = typename Reader::InputObjectType;
using InputVarType = typename Reader::InputVarType; using InputVarType = typename Reader::InputVarType;
/// Parses an object from a BSON var. /// Parses an object from a BSON var.
template <class T, class... Ps> template <class T, class... Ps>
Result<internal::wrap_in_rfl_array_t<T>> read(const InputVarType& _obj) { Result<internal::wrap_in_rfl_array_t<T>> read(const InputVarType& _obj) {
const auto r = Reader(); const auto r = Reader();
return Parser<T, Processors<Ps...>>::read(r, _obj); return Parser<T, Processors<Ps...>>::read(r, _obj);
} }
/// Parses an BSON object using reflection. /// Parses an BSON object using reflection.
template <class T, class... Ps> template <class T, class... Ps>
auto read(const uint8_t* _bytes, const size_t _size) { auto read(const uint8_t* _bytes, const size_t _size) {
Reader::BSONValue value; Reader::BSONValue value;
value.val_.value.v_doc.data_len = static_cast<uint32_t>(_size); value.val_.value.v_doc.data_len = static_cast<uint32_t>(_size);
value.val_.value.v_doc.data = const_cast<uint8_t*>(_bytes); value.val_.value.v_doc.data = const_cast<uint8_t*>(_bytes);
value.val_.value_type = BSON_TYPE_DOCUMENT; value.val_.value_type = BSON_TYPE_DOCUMENT;
auto doc = InputVarType{&value}; auto doc = InputVarType {&value};
return read<T, Ps...>(doc); return read<T, Ps...>(doc);
} }
/// Parses an BSON object using reflection. /// Parses an BSON object using reflection.
template <class T, class... Ps> template <class T, class... Ps>
auto read(const char* _bytes, const size_t _size) { auto read(const char* _bytes, const size_t _size) {
return read<T, Ps...>(reinterpret_cast<const uint8_t*>(_bytes), _size); return read<T, Ps...>(reinterpret_cast<const uint8_t*>(_bytes), _size);
} }
/// Parses an object from BSON using reflection. /// Parses an object from BSON using reflection.
template <class T, class... Ps> template <class T, class... Ps>
auto read(const std::vector<char>& _bytes) { auto read(const std::vector<char>& _bytes) {
return read<T, Ps...>(_bytes.data(), _bytes.size()); return read<T, Ps...>(_bytes.data(), _bytes.size());
} }
/// Parses an object from a stream. /// Parses an object from a stream.
template <class T, class... Ps> template <class T, class... Ps>
auto read(std::istream& _stream) { auto read(std::istream& _stream) {
std::istreambuf_iterator<char> begin(_stream), end; std::istreambuf_iterator<char> begin(_stream), end;
auto bytes = std::vector<char>(begin, end); auto bytes = std::vector<char>(begin, end);
return read<T, Ps...>(bytes.data(), bytes.size()); return read<T, Ps...>(bytes.data(), bytes.size());
} }
} // namespace bson } // namespace bson
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -10,17 +10,17 @@
#include "write.hpp" #include "write.hpp"
namespace rfl { namespace rfl {
namespace bson { namespace bson {
template <class... Ps> template <class... Ps>
Result<Nothing> save(const std::string& _fname, const auto& _obj) { Result<Nothing> save(const std::string& _fname, const auto& _obj) {
const auto write_func = [](const auto& _obj, auto& _stream) -> auto& { const auto write_func = [](const auto& _obj, auto& _stream) -> auto& {
return write<Ps...>(_obj, _stream); return write<Ps...>(_obj, _stream);
}; };
return rfl::io::save_bytes(_fname, _obj, write_func); return rfl::io::save_bytes(_fname, _obj, write_func);
} }
} // namespace bson } // namespace bson
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -2,7 +2,6 @@
#define RFL_BSON_WRITE_HPP_ #define RFL_BSON_WRITE_HPP_
#include <bson/bson.h> #include <bson/bson.h>
#include <ostream> #include <ostream>
#include <sstream> #include <sstream>
#include <string> #include <string>
@ -13,49 +12,49 @@
#include "Parser.hpp" #include "Parser.hpp"
namespace rfl { namespace rfl {
namespace bson { namespace bson {
/// Returns BSON bytes. Careful: It is the responsibility of the caller to call /// Returns BSON bytes. Careful: It is the responsibility of the caller to
/// bson_free on the returned pointer. /// call bson_free on the returned pointer.
template <class... Ps> template <class... Ps>
std::pair<uint8_t*, size_t> to_buffer(const auto& _obj) noexcept { std::pair<uint8_t*, size_t> to_buffer(const auto& _obj) noexcept {
using T = std::remove_cvref_t<decltype(_obj)>; using T = std::remove_cvref_t<decltype(_obj)>;
using ParentType = parsing::Parent<Writer>; using ParentType = parsing::Parent<Writer>;
bson_t* doc = nullptr; bson_t* doc = nullptr;
uint8_t* buf = nullptr; uint8_t* buf = nullptr;
size_t buflen = 0; size_t buflen = 0;
bson_writer_t* bson_writer = bson_writer_t* bson_writer =
bson_writer_new(&buf, &buflen, 0, bson_realloc_ctx, NULL); bson_writer_new(&buf, &buflen, 0, bson_realloc_ctx, NULL);
bson_writer_begin(bson_writer, &doc); bson_writer_begin(bson_writer, &doc);
const auto rfl_writer = Writer(doc); const auto rfl_writer = Writer(doc);
Parser<T, Processors<Ps...>>::write(rfl_writer, _obj, Parser<T, Processors<Ps...>>::write(rfl_writer, _obj,
typename ParentType::Root{}); typename ParentType::Root {});
bson_writer_end(bson_writer); bson_writer_end(bson_writer);
const auto len = bson_writer_get_length(bson_writer); const auto len = bson_writer_get_length(bson_writer);
bson_writer_destroy(bson_writer); bson_writer_destroy(bson_writer);
return std::make_pair(buf, len); return std::make_pair(buf, len);
} }
/// Returns BSON bytes. /// Returns BSON bytes.
template <class... Ps> template <class... Ps>
std::vector<char> write(const auto& _obj) noexcept { std::vector<char> write(const auto& _obj) noexcept {
auto [buf, len] = to_buffer<Ps...>(_obj); auto [buf, len] = to_buffer<Ps...>(_obj);
const auto result = std::vector<char>(reinterpret_cast<char*>(buf), const auto result = std::vector<char>(reinterpret_cast<char*>(buf),
reinterpret_cast<char*>(buf) + len); reinterpret_cast<char*>(buf) + len);
bson_free(buf); bson_free(buf);
return result; return result;
} }
/// Writes a BSON into an ostream. /// Writes a BSON into an ostream.
template <class... Ps> template <class... Ps>
std::ostream& write(const auto& _obj, std::ostream& _stream) noexcept { std::ostream& write(const auto& _obj, std::ostream& _stream) noexcept {
auto [buf, len] = to_buffer<Ps...>(_obj); auto [buf, len] = to_buffer<Ps...>(_obj);
_stream.write(reinterpret_cast<const char*>(buf), len); _stream.write(reinterpret_cast<const char*>(buf), len);
bson_free(buf); bson_free(buf);
return _stream; return _stream;
} }
} // namespace bson } // namespace bson
} // namespace rfl } // namespace rfl
#endif // BSON_PARSER_HPP_ #endif // BSON_PARSER_HPP_

View file

@ -6,40 +6,46 @@
#include "Writer.hpp" #include "Writer.hpp"
namespace rfl { namespace rfl {
namespace parsing { namespace parsing {
/// CBOR requires us to explicitly set the number of fields in advance. Because /// CBOR requires us to explicitly set the number of fields in advance.
/// of that, we require all of the fields and then set them to nullptr, if /// Because of that, we require all of the fields and then set them to
/// necessary. /// nullptr, if necessary.
template <class ProcessorsType, class... FieldTypes> template <class ProcessorsType, class... FieldTypes>
requires AreReaderAndWriter<cbor::Reader, cbor::Writer, requires AreReaderAndWriter<cbor::Reader,
NamedTuple<FieldTypes...>> cbor::Writer,
struct Parser<cbor::Reader, cbor::Writer, NamedTuple<FieldTypes...>, NamedTuple<FieldTypes...>>
ProcessorsType> struct Parser<cbor::Reader,
: public NamedTupleParser<cbor::Reader, cbor::Writer, cbor::Writer,
/*_ignore_empty_containers=*/false, NamedTuple<FieldTypes...>,
/*_all_required=*/true, ProcessorsType, ProcessorsType>
FieldTypes...> { : public NamedTupleParser<cbor::Reader,
}; cbor::Writer,
/*_ignore_empty_containers=*/false,
/*_all_required=*/true,
ProcessorsType,
FieldTypes...> {};
template <class ProcessorsType, class... Ts> template <class ProcessorsType, class... Ts>
requires AreReaderAndWriter<cbor::Reader, cbor::Writer, std::tuple<Ts...>> requires AreReaderAndWriter<cbor::Reader, cbor::Writer, std::tuple<Ts...>>
struct Parser<cbor::Reader, cbor::Writer, std::tuple<Ts...>, ProcessorsType> struct Parser<cbor::Reader, cbor::Writer, std::tuple<Ts...>, ProcessorsType>
: public TupleParser<cbor::Reader, cbor::Writer, : public TupleParser<cbor::Reader,
/*_ignore_empty_containers=*/false, cbor::Writer,
/*_all_required=*/true, ProcessorsType, Ts...> { /*_ignore_empty_containers=*/false,
}; /*_all_required=*/true,
ProcessorsType,
Ts...> {};
} // namespace parsing } // namespace parsing
} // namespace rfl } // namespace rfl
namespace rfl { namespace rfl {
namespace cbor { namespace cbor {
template <class T, class ProcessorsType> template <class T, class ProcessorsType>
using Parser = parsing::Parser<Reader, Writer, T, ProcessorsType>; using Parser = parsing::Parser<Reader, Writer, T, ProcessorsType>;
} }
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -1,9 +1,8 @@
#ifndef RFL_CBOR_READER_HPP_ #ifndef RFL_CBOR_READER_HPP_
#define RFL_CBOR_READER_HPP_ #define RFL_CBOR_READER_HPP_
#include <cbor.h>
#include <array> #include <array>
#include <cbor.h>
#include <concepts> #include <concepts>
#include <exception> #include <exception>
#include <map> #include <map>
@ -22,238 +21,206 @@
#include "../always_false.hpp" #include "../always_false.hpp"
namespace rfl { namespace rfl {
namespace cbor { namespace cbor {
/// Please refer to https://intel.github.io/tinycbor/current/index.html /// Please refer to https://intel.github.io/tinycbor/current/index.html
struct Reader { struct Reader {
struct CBORInputArray { struct CBORInputArray {
CborValue* val_; CborValue* val_;
}; };
struct CBORInputObject { struct CBORInputObject {
CborValue* val_; CborValue* val_;
}; };
struct CBORInputVar { struct CBORInputVar {
CborValue* val_; CborValue* val_;
}; };
using InputArrayType = CBORInputArray; using InputArrayType = CBORInputArray;
using InputObjectType = CBORInputObject; using InputObjectType = CBORInputObject;
using InputVarType = CBORInputVar; using InputVarType = CBORInputVar;
template <class T> template <class T>
static constexpr bool has_custom_constructor = (requires(InputVarType var) { static constexpr bool has_custom_constructor =
T::from_cbor_obj(var); (requires(InputVarType var) { T::from_cbor_obj(var); });
});
rfl::Result<InputVarType> get_field( rfl::Result<InputVarType> get_field(
const std::string& _name, const InputObjectType& _obj) const noexcept { const std::string& _name,
CborValue val; const InputObjectType& _obj) const noexcept {
auto buffer = std::vector<char>(); CborValue val;
auto err = cbor_value_enter_container(_obj.val_, &val); auto buffer = std::vector<char>();
if (err != CborNoError) { auto err = cbor_value_enter_container(_obj.val_, &val);
return Error(cbor_error_string(err)); if (err != CborNoError) { return Error(cbor_error_string(err)); }
} size_t length = 0;
size_t length = 0; err = cbor_value_get_map_length(_obj.val_, &length);
err = cbor_value_get_map_length(_obj.val_, &length); if (err != CborNoError) { return Error(cbor_error_string(err)); }
if (err != CborNoError) { for (size_t i = 0; i < length; ++i) {
return Error(cbor_error_string(err)); if (!cbor_value_is_text_string(&val)) {
} return Error("Expected the key to be a string value.");
for (size_t i = 0; i < length; ++i) { }
if (!cbor_value_is_text_string(&val)) { err = get_string(&val, &buffer);
return Error("Expected the key to be a string value."); if (err != CborNoError) { return Error(cbor_error_string(err)); }
err = cbor_value_advance(&val);
if (err != CborNoError) { return Error(cbor_error_string(err)); }
if (_name == buffer.data()) { return to_input_var(&val); }
err = cbor_value_advance(&val);
if (err != CborNoError) { return Error(cbor_error_string(err)); }
}
return Error("No field named '" + _name + "' was found.");
} }
err = get_string(&val, &buffer);
if (err != CborNoError) {
return Error(cbor_error_string(err));
}
err = cbor_value_advance(&val);
if (err != CborNoError) {
return Error(cbor_error_string(err));
}
if (_name == buffer.data()) {
return to_input_var(&val);
}
err = cbor_value_advance(&val);
if (err != CborNoError) {
return Error(cbor_error_string(err));
}
}
return Error("No field named '" + _name + "' was found.");
}
bool is_empty(const InputVarType& _var) const noexcept { bool is_empty(const InputVarType& _var) const noexcept {
return cbor_value_is_null(_var.val_); return cbor_value_is_null(_var.val_);
} }
template <class T> template <class T>
rfl::Result<T> to_basic_type(const InputVarType& _var) const noexcept { rfl::Result<T> to_basic_type(const InputVarType& _var) const noexcept {
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) { if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
if (!cbor_value_is_text_string(_var.val_)) { if (!cbor_value_is_text_string(_var.val_)) {
return Error("Could not cast to string."); return Error("Could not cast to string.");
}
std::vector<char> buffer;
const auto err = get_string(_var.val_, &buffer);
if (err != CborNoError) { return Error(cbor_error_string(err)); }
return std::string(buffer.data());
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
if (!cbor_value_is_boolean(_var.val_)) {
return rfl::Error("Could not cast to boolean.");
}
bool result = false;
const auto err = cbor_value_get_boolean(_var.val_, &result);
if (err != CborNoError) { return Error(cbor_error_string(err)); }
return result;
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>() ||
std::is_integral<std::remove_cvref_t<T>>()) {
if (cbor_value_is_integer(_var.val_)) {
std::int64_t result = 0;
const auto err = cbor_value_get_int64(_var.val_, &result);
if (err != CborNoError) { return Error(cbor_error_string(err)); }
return static_cast<T>(result);
} else if (cbor_value_is_float(_var.val_)) {
float result = 0.0;
const auto err = cbor_value_get_float(_var.val_, &result);
if (err != CborNoError) { return Error(cbor_error_string(err)); }
return static_cast<T>(result);
} else if (cbor_value_is_double(_var.val_)) {
double result = 0.0;
const auto err = cbor_value_get_double(_var.val_, &result);
if (err != CborNoError) { return Error(cbor_error_string(err)); }
return static_cast<T>(result);
}
return rfl::Error(
"Could not cast to numeric value. The type must be integral, "
"float "
"or double.");
} else {
static_assert(rfl::always_false_v<T>, "Unsupported type.");
}
} }
std::vector<char> buffer;
const auto err = get_string(_var.val_, &buffer); rfl::Result<InputArrayType> to_array(
if (err != CborNoError) { const InputVarType& _var) const noexcept {
return Error(cbor_error_string(err)); if (!cbor_value_is_array(_var.val_)) {
return Error("Could not cast to an array.");
}
return InputArrayType {_var.val_};
} }
return std::string(buffer.data());
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) { rfl::Result<InputObjectType> to_object(
if (!cbor_value_is_boolean(_var.val_)) { const InputVarType& _var) const noexcept {
return rfl::Error("Could not cast to boolean."); if (!cbor_value_is_map(_var.val_)) {
return Error("Could not cast to an object.");
}
return InputObjectType {_var.val_};
} }
bool result = false;
const auto err = cbor_value_get_boolean(_var.val_, &result); template <class ArrayReader>
if (err != CborNoError) { std::optional<Error> read_array(
return Error(cbor_error_string(err)); const ArrayReader& _array_reader,
} const InputArrayType& _arr) const noexcept {
return result; CborValue val;
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>() || auto buffer = std::vector<char>();
std::is_integral<std::remove_cvref_t<T>>()) { auto err = cbor_value_enter_container(_arr.val_, &val);
if (cbor_value_is_integer(_var.val_)) { if (err != CborNoError && err != CborErrorOutOfMemory) {
std::int64_t result = 0;
const auto err = cbor_value_get_int64(_var.val_, &result);
if (err != CborNoError) {
return Error(cbor_error_string(err)); return Error(cbor_error_string(err));
} }
return static_cast<T>(result); size_t length = 0;
} else if (cbor_value_is_float(_var.val_)) { err = cbor_value_get_array_length(_arr.val_, &length);
float result = 0.0; if (err != CborNoError && err != CborErrorOutOfMemory) {
const auto err = cbor_value_get_float(_var.val_, &result);
if (err != CborNoError) {
return Error(cbor_error_string(err)); return Error(cbor_error_string(err));
} }
return static_cast<T>(result); for (size_t i = 0; i < length; ++i) {
} else if (cbor_value_is_double(_var.val_)) { const auto err2 = _array_reader.read(to_input_var(&val));
double result = 0.0; if (err2) { return err2; }
const auto err = cbor_value_get_double(_var.val_, &result); err = cbor_value_advance(&val);
if (err != CborNoError) { if (err != CborNoError && err != CborErrorOutOfMemory) {
return Error(cbor_error_string(err)); return Error(cbor_error_string(err));
}
} }
return static_cast<T>(result); return std::nullopt;
} }
return rfl::Error(
"Could not cast to numeric value. The type must be integral, float "
"or double.");
} else { template <class ObjectReader>
static_assert(rfl::always_false_v<T>, "Unsupported type."); std::optional<Error> read_object(
} const ObjectReader& _object_reader,
} const InputObjectType& _obj) const noexcept {
size_t length = 0;
auto err = cbor_value_get_map_length(_obj.val_, &length);
if (err != CborNoError) { return Error(cbor_error_string(err)); }
rfl::Result<InputArrayType> to_array( CborValue val;
const InputVarType& _var) const noexcept { err = cbor_value_enter_container(_obj.val_, &val);
if (!cbor_value_is_array(_var.val_)) { if (err != CborNoError) { return Error(cbor_error_string(err)); }
return Error("Could not cast to an array.");
}
return InputArrayType{_var.val_};
}
rfl::Result<InputObjectType> to_object( auto buffer = std::vector<char>();
const InputVarType& _var) const noexcept {
if (!cbor_value_is_map(_var.val_)) {
return Error("Could not cast to an object.");
}
return InputObjectType{_var.val_};
}
template <class ArrayReader> for (size_t i = 0; i < length; ++i) {
std::optional<Error> read_array(const ArrayReader& _array_reader, err = get_string(&val, &buffer);
const InputArrayType& _arr) const noexcept { if (err != CborNoError) { return Error(cbor_error_string(err)); }
CborValue val; err = cbor_value_advance(&val);
auto buffer = std::vector<char>(); if (err != CborNoError) { return Error(cbor_error_string(err)); }
auto err = cbor_value_enter_container(_arr.val_, &val); const auto name = std::string_view(buffer.data(), buffer.size() - 1);
if (err != CborNoError && err != CborErrorOutOfMemory) { _object_reader.read(name, InputVarType {&val});
return Error(cbor_error_string(err)); cbor_value_advance(&val);
} }
size_t length = 0;
err = cbor_value_get_array_length(_arr.val_, &length); return std::nullopt;
if (err != CborNoError && err != CborErrorOutOfMemory) {
return Error(cbor_error_string(err));
}
for (size_t i = 0; i < length; ++i) {
const auto err2 = _array_reader.read(to_input_var(&val));
if (err2) {
return err2;
} }
err = cbor_value_advance(&val);
if (err != CborNoError && err != CborErrorOutOfMemory) { template <class T>
return Error(cbor_error_string(err)); rfl::Result<T> use_custom_constructor(
const InputVarType& _var) const noexcept {
try {
return T::from_cbor_obj(_var);
} catch (std::exception& e) { return rfl::Error(e.what()); }
} }
}
return std::nullopt;
}
template <class ObjectReader> private:
std::optional<Error> read_object(const ObjectReader& _object_reader, CborError get_string(const CborValue* _ptr,
const InputObjectType& _obj) const noexcept { std::vector<char>* _buffer) const noexcept {
size_t length = 0; size_t length = 0;
auto err = cbor_value_get_map_length(_obj.val_, &length); auto err = cbor_value_get_string_length(_ptr, &length);
if (err != CborNoError) { if (err != CborNoError && err != CborErrorOutOfMemory) { return err; }
return Error(cbor_error_string(err)); _buffer->resize(length + 1);
} (*_buffer)[length] = '\0';
return cbor_value_copy_text_string(_ptr, _buffer->data(), &length,
CborValue val; NULL);
err = cbor_value_enter_container(_obj.val_, &val);
if (err != CborNoError) {
return Error(cbor_error_string(err));
}
auto buffer = std::vector<char>();
for (size_t i = 0; i < length; ++i) {
err = get_string(&val, &buffer);
if (err != CborNoError) {
return Error(cbor_error_string(err));
} }
err = cbor_value_advance(&val);
if (err != CborNoError) { InputVarType to_input_var(CborValue* _ptr) const noexcept {
return Error(cbor_error_string(err)); values_->emplace_back(rfl::Box<CborValue>::make(*_ptr));
auto* last_value = values_->back().get();
return InputVarType {last_value};
} }
const auto name = std::string_view(buffer.data(), buffer.size() - 1);
_object_reader.read(name, InputVarType{&val});
cbor_value_advance(&val);
}
return std::nullopt; private:
} /// Contains the values inside the object.
rfl::Box<std::vector<rfl::Box<CborValue>>> values_;
};
template <class T> } // namespace cbor
rfl::Result<T> use_custom_constructor( } // namespace rfl
const InputVarType& _var) const noexcept {
try {
return T::from_cbor_obj(_var);
} catch (std::exception& e) {
return rfl::Error(e.what());
}
}
private: #endif // JSON_PARSER_HPP_
CborError get_string(const CborValue* _ptr,
std::vector<char>* _buffer) const noexcept {
size_t length = 0;
auto err = cbor_value_get_string_length(_ptr, &length);
if (err != CborNoError && err != CborErrorOutOfMemory) {
return err;
}
_buffer->resize(length + 1);
(*_buffer)[length] = '\0';
return cbor_value_copy_text_string(_ptr, _buffer->data(), &length, NULL);
}
InputVarType to_input_var(CborValue* _ptr) const noexcept {
values_->emplace_back(rfl::Box<CborValue>::make(*_ptr));
auto* last_value = values_->back().get();
return InputVarType{last_value};
}
private:
/// Contains the values inside the object.
rfl::Box<std::vector<rfl::Box<CborValue>>> values_;
};
} // namespace cbor
} // namespace rfl
#endif // JSON_PARSER_HPP_

View file

@ -2,7 +2,6 @@
#define RFL_CBOR_WRITER_HPP_ #define RFL_CBOR_WRITER_HPP_
#include <cbor.h> #include <cbor.h>
#include <exception> #include <exception>
#include <map> #include <map>
#include <sstream> #include <sstream>
@ -19,146 +18,153 @@
#include "../always_false.hpp" #include "../always_false.hpp"
namespace rfl { namespace rfl {
namespace cbor { namespace cbor {
class Writer { class Writer {
public: public:
struct CBOROutputArray { struct CBOROutputArray {
CborEncoder* encoder_; CborEncoder* encoder_;
CborEncoder* parent_; CborEncoder* parent_;
}; };
struct CBOROutputObject { struct CBOROutputObject {
CborEncoder* encoder_; CborEncoder* encoder_;
CborEncoder* parent_; CborEncoder* parent_;
}; };
struct CBOROutputVar {}; struct CBOROutputVar {};
using OutputArrayType = CBOROutputArray; using OutputArrayType = CBOROutputArray;
using OutputObjectType = CBOROutputObject; using OutputObjectType = CBOROutputObject;
using OutputVarType = CBOROutputVar; using OutputVarType = CBOROutputVar;
Writer(CborEncoder* _encoder) : encoder_(_encoder) {} Writer(CborEncoder* _encoder) : encoder_(_encoder) {}
~Writer() = default; ~Writer() = default;
OutputArrayType array_as_root(const size_t _size) const noexcept { OutputArrayType array_as_root(const size_t _size) const noexcept {
return new_array(_size, encoder_); return new_array(_size, encoder_);
} }
OutputObjectType object_as_root(const size_t _size) const noexcept { OutputObjectType object_as_root(const size_t _size) const noexcept {
return new_object(_size, encoder_); return new_object(_size, encoder_);
} }
OutputVarType null_as_root() const noexcept { OutputVarType null_as_root() const noexcept {
cbor_encode_null(encoder_); cbor_encode_null(encoder_);
return OutputVarType{}; return OutputVarType {};
} }
template <class T> template <class T>
OutputVarType value_as_root(const T& _var) const noexcept { OutputVarType value_as_root(const T& _var) const noexcept {
return new_value(_var, encoder_); return new_value(_var, encoder_);
} }
OutputArrayType add_array_to_array(const size_t _size, OutputArrayType add_array_to_array(
OutputArrayType* _parent) const noexcept { const size_t _size,
return new_array(_size, _parent->encoder_); OutputArrayType* _parent) const noexcept {
} return new_array(_size, _parent->encoder_);
}
OutputArrayType add_array_to_object( OutputArrayType add_array_to_object(
const std::string_view& _name, const size_t _size, const std::string_view& _name,
OutputObjectType* _parent) const noexcept { const size_t _size,
cbor_encode_text_string(_parent->encoder_, _name.data(), _name.size()); OutputObjectType* _parent) const noexcept {
return new_array(_size, _parent->encoder_); cbor_encode_text_string(_parent->encoder_, _name.data(), _name.size());
} return new_array(_size, _parent->encoder_);
}
OutputObjectType add_object_to_array( OutputObjectType add_object_to_array(
const size_t _size, OutputArrayType* _parent) const noexcept { const size_t _size,
return new_object(_size, _parent->encoder_); OutputArrayType* _parent) const noexcept {
} return new_object(_size, _parent->encoder_);
}
OutputObjectType add_object_to_object( OutputObjectType add_object_to_object(
const std::string_view& _name, const size_t _size, const std::string_view& _name,
OutputObjectType* _parent) const noexcept { const size_t _size,
cbor_encode_text_string(_parent->encoder_, _name.data(), _name.size()); OutputObjectType* _parent) const noexcept {
return new_object(_size, _parent->encoder_); cbor_encode_text_string(_parent->encoder_, _name.data(), _name.size());
} return new_object(_size, _parent->encoder_);
}
template <class T> template <class T>
OutputVarType add_value_to_array(const T& _var, OutputVarType add_value_to_array(const T& _var, OutputArrayType* _parent)
OutputArrayType* _parent) const noexcept { const noexcept {
return new_value(_var, _parent->encoder_); return new_value(_var, _parent->encoder_);
} }
template <class T> template <class T>
OutputVarType add_value_to_object(const std::string_view& _name, OutputVarType add_value_to_object(
const T& _var, const std::string_view& _name,
OutputObjectType* _parent) const noexcept { const T& _var,
cbor_encode_text_string(_parent->encoder_, _name.data(), _name.size()); OutputObjectType* _parent) const noexcept {
return new_value(_var, _parent->encoder_); cbor_encode_text_string(_parent->encoder_, _name.data(), _name.size());
} return new_value(_var, _parent->encoder_);
}
OutputVarType add_null_to_array(OutputArrayType* _parent) const noexcept { OutputVarType add_null_to_array(OutputArrayType* _parent) const noexcept {
cbor_encode_null(_parent->encoder_); cbor_encode_null(_parent->encoder_);
return OutputVarType{}; return OutputVarType {};
} }
OutputVarType add_null_to_object(const std::string_view& _name, OutputVarType add_null_to_object(
OutputObjectType* _parent) const noexcept { const std::string_view& _name,
cbor_encode_text_string(_parent->encoder_, _name.data(), _name.size()); OutputObjectType* _parent) const noexcept {
cbor_encode_null(_parent->encoder_); cbor_encode_text_string(_parent->encoder_, _name.data(), _name.size());
return OutputVarType{}; cbor_encode_null(_parent->encoder_);
} return OutputVarType {};
}
void end_array(OutputArrayType* _arr) const noexcept { void end_array(OutputArrayType* _arr) const noexcept {
cbor_encoder_close_container(_arr->parent_, _arr->encoder_); cbor_encoder_close_container(_arr->parent_, _arr->encoder_);
} }
void end_object(OutputObjectType* _obj) const noexcept { void end_object(OutputObjectType* _obj) const noexcept {
cbor_encoder_close_container(_obj->parent_, _obj->encoder_); cbor_encoder_close_container(_obj->parent_, _obj->encoder_);
} }
private: private:
OutputArrayType new_array(const size_t _size, OutputArrayType new_array(const size_t _size,
CborEncoder* _parent) const noexcept { CborEncoder* _parent) const noexcept {
subencoders_->emplace_back(rfl::Box<CborEncoder>::make()); subencoders_->emplace_back(rfl::Box<CborEncoder>::make());
cbor_encoder_create_array(_parent, subencoders_->back().get(), _size); cbor_encoder_create_array(_parent, subencoders_->back().get(), _size);
return OutputArrayType{subencoders_->back().get(), _parent}; return OutputArrayType {subencoders_->back().get(), _parent};
} }
OutputObjectType new_object(const size_t _size, OutputObjectType new_object(const size_t _size,
CborEncoder* _parent) const noexcept {
subencoders_->emplace_back(rfl::Box<CborEncoder>::make());
cbor_encoder_create_map(_parent, subencoders_->back().get(), _size);
return OutputObjectType {subencoders_->back().get(), _parent};
}
template <class T>
OutputVarType new_value(const T& _var,
CborEncoder* _parent) const noexcept { CborEncoder* _parent) const noexcept {
subencoders_->emplace_back(rfl::Box<CborEncoder>::make()); if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
cbor_encoder_create_map(_parent, subencoders_->back().get(), _size); cbor_encode_text_string(_parent, _var.c_str(), _var.size());
return OutputObjectType{subencoders_->back().get(), _parent}; } else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
} cbor_encode_boolean(_parent, _var);
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>()) {
cbor_encode_double(_parent, static_cast<double>(_var));
} else if constexpr (std::is_integral<std::remove_cvref_t<T>>()) {
cbor_encode_int(_parent, static_cast<std::int64_t>(_var));
} else {
static_assert(rfl::always_false_v<T>, "Unsupported type.");
}
return OutputVarType {};
}
template <class T> private:
OutputVarType new_value(const T& _var, CborEncoder* _parent) const noexcept { /// The underlying TinyCBOR encoder.
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) { CborEncoder* const encoder_;
cbor_encode_text_string(_parent, _var.c_str(), _var.size());
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
cbor_encode_boolean(_parent, _var);
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>()) {
cbor_encode_double(_parent, static_cast<double>(_var));
} else if constexpr (std::is_integral<std::remove_cvref_t<T>>()) {
cbor_encode_int(_parent, static_cast<std::int64_t>(_var));
} else {
static_assert(rfl::always_false_v<T>, "Unsupported type.");
}
return OutputVarType{};
}
private: /// Contain all of the subobjects and subarrays.
/// The underlying TinyCBOR encoder. const rfl::Box<std::vector<rfl::Box<CborEncoder>>> subencoders_;
CborEncoder* const encoder_; };
/// Contain all of the subobjects and subarrays. } // namespace cbor
const rfl::Box<std::vector<rfl::Box<CborEncoder>>> subencoders_; } // namespace rfl
};
} // namespace cbor #endif // CBOR_PARSER_HPP_
} // namespace rfl
#endif // CBOR_PARSER_HPP_

View file

@ -7,17 +7,17 @@
#include "read.hpp" #include "read.hpp"
namespace rfl { namespace rfl {
namespace cbor { namespace cbor {
template <class T, class... Ps> template <class T, class... Ps>
Result<T> load(const std::string& _fname) { Result<T> load(const std::string& _fname) {
const auto read_bytes = [](const auto& _bytes) { const auto read_bytes = [](const auto& _bytes) {
return read<T, Ps...>(_bytes); return read<T, Ps...>(_bytes);
}; };
return rfl::io::load_bytes(_fname).and_then(read_bytes); return rfl::io::load_bytes(_fname).and_then(read_bytes);
} }
} // namespace cbor } // namespace cbor
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -2,7 +2,6 @@
#define RFL_CBOR_READ_HPP_ #define RFL_CBOR_READ_HPP_
#include <cbor.h> #include <cbor.h>
#include <istream> #include <istream>
#include <string> #include <string>
@ -12,46 +11,46 @@
#include "Reader.hpp" #include "Reader.hpp"
namespace rfl { namespace rfl {
namespace cbor { namespace cbor {
using InputObjectType = typename Reader::InputObjectType; using InputObjectType = typename Reader::InputObjectType;
using InputVarType = typename Reader::InputVarType; using InputVarType = typename Reader::InputVarType;
/// Parses an object from a CBOR var. /// Parses an object from a CBOR var.
template <class T, class... Ps> template <class T, class... Ps>
auto read(const InputVarType& _obj) { auto read(const InputVarType& _obj) {
const auto r = Reader(); const auto r = Reader();
return Parser<T, Processors<Ps...>>::read(r, _obj); return Parser<T, Processors<Ps...>>::read(r, _obj);
} }
/// Parses an object from CBOR using reflection. /// Parses an object from CBOR using reflection.
template <class T, class... Ps> template <class T, class... Ps>
Result<internal::wrap_in_rfl_array_t<T>> read(const char* _bytes, Result<internal::wrap_in_rfl_array_t<T>> read(const char* _bytes,
const size_t _size) { const size_t _size) {
CborParser parser; CborParser parser;
CborValue value; CborValue value;
cbor_parser_init(reinterpret_cast<const uint8_t*>(_bytes), _size, 0, &parser, cbor_parser_init(reinterpret_cast<const uint8_t*>(_bytes), _size, 0,
&value); &parser, &value);
auto doc = InputVarType{&value}; auto doc = InputVarType {&value};
auto result = read<T, Ps...>(doc); auto result = read<T, Ps...>(doc);
return result; return result;
} }
/// Parses an object from CBOR using reflection. /// Parses an object from CBOR using reflection.
template <class T, class... Ps> template <class T, class... Ps>
auto read(const std::vector<char>& _bytes) { auto read(const std::vector<char>& _bytes) {
return read<T, Ps...>(_bytes.data(), _bytes.size()); return read<T, Ps...>(_bytes.data(), _bytes.size());
} }
/// Parses an object from a stream. /// Parses an object from a stream.
template <class T, class... Ps> template <class T, class... Ps>
auto read(std::istream& _stream) { auto read(std::istream& _stream) {
std::istreambuf_iterator<char> begin(_stream), end; std::istreambuf_iterator<char> begin(_stream), end;
auto bytes = std::vector<char>(begin, end); auto bytes = std::vector<char>(begin, end);
return read<T, Ps...>(bytes.data(), bytes.size()); return read<T, Ps...>(bytes.data(), bytes.size());
} }
} // namespace cbor } // namespace cbor
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -10,17 +10,17 @@
#include "write.hpp" #include "write.hpp"
namespace rfl { namespace rfl {
namespace cbor { namespace cbor {
template <class... Ps> template <class... Ps>
Result<Nothing> save(const std::string& _fname, const auto& _obj) { Result<Nothing> save(const std::string& _fname, const auto& _obj) {
const auto write_func = [](const auto& _obj, auto& _stream) -> auto& { const auto write_func = [](const auto& _obj, auto& _stream) -> auto& {
return write<Ps...>(_obj, _stream); return write<Ps...>(_obj, _stream);
}; };
return rfl::io::save_bytes(_fname, _obj, write_func); return rfl::io::save_bytes(_fname, _obj, write_func);
} }
} // namespace cbor } // namespace cbor
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -2,7 +2,6 @@
#define RFL_CBOR_WRITE_HPP_ #define RFL_CBOR_WRITE_HPP_
#include <cbor.h> #include <cbor.h>
#include <cstdint> #include <cstdint>
#include <ostream> #include <ostream>
#include <sstream> #include <sstream>
@ -13,47 +12,48 @@
#include "Parser.hpp" #include "Parser.hpp"
namespace rfl { namespace rfl {
namespace cbor { namespace cbor {
template <class... Ps> template <class... Ps>
void write_into_buffer(const auto& _obj, CborEncoder* _encoder, void write_into_buffer(const auto& _obj,
std::vector<char>* _buffer) noexcept { CborEncoder* _encoder,
using T = std::remove_cvref_t<decltype(_obj)>; std::vector<char>* _buffer) noexcept {
using ParentType = parsing::Parent<Writer>; using T = std::remove_cvref_t<decltype(_obj)>;
cbor_encoder_init(_encoder, reinterpret_cast<uint8_t*>(_buffer->data()), using ParentType = parsing::Parent<Writer>;
_buffer->size(), 0); cbor_encoder_init(_encoder, reinterpret_cast<uint8_t*>(_buffer->data()),
const auto writer = Writer(_encoder); _buffer->size(), 0);
Parser<T, Processors<Ps...>>::write(writer, _obj, const auto writer = Writer(_encoder);
typename ParentType::Root{}); Parser<T, Processors<Ps...>>::write(writer, _obj,
} typename ParentType::Root {});
}
/// Returns CBOR bytes. /// Returns CBOR bytes.
template <class... Ps> template <class... Ps>
std::vector<char> write(const auto& _obj) noexcept { std::vector<char> write(const auto& _obj) noexcept {
std::vector<char> buffer(4096); std::vector<char> buffer(4096);
CborEncoder encoder; CborEncoder encoder;
write_into_buffer<Ps...>(_obj, &encoder, &buffer); write_into_buffer<Ps...>(_obj, &encoder, &buffer);
const auto total_bytes_needed = const auto total_bytes_needed =
buffer.size() + cbor_encoder_get_extra_bytes_needed(&encoder); buffer.size() + cbor_encoder_get_extra_bytes_needed(&encoder);
if (total_bytes_needed != buffer.size()) { if (total_bytes_needed != buffer.size()) {
buffer.resize(total_bytes_needed); buffer.resize(total_bytes_needed);
write_into_buffer<Ps...>(_obj, &encoder, &buffer); write_into_buffer<Ps...>(_obj, &encoder, &buffer);
} }
const auto length = cbor_encoder_get_buffer_size( const auto length = cbor_encoder_get_buffer_size(
&encoder, reinterpret_cast<uint8_t*>(buffer.data())); &encoder, reinterpret_cast<uint8_t*>(buffer.data()));
buffer.resize(length); buffer.resize(length);
return buffer; return buffer;
} }
/// Writes a CBOR into an ostream. /// Writes a CBOR into an ostream.
template <class... Ps> template <class... Ps>
std::ostream& write(const auto& _obj, std::ostream& _stream) noexcept { std::ostream& write(const auto& _obj, std::ostream& _stream) noexcept {
auto buffer = write<Ps...>(_obj); auto buffer = write<Ps...>(_obj);
_stream.write(buffer.data(), buffer.size()); _stream.write(buffer.data(), buffer.size());
return _stream; return _stream;
} }
} // namespace cbor } // namespace cbor
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -8,150 +8,152 @@
namespace rfl { namespace rfl {
template <auto _threshold> template <auto _threshold>
struct EqualTo { struct EqualTo {
template <class T> template <class T>
static Result<T> validate(T _value) noexcept { static Result<T> validate(T _value) noexcept {
constexpr auto threshold = static_cast<T>(_threshold); constexpr auto threshold = static_cast<T>(_threshold);
if (_value != threshold) { if (_value != threshold) {
return Error("Value expected to be equal to " + return Error("Value expected to be equal to " +
std::to_string(threshold) + ", but got " + std::to_string(threshold) + ", but got " +
std::to_string(_value) + "."); std::to_string(_value) + ".");
}
return _value;
} }
return _value;
}
template <class T> template <class T>
static parsing::schema::ValidationType to_schema() { static parsing::schema::ValidationType to_schema() {
using ValidationType = parsing::schema::ValidationType; using ValidationType = parsing::schema::ValidationType;
const auto value = const auto value =
std::is_floating_point_v<T> std::is_floating_point_v<T>
? std::variant<double, int>(static_cast<double>(_threshold)) ? std::variant<double, int>(static_cast<double>(_threshold))
: std::variant<double, int>(static_cast<int>(_threshold)); : std::variant<double, int>(static_cast<int>(_threshold));
return ValidationType{ValidationType::EqualTo{.value_ = value}}; 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) {
return Error("Value expected to be greater than or equal to " +
std::to_string(threshold) + ", but got " +
std::to_string(_value) + ".");
} }
return _value; };
}
template <class T> template <auto _threshold>
static parsing::schema::ValidationType to_schema() { struct Minimum {
using ValidationType = parsing::schema::ValidationType; template <class T>
const auto value = static Result<T> validate(T _value) noexcept {
std::is_floating_point_v<T> constexpr auto threshold = static_cast<T>(_threshold);
? std::variant<double, int>(static_cast<double>(_threshold)) if (_value < threshold) {
: std::variant<double, int>(static_cast<int>(_threshold)); return Error("Value expected to be greater than or equal to " +
return ValidationType{ValidationType::Minimum{.value_ = value}}; std::to_string(threshold) + ", but got " +
} std::to_string(_value) + ".");
}; }
return _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) {
return Error("Value expected to be greater than " +
std::to_string(threshold) + ", but got " +
std::to_string(_value) + ".");
} }
return _value;
}
template <class T> template <class T>
static parsing::schema::ValidationType to_schema() { static parsing::schema::ValidationType to_schema() {
using ValidationType = parsing::schema::ValidationType; using ValidationType = parsing::schema::ValidationType;
const auto value = const auto value =
std::is_floating_point_v<T> std::is_floating_point_v<T>
? std::variant<double, int>(static_cast<double>(_threshold)) ? std::variant<double, int>(static_cast<double>(_threshold))
: std::variant<double, int>(static_cast<int>(_threshold)); : std::variant<double, int>(static_cast<int>(_threshold));
return ValidationType{ValidationType::ExclusiveMinimum{.value_ = value}}; return ValidationType {ValidationType::Minimum {.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) {
return Error("Value expected to be less than or equal to " +
std::to_string(threshold) + ", but got " +
std::to_string(_value) + ".");
} }
return _value; };
}
template <class T> template <auto _threshold>
static parsing::schema::ValidationType to_schema() { struct ExclusiveMinimum {
using ValidationType = parsing::schema::ValidationType; template <class T>
const auto value = static Result<T> validate(T _value) noexcept {
std::is_floating_point_v<T> constexpr auto threshold = static_cast<T>(_threshold);
? std::variant<double, int>(static_cast<double>(_threshold)) if (_value <= threshold) {
: std::variant<double, int>(static_cast<int>(_threshold)); return Error("Value expected to be greater than " +
return ValidationType{ValidationType::Maximum{.value_ = value}}; std::to_string(threshold) + ", but got " +
} std::to_string(_value) + ".");
}; }
return _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) {
return Error("Value expected to be less than " +
std::to_string(threshold) + ", but got " +
std::to_string(_value) + ".");
} }
return _value;
}
template <class T> template <class T>
static parsing::schema::ValidationType to_schema() { static parsing::schema::ValidationType to_schema() {
using ValidationType = parsing::schema::ValidationType; using ValidationType = parsing::schema::ValidationType;
const auto value = const auto value =
std::is_floating_point_v<T> std::is_floating_point_v<T>
? std::variant<double, int>(static_cast<double>(_threshold)) ? std::variant<double, int>(static_cast<double>(_threshold))
: std::variant<double, int>(static_cast<int>(_threshold)); : std::variant<double, int>(static_cast<int>(_threshold));
return ValidationType{ValidationType::ExclusiveMaximum{.value_ = value}}; return ValidationType {
} ValidationType::ExclusiveMinimum {.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) {
return Error("Value expected to not be equal to " +
std::to_string(threshold) + ", but got " +
std::to_string(_value) + ".");
} }
return _value; };
}
template <class T> template <auto _threshold>
static parsing::schema::ValidationType to_schema() { struct Maximum {
using ValidationType = parsing::schema::ValidationType; template <class T>
const auto value = static Result<T> validate(T _value) noexcept {
std::is_floating_point_v<T> constexpr auto threshold = static_cast<T>(_threshold);
? std::variant<double, int>(static_cast<double>(_threshold)) if (_value > threshold) {
: std::variant<double, int>(static_cast<int>(_threshold)); return Error("Value expected to be less than or equal to " +
return ValidationType{ValidationType::NotEqualTo{.value_ = value}}; std::to_string(threshold) + ", but got " +
} std::to_string(_value) + ".");
}; }
return _value;
}
} // namespace rfl template <class T>
static parsing::schema::ValidationType to_schema() {
using ValidationType = parsing::schema::ValidationType;
const auto value =
std::is_floating_point_v<T>
? std::variant<double, int>(static_cast<double>(_threshold))
: std::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) {
return Error("Value expected to be less than " +
std::to_string(threshold) + ", but got " +
std::to_string(_value) + ".");
}
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>
? std::variant<double, int>(static_cast<double>(_threshold))
: std::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) {
return Error("Value expected to not be equal to " +
std::to_string(threshold) + ", but got " +
std::to_string(_value) + ".");
}
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>
? std::variant<double, int>(static_cast<double>(_threshold))
: std::variant<double, int>(static_cast<int>(_threshold));
return ValidationType {ValidationType::NotEqualTo {.value_ = value}};
}
};
} // namespace rfl
#endif #endif

View file

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

View file

@ -6,11 +6,11 @@
namespace rfl { namespace rfl {
/// Allows you to combine several literal types. /// Allows you to combine several literal types.
template <class... LiteralTypes> template <class... LiteralTypes>
using define_literal_t = using define_literal_t =
typename internal::define_literal<LiteralTypes...>::type; typename internal::define_literal<LiteralTypes...>::type;
} // namespace rfl } // namespace rfl
#endif // RFL_DEFINELITERAL_HPP_ #endif // RFL_DEFINELITERAL_HPP_

View file

@ -6,10 +6,10 @@
namespace rfl { namespace rfl {
template <class... FieldTypes> template <class... FieldTypes>
using define_named_tuple_t = using define_named_tuple_t =
typename internal::define_named_tuple<FieldTypes...>::type; typename internal::define_named_tuple<FieldTypes...>::type;
} // namespace rfl } // namespace rfl
#endif // RFL_DEFINENAMEDTUPLE_HPP_ #endif // RFL_DEFINENAMEDTUPLE_HPP_

View file

@ -7,11 +7,11 @@
namespace rfl { namespace rfl {
template <internal::StringLiteral _discriminator, class... TaggedUnionTypes> template <internal::StringLiteral _discriminator, class... TaggedUnionTypes>
using define_tagged_union_t = using define_tagged_union_t =
typename internal::define_tagged_union<_discriminator, typename internal::define_tagged_union<_discriminator,
TaggedUnionTypes...>::type; TaggedUnionTypes...>::type;
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -7,9 +7,9 @@
namespace rfl { namespace rfl {
template <class... Vars> template <class... Vars>
using define_variant_t = typename internal::define_variant<Vars...>::type; using define_variant_t = typename internal::define_variant<Vars...>::type;
} // namespace rfl } // namespace rfl
#endif // RFL_DEFINEVARIANT_HPP_ #endif // RFL_DEFINEVARIANT_HPP_

View file

@ -11,54 +11,56 @@
namespace rfl { namespace rfl {
// Converts an enum value to a string. // Converts an enum value to a string.
template <internal::enums::is_scoped_enum EnumType> template <internal::enums::is_scoped_enum EnumType>
std::string enum_to_string(EnumType _enum) { std::string enum_to_string(EnumType _enum) {
return rfl::internal::enums::StringConverter<EnumType>::enum_to_string(_enum); return rfl::internal::enums::StringConverter<EnumType>::enum_to_string(
} _enum);
}
// Converts a string to a value of the given enum type. // Converts a string to a value of the given enum type.
template <internal::enums::is_scoped_enum EnumType> template <internal::enums::is_scoped_enum EnumType>
rfl::Result<EnumType> string_to_enum(const std::string& _str) { rfl::Result<EnumType> string_to_enum(const std::string& _str) {
return rfl::internal::enums::StringConverter<EnumType>::string_to_enum(_str); return rfl::internal::enums::StringConverter<EnumType>::string_to_enum(
} _str);
}
// Returns a named tuple mapping names of enumerators of the given enum type to // Returns a named tuple mapping names of enumerators of the given enum type
// their values. // to their values.
template <internal::enums::is_scoped_enum EnumType> template <internal::enums::is_scoped_enum EnumType>
auto get_enumerators() { auto get_enumerators() {
constexpr auto names = internal::enums::get_enum_names< constexpr auto names = internal::enums::get_enum_names<
EnumType, internal::enums::is_flag_enum<EnumType>>(); EnumType, internal::enums::is_flag_enum<EnumType>>();
return internal::enums::names_to_enumerator_named_tuple(names); return internal::enums::names_to_enumerator_named_tuple(names);
} }
// Returns a named tuple mapping names of enumerators of the given enum type to // Returns a named tuple mapping names of enumerators of the given enum type
// their underlying values. // to their underlying values.
template <internal::enums::is_scoped_enum EnumType> template <internal::enums::is_scoped_enum EnumType>
auto get_underlying_enumerators() { auto get_underlying_enumerators() {
constexpr auto names = internal::enums::get_enum_names< constexpr auto names = internal::enums::get_enum_names<
EnumType, internal::enums::is_flag_enum<EnumType>>(); EnumType, internal::enums::is_flag_enum<EnumType>>();
return internal::enums::names_to_underlying_enumerator_named_tuple(names); return internal::enums::names_to_underlying_enumerator_named_tuple(names);
} }
// Returns an std::array containing pairs of enumerator names (as // Returns an std::array containing pairs of enumerator names (as
// std::string_view) and values. // std::string_view) and values.
template <internal::enums::is_scoped_enum EnumType> template <internal::enums::is_scoped_enum EnumType>
constexpr auto get_enumerator_array() { constexpr auto get_enumerator_array() {
constexpr auto names = internal::enums::get_enum_names< constexpr auto names = internal::enums::get_enum_names<
EnumType, internal::enums::is_flag_enum<EnumType>>(); EnumType, internal::enums::is_flag_enum<EnumType>>();
return internal::enums::names_to_enumerator_array(names); return internal::enums::names_to_enumerator_array(names);
} }
// Returns an std::array containing pairs of enumerator names (as // Returns an std::array containing pairs of enumerator names (as
// std::string_view) and underlying values. // std::string_view) and underlying values.
template <internal::enums::is_scoped_enum EnumType> template <internal::enums::is_scoped_enum EnumType>
constexpr auto get_underlying_enumerator_array() { constexpr auto get_underlying_enumerator_array() {
constexpr auto names = internal::enums::get_enum_names< constexpr auto names = internal::enums::get_enum_names<
EnumType, internal::enums::is_flag_enum<EnumType>>(); EnumType, internal::enums::is_flag_enum<EnumType>>();
return internal::enums::names_to_underlying_enumerator_array(names); return internal::enums::names_to_underlying_enumerator_array(names);
} }
} // namespace rfl } // namespace rfl
#endif // RFL_ENUMS_HPP_ #endif // RFL_ENUMS_HPP_

View file

@ -10,11 +10,12 @@
namespace rfl { namespace rfl {
/// Extracts a Literal containing all of the discriminators from a TaggedUnion. /// Extracts a Literal containing all of the discriminators from a
template <class TaggedUnionType> /// TaggedUnion.
using extract_discriminators_t = template <class TaggedUnionType>
typename internal::extract_discriminators<TaggedUnionType>::type; using extract_discriminators_t =
typename internal::extract_discriminators<TaggedUnionType>::type;
} // namespace rfl } // namespace rfl
#endif // RFL_EXTRACTDISTRIMINATORS_HPP_ #endif // RFL_EXTRACTDISTRIMINATORS_HPP_

View file

@ -8,11 +8,11 @@
namespace rfl { namespace rfl {
/// Returns a rfl::Literal containing the field names of struct T. /// Returns a rfl::Literal containing the field names of struct T.
template <class T> template <class T>
using field_names_t = typename std::invoke_result< using field_names_t = typename std::invoke_result<
decltype(internal::get_field_names<std::remove_cvref_t<T>>)>::type; decltype(internal::get_field_names<std::remove_cvref_t<T>>)>::type;
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -10,9 +10,9 @@
namespace rfl { namespace rfl {
template <internal::StringLiteral _field_name, class T> template <internal::StringLiteral _field_name, class T>
using field_type_t = typename internal::FieldType<_field_name, T>::Type; using field_type_t = typename internal::FieldType<_field_name, T>::Type;
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -6,12 +6,12 @@
namespace rfl { namespace rfl {
/// Returns meta-information about the fields. /// Returns meta-information about the fields.
template <class T> template <class T>
auto fields() { auto fields() {
return internal::get_meta_fields<named_tuple_t<T>>(); return internal::get_meta_fields<named_tuple_t<T>>();
} }
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -6,12 +6,12 @@
#include "Writer.hpp" #include "Writer.hpp"
namespace rfl { namespace rfl {
namespace flexbuf { namespace flexbuf {
template <class T, class ProcessorsType> template <class T, class ProcessorsType>
using Parser = parsing::Parser<Reader, Writer, T, ProcessorsType>; using Parser = parsing::Parser<Reader, Writer, T, ProcessorsType>;
} }
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -1,9 +1,8 @@
#ifndef FLEXBUF_READER_HPP_ #ifndef FLEXBUF_READER_HPP_
#define FLEXBUF_READER_HPP_ #define FLEXBUF_READER_HPP_
#include <flatbuffers/flexbuffers.h>
#include <exception> #include <exception>
#include <flatbuffers/flexbuffers.h>
#include <map> #include <map>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
@ -16,130 +15,129 @@
#include "../always_false.hpp" #include "../always_false.hpp"
namespace rfl { namespace rfl {
namespace flexbuf { namespace flexbuf {
struct Reader { struct Reader {
using InputArrayType = flexbuffers::Vector; using InputArrayType = flexbuffers::Vector;
using InputObjectType = flexbuffers::Map; using InputObjectType = flexbuffers::Map;
using InputVarType = flexbuffers::Reference; using InputVarType = flexbuffers::Reference;
template <class T, class = void> template <class T, class = void>
struct has_from_flexbuf : std::false_type {}; struct has_from_flexbuf : std::false_type {};
template <class T> template <class T>
struct has_from_flexbuf< struct has_from_flexbuf<
T, std::enable_if_t<std::is_invocable_r<T, decltype(T::from_flexbuf), T,
InputVarType>::value>> std::enable_if_t<std::is_invocable_r<T,
: std::true_type {}; decltype(T::from_flexbuf),
InputVarType>::value>>
: std::true_type {};
template <class T> template <class T>
struct has_from_flexbuf< struct has_from_flexbuf<
T, std::enable_if_t<std::is_invocable_r< T,
rfl::Result<T>, decltype(T::from_flexbuf), InputVarType>::value>> std::enable_if_t<std::is_invocable_r<rfl::Result<T>,
: std::true_type {}; decltype(T::from_flexbuf),
InputVarType>::value>>
: std::true_type {};
template <class T> template <class T>
static constexpr bool has_custom_constructor = has_from_flexbuf<T>::value; static constexpr bool has_custom_constructor = has_from_flexbuf<T>::value;
rfl::Result<InputVarType> get_field( rfl::Result<InputVarType> get_field(
const std::string& _name, const InputObjectType& _obj) const noexcept { const std::string& _name,
const auto keys = _obj.Keys(); const InputObjectType& _obj) const noexcept {
for (size_t i = 0; i < keys.size(); ++i) { const auto keys = _obj.Keys();
if (_name == keys[i].AsString().c_str()) { for (size_t i = 0; i < keys.size(); ++i) {
return _obj.Values()[i]; if (_name == keys[i].AsString().c_str()) { return _obj.Values()[i]; }
}
return rfl::Error("Map does not contain any element called '" + _name +
"'.");
} }
}
return rfl::Error("Map does not contain any element called '" + _name +
"'.");
}
bool is_empty(const InputVarType& _var) const noexcept { bool is_empty(const InputVarType& _var) const noexcept {
return _var.IsNull(); return _var.IsNull();
}
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>()) {
if (!_var.IsString()) {
return rfl::Error("Could not cast to string.");
} }
return std::string(_var.AsString().c_str());
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) { template <class T>
if (!_var.IsBool()) { rfl::Result<T> to_basic_type(const InputVarType& _var) const noexcept {
return rfl::Error("Could not cast to boolean."); if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
if (!_var.IsString()) {
return rfl::Error("Could not cast to string.");
}
return std::string(_var.AsString().c_str());
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
if (!_var.IsBool()) {
return rfl::Error("Could not cast to boolean.");
}
return _var.AsBool();
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>()) {
if (!_var.IsNumeric()) {
return rfl::Error("Could not cast to double.");
}
return static_cast<T>(_var.AsDouble());
} else if constexpr (std::is_integral<std::remove_cvref_t<T>>()) {
if (!_var.IsNumeric()) {
return rfl::Error("Could not cast to int.");
}
return static_cast<T>(_var.AsInt64());
} else {
static_assert(rfl::always_false_v<T>, "Unsupported type.");
}
} }
return _var.AsBool();
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>()) { template <class ArrayReader>
if (!_var.IsNumeric()) { std::optional<Error> read_array(
return rfl::Error("Could not cast to double."); const ArrayReader& _array_reader,
const InputArrayType& _arr) const noexcept {
const auto size = _arr.size();
for (size_t i = 0; i < size; ++i) {
const auto err = _array_reader.read(InputVarType(_arr[i]));
if (err) { return err; }
}
return std::nullopt;
} }
return static_cast<T>(_var.AsDouble());
} else if constexpr (std::is_integral<std::remove_cvref_t<T>>()) { template <class ObjectReader>
if (!_var.IsNumeric()) { std::optional<Error> read_object(
return rfl::Error("Could not cast to int."); const ObjectReader& _object_reader,
const InputObjectType& _obj) const noexcept {
const auto keys = _obj.Keys();
const auto values = _obj.Values();
const auto num_values = std::min(keys.size(), values.size());
for (size_t i = 0; i < num_values; ++i) {
_object_reader.read(std::string_view(keys[i].AsString().c_str()),
values[i]);
}
return std::nullopt;
} }
return static_cast<T>(_var.AsInt64());
} else {
static_assert(rfl::always_false_v<T>, "Unsupported type.");
}
}
template <class ArrayReader> rfl::Result<InputArrayType> to_array(
std::optional<Error> read_array(const ArrayReader& _array_reader, const InputVarType& _var) const noexcept {
const InputArrayType& _arr) const noexcept { if (!_var.IsVector()) {
const auto size = _arr.size(); return rfl::Error("Could not cast to Vector.");
for (size_t i = 0; i < size; ++i) { }
const auto err = _array_reader.read(InputVarType(_arr[i])); return _var.AsVector();
if (err) {
return err;
} }
}
return std::nullopt;
}
template <class ObjectReader> rfl::Result<InputObjectType> to_object(
std::optional<Error> read_object(const ObjectReader& _object_reader, const InputVarType& _var) const noexcept {
const InputObjectType& _obj) const noexcept { if (!_var.IsMap()) { return rfl::Error("Could not cast to Map!"); }
const auto keys = _obj.Keys(); return _var.AsMap();
const auto values = _obj.Values(); }
const auto num_values = std::min(keys.size(), values.size());
for (size_t i = 0; i < num_values; ++i) { template <class T>
_object_reader.read(std::string_view(keys[i].AsString().c_str()), rfl::Result<T> use_custom_constructor(
values[i]); const InputVarType& _var) const noexcept {
} try {
return T::from_flexbuf(_var);
} catch (std::exception& e) { return rfl::Error(e.what()); }
}
};
return std::nullopt; } // namespace flexbuf
} } // namespace rfl
rfl::Result<InputArrayType> to_array(
const InputVarType& _var) const noexcept {
if (!_var.IsVector()) {
return rfl::Error("Could not cast to Vector.");
}
return _var.AsVector();
}
rfl::Result<InputObjectType> to_object(
const InputVarType& _var) const noexcept {
if (!_var.IsMap()) {
return rfl::Error("Could not cast to Map!");
}
return _var.AsMap();
}
template <class T>
rfl::Result<T> use_custom_constructor(
const InputVarType& _var) const noexcept {
try {
return T::from_flexbuf(_var);
} catch (std::exception& e) {
return rfl::Error(e.what());
}
}
};
} // namespace flexbuf
} // namespace rfl
#endif #endif

View file

@ -1,9 +1,8 @@
#ifndef FLEXBUF_WRITER_HPP_ #ifndef FLEXBUF_WRITER_HPP_
#define FLEXBUF_WRITER_HPP_ #define FLEXBUF_WRITER_HPP_
#include <flatbuffers/flexbuffers.h>
#include <exception> #include <exception>
#include <flatbuffers/flexbuffers.h>
#include <functional> #include <functional>
#include <map> #include <map>
#include <optional> #include <optional>
@ -19,158 +18,165 @@
#include "../always_false.hpp" #include "../always_false.hpp"
namespace rfl { namespace rfl {
namespace flexbuf { namespace flexbuf {
struct Writer { struct Writer {
struct OutputArray { struct OutputArray {
size_t start_; size_t start_;
}; };
struct OutputObject { struct OutputObject {
size_t start_; size_t start_;
}; };
struct OutputVar {}; struct OutputVar {};
using OutputArrayType = OutputArray; using OutputArrayType = OutputArray;
using OutputObjectType = OutputObject; using OutputObjectType = OutputObject;
using OutputVarType = OutputVar; using OutputVarType = OutputVar;
Writer(const Ref<flexbuffers::Builder>& _fbb) : fbb_(_fbb) {} Writer(const Ref<flexbuffers::Builder>& _fbb) : fbb_(_fbb) {}
~Writer() = default; ~Writer() = default;
OutputArrayType array_as_root(const size_t _size) const noexcept { OutputArrayType array_as_root(const size_t _size) const noexcept {
return new_array(); return new_array();
} }
OutputObjectType object_as_root(const size_t _size) const noexcept { OutputObjectType object_as_root(const size_t _size) const noexcept {
return new_object(); return new_object();
} }
OutputVarType null_as_root() const noexcept { OutputVarType null_as_root() const noexcept {
fbb_->Null(); fbb_->Null();
return OutputVarType{}; return OutputVarType {};
} }
template <class T> template <class T>
OutputVarType value_as_root(const T& _var) const noexcept { OutputVarType value_as_root(const T& _var) const noexcept {
return insert_value(_var); return insert_value(_var);
} }
OutputArrayType add_array_to_array(const size_t _size, OutputArrayType add_array_to_array(
OutputArrayType* _parent) const noexcept { const size_t _size,
return new_array(); OutputArrayType* _parent) const noexcept {
} return new_array();
}
OutputArrayType add_array_to_object( OutputArrayType add_array_to_object(
const std::string_view& _name, const size_t _size, const std::string_view& _name,
OutputObjectType* _parent) const noexcept { const size_t _size,
return new_array(_name); OutputObjectType* _parent) const noexcept {
} return new_array(_name);
}
OutputObjectType add_object_to_array( OutputObjectType add_object_to_array(
const size_t _size, OutputArrayType* _parent) const noexcept { const size_t _size,
return new_object(); OutputArrayType* _parent) const noexcept {
} return new_object();
}
OutputObjectType add_object_to_object( OutputObjectType add_object_to_object(
const std::string_view& _name, const size_t _size, const std::string_view& _name,
OutputObjectType* _parent) const noexcept { const size_t _size,
return new_object(_name); OutputObjectType* _parent) const noexcept {
} return new_object(_name);
}
template <class T> template <class T>
OutputVarType add_value_to_array(const T& _var, OutputVarType add_value_to_array(const T& _var, OutputArrayType* _parent)
OutputArrayType* _parent) const noexcept { const noexcept {
return insert_value(_var); return insert_value(_var);
} }
template <class T> template <class T>
OutputVarType add_value_to_object(const std::string_view& _name, OutputVarType add_value_to_object(
const T& _var, const std::string_view& _name,
OutputObjectType* _parent) const noexcept { const T& _var,
return insert_value(_name, _var); OutputObjectType* _parent) const noexcept {
} return insert_value(_name, _var);
}
OutputVarType add_null_to_array(OutputArrayType* _parent) const noexcept { OutputVarType add_null_to_array(OutputArrayType* _parent) const noexcept {
fbb_->Null(); fbb_->Null();
return OutputVarType{}; return OutputVarType {};
} }
OutputVarType add_null_to_object(const std::string_view& _name, OutputVarType add_null_to_object(
OutputObjectType* _parent) const noexcept { const std::string_view& _name,
fbb_->Null(_name.data()); OutputObjectType* _parent) const noexcept {
return OutputVarType{}; fbb_->Null(_name.data());
} return OutputVarType {};
}
void end_array(OutputArrayType* _arr) const noexcept { void end_array(OutputArrayType* _arr) const noexcept {
fbb_->EndVector(_arr->start_, false, false); fbb_->EndVector(_arr->start_, false, false);
} }
void end_object(OutputObjectType* _obj) const noexcept { void end_object(OutputObjectType* _obj) const noexcept {
fbb_->EndMap(_obj->start_); fbb_->EndMap(_obj->start_);
} }
private: private:
template <class T> template <class T>
OutputVarType insert_value(const std::string_view& _name, OutputVarType insert_value(const std::string_view& _name,
const T& _var) const noexcept { const T& _var) const noexcept {
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) { if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
fbb_->String(_name.data(), _var); fbb_->String(_name.data(), _var);
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) { } else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
fbb_->Bool(_name.data(), _var); fbb_->Bool(_name.data(), _var);
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>()) { } else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>()) {
fbb_->Double(_name.data(), _var); fbb_->Double(_name.data(), _var);
} else if constexpr (std::is_integral<std::remove_cvref_t<T>>()) { } else if constexpr (std::is_integral<std::remove_cvref_t<T>>()) {
fbb_->Int(_name.data(), _var); fbb_->Int(_name.data(), _var);
} else { } else {
static_assert(always_false_v<T>, "Unsupported type"); static_assert(always_false_v<T>, "Unsupported type");
} }
return OutputVarType{}; return OutputVarType {};
} }
template <class T> template <class T>
OutputVarType insert_value(const T& _var) const noexcept { OutputVarType insert_value(const T& _var) const noexcept {
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) { if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
fbb_->String(_var); fbb_->String(_var);
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) { } else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
fbb_->Bool(_var); fbb_->Bool(_var);
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>()) { } else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>()) {
fbb_->Double(_var); fbb_->Double(_var);
} else if constexpr (std::is_integral<std::remove_cvref_t<T>>()) { } else if constexpr (std::is_integral<std::remove_cvref_t<T>>()) {
fbb_->Int(_var); fbb_->Int(_var);
} else { } else {
static_assert(always_false_v<T>, "Unsupported type"); static_assert(always_false_v<T>, "Unsupported type");
} }
return OutputVarType{}; return OutputVarType {};
} }
OutputArrayType new_array(const std::string_view& _name) const noexcept { OutputArrayType new_array(const std::string_view& _name) const noexcept {
const auto start = fbb_->StartVector(_name.data()); const auto start = fbb_->StartVector(_name.data());
return OutputArrayType{start}; return OutputArrayType {start};
} }
OutputArrayType new_array() const noexcept { OutputArrayType new_array() const noexcept {
const auto start = fbb_->StartVector(); const auto start = fbb_->StartVector();
return OutputArrayType{start}; return OutputArrayType {start};
} }
OutputObjectType new_object(const std::string_view& _name) const noexcept { OutputObjectType new_object(
const auto start = fbb_->StartMap(_name.data()); const std::string_view& _name) const noexcept {
return OutputObjectType{start}; const auto start = fbb_->StartMap(_name.data());
} return OutputObjectType {start};
}
OutputObjectType new_object() const noexcept { OutputObjectType new_object() const noexcept {
const auto start = fbb_->StartMap(); const auto start = fbb_->StartMap();
return OutputObjectType{start}; return OutputObjectType {start};
} }
private: private:
Ref<flexbuffers::Builder> fbb_; Ref<flexbuffers::Builder> fbb_;
}; };
} // namespace flexbuf } // namespace flexbuf
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -6,17 +6,17 @@
#include "read.hpp" #include "read.hpp"
namespace rfl { namespace rfl {
namespace flexbuf { namespace flexbuf {
template <class T, class... Ps> template <class T, class... Ps>
Result<T> load(const std::string& _fname) { Result<T> load(const std::string& _fname) {
const auto read_bytes = [](const auto& _bytes) { const auto read_bytes = [](const auto& _bytes) {
return read<T, Ps...>(_bytes); return read<T, Ps...>(_bytes);
}; };
return rfl::io::load_bytes(_fname).and_then(read_bytes); return rfl::io::load_bytes(_fname).and_then(read_bytes);
} }
} // namespace flexbuf } // namespace flexbuf
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -2,7 +2,6 @@
#define FLEXBUF_READ_HPP_ #define FLEXBUF_READ_HPP_
#include <flatbuffers/flexbuffers.h> #include <flatbuffers/flexbuffers.h>
#include <istream> #include <istream>
#include <vector> #include <vector>
@ -11,40 +10,40 @@
#include "Parser.hpp" #include "Parser.hpp"
namespace rfl { namespace rfl {
namespace flexbuf { namespace flexbuf {
using InputVarType = typename Reader::InputVarType; using InputVarType = typename Reader::InputVarType;
/// Parses an object from flexbuf var. /// Parses an object from flexbuf var.
template <class T, class... Ps> template <class T, class... Ps>
auto read(const InputVarType& _obj) { auto read(const InputVarType& _obj) {
const auto r = Reader(); const auto r = Reader();
return Parser<T, Processors<Ps...>>::read(r, _obj); return Parser<T, Processors<Ps...>>::read(r, _obj);
} }
/// Parses an object from flexbuf using reflection. /// Parses an object from flexbuf using reflection.
template <class T, class... Ps> template <class T, class... Ps>
auto read(const char* _bytes, const size_t _size) { auto read(const char* _bytes, const size_t _size) {
const InputVarType root = const InputVarType root =
flexbuffers::GetRoot(reinterpret_cast<const uint8_t*>(_bytes), _size); flexbuffers::GetRoot(reinterpret_cast<const uint8_t*>(_bytes), _size);
return read<T, Ps...>(root); return read<T, Ps...>(root);
} }
/// Parses an object from flexbuf using reflection. /// Parses an object from flexbuf using reflection.
template <class T, class... Ps> template <class T, class... Ps>
auto read(const std::vector<char>& _bytes) { auto read(const std::vector<char>& _bytes) {
return read<T, Ps...>(_bytes.data(), _bytes.size()); return read<T, Ps...>(_bytes.data(), _bytes.size());
} }
/// Parses an object directly from a stream. /// Parses an object directly from a stream.
template <class T, class... Ps> template <class T, class... Ps>
auto read(std::istream& _stream) { auto read(std::istream& _stream) {
std::istreambuf_iterator<char> begin(_stream), end; std::istreambuf_iterator<char> begin(_stream), end;
const auto bytes = std::vector<char>(begin, end); const auto bytes = std::vector<char>(begin, end);
return read<T, Ps...>(bytes.data(), bytes.size()); return read<T, Ps...>(bytes.data(), bytes.size());
} }
} // namespace flexbuf } // namespace flexbuf
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -10,17 +10,17 @@
#include "write.hpp" #include "write.hpp"
namespace rfl { namespace rfl {
namespace flexbuf { namespace flexbuf {
template <class... Ps> template <class... Ps>
Result<Nothing> save(const std::string& _fname, const auto& _obj) { Result<Nothing> save(const std::string& _fname, const auto& _obj) {
const auto write_func = [](const auto& _obj, auto& _stream) -> auto& { const auto write_func = [](const auto& _obj, auto& _stream) -> auto& {
return write<Ps...>(_obj, _stream); return write<Ps...>(_obj, _stream);
}; };
return rfl::io::save_bytes(_fname, _obj, write_func); return rfl::io::save_bytes(_fname, _obj, write_func);
} }
} // namespace flexbuf } // namespace flexbuf
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -1,9 +1,8 @@
#ifndef FLEXBUF_WRITE_HPP_ #ifndef FLEXBUF_WRITE_HPP_
#define FLEXBUF_WRITE_HPP_ #define FLEXBUF_WRITE_HPP_
#include <flatbuffers/flexbuffers.h>
#include <cstddef> #include <cstddef>
#include <flatbuffers/flexbuffers.h>
#include <ostream> #include <ostream>
#include <sstream> #include <sstream>
#include <vector> #include <vector>
@ -14,37 +13,38 @@
#include "Parser.hpp" #include "Parser.hpp"
namespace rfl { namespace rfl {
namespace flexbuf { namespace flexbuf {
template <class... Ps> template <class... Ps>
std::vector<uint8_t> to_buffer(const auto& _obj) { std::vector<uint8_t> to_buffer(const auto& _obj) {
using T = std::remove_cvref_t<decltype(_obj)>; using T = std::remove_cvref_t<decltype(_obj)>;
using ParentType = parsing::Parent<Writer>; using ParentType = parsing::Parent<Writer>;
const auto fbb = Ref<flexbuffers::Builder>::make(); const auto fbb = Ref<flexbuffers::Builder>::make();
auto w = Writer(fbb); auto w = Writer(fbb);
Parser<T, Processors<Ps...>>::write(w, _obj, typename ParentType::Root{}); Parser<T, Processors<Ps...>>::write(w, _obj,
fbb->Finish(); typename ParentType::Root {});
return fbb->GetBuffer(); fbb->Finish();
} return fbb->GetBuffer();
}
/// Writes an object to flexbuf. /// Writes an object to flexbuf.
template <class... Ps> template <class... Ps>
std::vector<char> write(const auto& _obj) { std::vector<char> write(const auto& _obj) {
const auto buffer = to_buffer<Ps...>(_obj); const auto buffer = to_buffer<Ps...>(_obj);
const auto data = reinterpret_cast<const char*>(buffer.data()); const auto data = reinterpret_cast<const char*>(buffer.data());
return std::vector<char>(data, data + buffer.size()); return std::vector<char>(data, data + buffer.size());
} }
/// Writes an object to an ostream. /// Writes an object to an ostream.
template <class... Ps> template <class... Ps>
std::ostream& write(const auto& _obj, std::ostream& _stream) { std::ostream& write(const auto& _obj, std::ostream& _stream) {
const auto buffer = to_buffer<Ps...>(_obj); const auto buffer = to_buffer<Ps...>(_obj);
const auto data = reinterpret_cast<const char*>(buffer.data()); const auto data = reinterpret_cast<const char*>(buffer.data());
_stream.write(data, buffer.size()); _stream.write(data, buffer.size());
return _stream; return _stream;
} }
} // namespace flexbuf } // namespace flexbuf
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -12,42 +12,43 @@
namespace rfl { namespace rfl {
/// Generates the struct T from a named tuple. /// Generates the struct T from a named tuple.
template <class T, class NamedTupleType> template <class T, class NamedTupleType>
auto from_named_tuple(NamedTupleType&& _n) { auto from_named_tuple(NamedTupleType&& _n) {
using RequiredType = std::remove_cvref_t<rfl::named_tuple_t<T>>; using RequiredType = std::remove_cvref_t<rfl::named_tuple_t<T>>;
if constexpr (!std::is_same<std::remove_cvref_t<NamedTupleType>, if constexpr (!std::is_same<std::remove_cvref_t<NamedTupleType>,
RequiredType>()) { RequiredType>()) {
return from_named_tuple<T>(RequiredType(std::forward<NamedTupleType>(_n))); return from_named_tuple<T>(
} else if constexpr (internal::has_fields<T>()) { RequiredType(std::forward<NamedTupleType>(_n)));
if constexpr (std::is_lvalue_reference<NamedTupleType>{}) { } 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); return internal::copy_from_named_tuple<T>(_n);
} else { } 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()); 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. } // namespace rfl
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 #endif

View file

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

View file

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

View file

@ -8,13 +8,13 @@
#include "copy_to_field_tuple.hpp" #include "copy_to_field_tuple.hpp"
namespace rfl { namespace rfl {
namespace internal { namespace internal {
template <class T> template <class T>
using field_tuple_t = using field_tuple_t =
typename std::invoke_result<decltype(copy_to_field_tuple<T>), T>::type; typename std::invoke_result<decltype(copy_to_field_tuple<T>), T>::type;
} }
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -8,16 +8,16 @@
#include <unordered_map> #include <unordered_map>
namespace rfl { namespace rfl {
namespace internal { namespace internal {
template <int N> template <int N>
struct Fields { struct Fields {
std::array<std::string, N> names_; std::array<std::string, N> names_;
std::unordered_map<std::string_view, std::int16_t> indices_; std::unordered_map<std::string_view, std::int16_t> indices_;
}; };
} // namespace internal } // namespace internal
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -9,148 +9,148 @@
namespace rfl::internal { namespace rfl::internal {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
template <class NamedTupleType> template <class NamedTupleType>
struct Getter; struct Getter;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/// Default case - anything that cannot be explicitly matched. /// Default case - anything that cannot be explicitly matched.
template <class NamedTupleType> template <class NamedTupleType>
struct Getter { struct Getter {
public: public:
/// Retrieves the indicated value from the tuple. /// Retrieves the indicated value from the tuple.
template <int _index> template <int _index>
static inline auto& get(NamedTupleType& _tup) { static inline auto& get(NamedTupleType& _tup) {
return std::get<_index>(_tup.values()); return std::get<_index>(_tup.values());
} }
/// Gets a field by name. /// Gets a field by name.
template <StringLiteral _field_name> template <StringLiteral _field_name>
static inline auto& get(NamedTupleType& _tup) { static inline auto& get(NamedTupleType& _tup) {
constexpr auto index = constexpr auto index =
find_index<_field_name, typename NamedTupleType::Fields>(); find_index<_field_name, typename NamedTupleType::Fields>();
return Getter<NamedTupleType>::template get<index>(_tup); return Getter<NamedTupleType>::template get<index>(_tup);
} }
/// Gets a field by the field type. /// Gets a field by the field type.
template <class Field> template <class Field>
static inline auto& get(NamedTupleType& _tup) { static inline auto& get(NamedTupleType& _tup) {
constexpr auto index = constexpr auto index =
find_index<Field::name_, typename NamedTupleType::Fields>(); find_index<Field::name_, typename NamedTupleType::Fields>();
static_assert( static_assert(
std::is_same<typename std::tuple_element< std::is_same<typename std::tuple_element<
index, typename NamedTupleType::Fields>::type::Type, index, typename NamedTupleType::Fields>::type::Type,
typename Field::Type>(), typename Field::Type>(),
"If two fields have the same name, " "If two fields have the same name, "
"their type must be the same as " "their type must be the same as "
"well."); "well.");
return Getter<NamedTupleType>::template get<index>(_tup); return Getter<NamedTupleType>::template get<index>(_tup);
} }
/// Retrieves the indicated value from the tuple. /// Retrieves the indicated value from the tuple.
template <int _index> template <int _index>
static inline const auto& get_const(const NamedTupleType& _tup) { static inline const auto& get_const(const NamedTupleType& _tup) {
return std::get<_index>(_tup.values()); return std::get<_index>(_tup.values());
} }
/// Gets a field by name. /// Gets a field by name.
template <StringLiteral _field_name> template <StringLiteral _field_name>
static inline const auto& get_const(const NamedTupleType& _tup) { static inline const auto& get_const(const NamedTupleType& _tup) {
constexpr auto index = constexpr auto index =
find_index<_field_name, typename NamedTupleType::Fields>(); find_index<_field_name, typename NamedTupleType::Fields>();
return Getter<NamedTupleType>::template get_const<index>(_tup); return Getter<NamedTupleType>::template get_const<index>(_tup);
} }
/// Gets a field by the field type. /// Gets a field by the field type.
template <class Field> template <class Field>
static inline const auto& get_const(const NamedTupleType& _tup) { static inline const auto& get_const(const NamedTupleType& _tup) {
constexpr auto index = constexpr auto index =
find_index<Field::name_, typename NamedTupleType::Fields>(); find_index<Field::name_, typename NamedTupleType::Fields>();
static_assert( static_assert(
std::is_same<typename std::tuple_element< std::is_same<typename std::tuple_element<
index, typename NamedTupleType::Fields>::type::Type, index, typename NamedTupleType::Fields>::type::Type,
typename Field::Type>(), typename Field::Type>(),
"If two fields have the same name, " "If two fields have the same name, "
"their type must be the same as " "their type must be the same as "
"well."); "well.");
return Getter<NamedTupleType>::template get_const<index>(_tup); return Getter<NamedTupleType>::template get_const<index>(_tup);
} }
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/// For handling std::variant. /// For handling std::variant.
template <class... NamedTupleTypes> template <class... NamedTupleTypes>
struct Getter<std::variant<NamedTupleTypes...>> { struct Getter<std::variant<NamedTupleTypes...>> {
public: public:
/// Retrieves the indicated value from the tuple. /// Retrieves the indicated value from the tuple.
template <int _index> template <int _index>
static inline auto& get(std::variant<NamedTupleTypes...>& _tup) { static inline auto& get(std::variant<NamedTupleTypes...>& _tup) {
const auto apply = [](auto& _t) -> auto& { const auto apply = [](auto& _t) -> auto& {
using NamedTupleType = std::remove_cvref_t<decltype(_t)>; using NamedTupleType = std::remove_cvref_t<decltype(_t)>;
return Getter<NamedTupleType>::template get<_index>(_t); return Getter<NamedTupleType>::template get<_index>(_t);
}; };
return std::visit(apply, _tup); return std::visit(apply, _tup);
} }
/// Gets a field by name. /// Gets a field by name.
template <StringLiteral _field_name> template <StringLiteral _field_name>
static inline auto& get(std::variant<NamedTupleTypes...>& _tup) { static inline auto& get(std::variant<NamedTupleTypes...>& _tup) {
const auto apply = [](auto& _t) -> auto& { const auto apply = [](auto& _t) -> auto& {
using NamedTupleType = std::remove_cvref_t<decltype(_t)>; using NamedTupleType = std::remove_cvref_t<decltype(_t)>;
return Getter<NamedTupleType>::template get<_field_name>(_t); return Getter<NamedTupleType>::template get<_field_name>(_t);
}; };
return std::visit(apply, _tup); return std::visit(apply, _tup);
} }
/// Gets a field by the field type. /// Gets a field by the field type.
template <class Field> template <class Field>
static inline auto& get(std::variant<NamedTupleTypes...>& _tup) { static inline auto& get(std::variant<NamedTupleTypes...>& _tup) {
const auto apply = [](auto& _t) -> auto& { const auto apply = [](auto& _t) -> auto& {
using NamedTupleType = std::remove_cvref_t<decltype(_t)>; using NamedTupleType = std::remove_cvref_t<decltype(_t)>;
return Getter<NamedTupleType>::template get<Field>(_t); return Getter<NamedTupleType>::template get<Field>(_t);
}; };
return std::visit(apply, _tup); return std::visit(apply, _tup);
} }
/// Retrieves the indicated value from the tuple. /// Retrieves the indicated value from the tuple.
template <int _index> template <int _index>
static inline const auto& get_const( static inline const auto& get_const(
const std::variant<NamedTupleTypes...>& _tup) { const std::variant<NamedTupleTypes...>& _tup) {
const auto apply = [](const auto& _tup) -> const auto& { const auto apply = [](const auto& _tup) -> const auto& {
using NamedTupleType = std::remove_cvref_t<decltype(_tup)>; using NamedTupleType = std::remove_cvref_t<decltype(_tup)>;
return Getter<NamedTupleType>::template get_const<_index>(_tup); return Getter<NamedTupleType>::template get_const<_index>(_tup);
}; };
return std::visit(apply, _tup); return std::visit(apply, _tup);
} }
/// Gets a field by name. /// Gets a field by name.
template <StringLiteral _field_name> template <StringLiteral _field_name>
static inline const auto& get_const( static inline const auto& get_const(
const std::variant<NamedTupleTypes...>& _tup) { const std::variant<NamedTupleTypes...>& _tup) {
const auto apply = [](const auto& _t) -> const auto& { const auto apply = [](const auto& _t) -> const auto& {
using NamedTupleType = std::remove_cvref_t<decltype(_t)>; using NamedTupleType = std::remove_cvref_t<decltype(_t)>;
return Getter<NamedTupleType>::template get_const<_field_name>(_t); return Getter<NamedTupleType>::template get_const<_field_name>(_t);
}; };
return std::visit(apply, _tup); return std::visit(apply, _tup);
} }
/// Gets a field by the field type. /// Gets a field by the field type.
template <class Field> template <class Field>
static inline const auto& get_const( static inline const auto& get_const(
const std::variant<NamedTupleTypes...>& _tup) { const std::variant<NamedTupleTypes...>& _tup) {
const auto apply = [](const auto& _t) -> const auto& { const auto apply = [](const auto& _t) -> const auto& {
using NamedTupleType = std::remove_cvref_t<decltype(_t)>; using NamedTupleType = std::remove_cvref_t<decltype(_t)>;
return Getter<NamedTupleType>::template get_const<Field>(_t); return Getter<NamedTupleType>::template get_const<Field>(_t);
}; };
return std::visit(apply, _tup); return std::visit(apply, _tup);
} }
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
} // namespace rfl::internal } // namespace rfl::internal
#endif #endif

View file

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

View file

@ -5,49 +5,45 @@
#include <mutex> #include <mutex>
namespace rfl { namespace rfl {
namespace internal { namespace internal {
/// For a thread-safe memoization pattern. /// For a thread-safe memoization pattern.
template <class T> template <class T>
class Memoization { class Memoization {
public: public:
Memoization() { flag_.clear(); } Memoization() { flag_.clear(); }
~Memoization() = default; ~Memoization() = default;
public: public:
/// Returns the underlying value. /// Returns the underlying value.
template <class F> template <class F>
const T& value(const F& _f) { const T& value(const F& _f) {
if (flag_.test()) { if (flag_.test()) { return value_; }
return value_;
}
std::lock_guard<std::mutex> guard(mtx_); std::lock_guard<std::mutex> guard(mtx_);
if (flag_.test()) { if (flag_.test()) { return value_; }
return value_;
}
_f(&value_); _f(&value_);
flag_.test_and_set(); flag_.test_and_set();
return value_; return value_;
} }
private: private:
/// Signifies whether t_ has been set. /// Signifies whether t_ has been set.
std::atomic_flag flag_; std::atomic_flag flag_;
/// A mutex, only needed for writing. /// A mutex, only needed for writing.
std::mutex mtx_; std::mutex mtx_;
/// The type to be initialized. /// The type to be initialized.
T value_; T value_;
}; };
} // namespace internal } // namespace internal
} // namespace rfl } // namespace rfl
#endif #endif

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -6,31 +6,31 @@
#include <type_traits> #include <type_traits>
#include "../Field.hpp" #include "../Field.hpp"
#include "lit_name.hpp"
#include "../make_named_tuple.hpp" #include "../make_named_tuple.hpp"
#include "lit_name.hpp"
namespace rfl { namespace rfl {
namespace internal { namespace internal {
template <class FieldNames, class... Fields> template <class FieldNames, class... Fields>
auto copy_flattened_tuple_to_named_tuple(const auto& _flattened_tuple, auto copy_flattened_tuple_to_named_tuple(const auto& _flattened_tuple,
Fields&&... _fields) { Fields&&... _fields) {
constexpr auto size = constexpr auto size =
std::tuple_size_v<std::remove_cvref_t<decltype(_flattened_tuple)>>; std::tuple_size_v<std::remove_cvref_t<decltype(_flattened_tuple)>>;
constexpr auto i = sizeof...(_fields); constexpr auto i = sizeof...(_fields);
if constexpr (i == size) { if constexpr (i == size) {
return make_named_tuple(std::move(_fields)...); return make_named_tuple(std::move(_fields)...);
} else { } else {
const auto name_literal = FieldNames::template name_of<i>(); const auto name_literal = FieldNames::template name_of<i>();
auto new_field = rfl::make_field< auto new_field = rfl::make_field<
lit_name_v<std::remove_cvref_t<decltype(name_literal)>>>( lit_name_v<std::remove_cvref_t<decltype(name_literal)>>>(
std::get<i>(_flattened_tuple)); std::get<i>(_flattened_tuple));
return copy_flattened_tuple_to_named_tuple<FieldNames>( return copy_flattened_tuple_to_named_tuple<FieldNames>(
_flattened_tuple, std::move(_fields)..., std::move(new_field)); _flattened_tuple, std::move(_fields)..., std::move(new_field));
} }
} }
} // namespace internal } // namespace internal
} // namespace rfl } // namespace rfl
#endif #endif

View file

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

View file

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

View file

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

View file

@ -4,26 +4,30 @@
#include "../Literal.hpp" #include "../Literal.hpp"
namespace rfl { namespace rfl {
namespace internal { namespace internal {
/// Allows you to combine several literals. /// Allows you to combine several literals.
template <class... LiteralTypes> template <class... LiteralTypes>
struct define_literal; struct define_literal;
/// General case /// General case
template <StringLiteral... _content1, StringLiteral... _content2, class... Tail> template <StringLiteral... _content1,
struct define_literal<Literal<_content1...>, Literal<_content2...>, Tail...> { StringLiteral... _content2,
using type = typename define_literal<Literal<_content1..., _content2...>, class... Tail>
Tail...>::type; 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 /// Special case - only a single literal is left
template <StringLiteral... _content> template <StringLiteral... _content>
struct define_literal<Literal<_content...>> { struct define_literal<Literal<_content...>> {
using type = Literal<_content...>; using type = Literal<_content...>;
}; };
} // namespace internal } // namespace internal
} // namespace rfl } // namespace rfl
#endif #endif

View file

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

View file

@ -5,53 +5,64 @@
#include "StringLiteral.hpp" #include "StringLiteral.hpp"
namespace rfl { namespace rfl {
namespace internal { namespace internal {
/// Allows you to combine several tagged unions. /// Allows you to combine several tagged unions.
template <StringLiteral _discriminator, class... TaggedUnionTypes> template <StringLiteral _discriminator, class... TaggedUnionTypes>
struct define_tagged_union; struct define_tagged_union;
/// Recursive case - both tagged union. /// Recursive case - both tagged union.
template <StringLiteral _discriminator, class... NamedTupleTypes1, template <StringLiteral _discriminator,
class... NamedTupleTypes2, class... Tail> class... NamedTupleTypes1,
struct define_tagged_union< class... NamedTupleTypes2,
_discriminator, TaggedUnion<_discriminator, NamedTupleTypes1...>, class... Tail>
TaggedUnion<_discriminator, NamedTupleTypes2...>, Tail...> { struct define_tagged_union<_discriminator,
using type = typename define_tagged_union< 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, _discriminator,
TaggedUnion<_discriminator, NamedTupleTypes1..., NamedTupleTypes2...>, TaggedUnion<_discriminator, NamedTupleTypes...>> {
Tail...>::type; using type = TaggedUnion<_discriminator, NamedTupleTypes...>;
}; };
/// Recursive case - tagged union plus named tuple. } // namespace internal
template <StringLiteral _discriminator, class... NamedTupleTypes, } // namespace rfl
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 #endif

View file

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

View file

@ -14,63 +14,82 @@
#include "../StringLiteral.hpp" #include "../StringLiteral.hpp"
namespace rfl { namespace rfl {
namespace internal { namespace internal {
namespace enums { namespace enums {
template <class EnumType, class LiteralType, size_t N, auto... _enums> template <class EnumType, class LiteralType, size_t N, auto... _enums>
struct Names { struct Names {
/// Contains a collection of enums as compile-time strings. /// Contains a collection of enums as compile-time strings.
using Literal = LiteralType; using Literal = LiteralType;
/// The number of possible values /// The number of possible values
constexpr static size_t size = N; constexpr static size_t size = N;
/// A list of all the possible enums /// A list of all the possible enums
constexpr static std::array<EnumType, N> enums_ = constexpr static std::array<EnumType, N> enums_ =
std::array<EnumType, N>{_enums...}; std::array<EnumType, N> {_enums...};
static_assert(N == 0 || LiteralType::size() == N, static_assert(N == 0 || LiteralType::size() == N,
"Size of literal and enum do not match."); "Size of literal and enum do not match.");
template <class NewLiteral, auto _new_enum> template <class NewLiteral, auto _new_enum>
using AddOneType = std::conditional_t< using AddOneType =
N == 0, Names<EnumType, NewLiteral, 1, _new_enum>, std::conditional_t<N == 0,
Names<EnumType, define_literal_t<LiteralType, NewLiteral>, N + 1, Names<EnumType, NewLiteral, 1, _new_enum>,
_enums..., _new_enum>>; Names<EnumType,
}; define_literal_t<LiteralType, NewLiteral>,
N + 1,
_enums...,
_new_enum>>;
};
template <class EnumType, size_t N, StringLiteral... _names, auto... _enums> template <class EnumType,
auto names_to_enumerator_named_tuple( size_t N,
Names<EnumType, Literal<_names...>, N, _enums...>) { StringLiteral... _names,
return make_named_tuple(Field<_names, EnumType>{_enums}...); auto... _enums>
} auto names_to_enumerator_named_tuple(
Names<EnumType, Literal<_names...>, N, _enums...>) {
return make_named_tuple(Field<_names, EnumType> {_enums}...);
}
template <class EnumType, size_t N, StringLiteral... _names, auto... _enums> template <class EnumType,
auto names_to_underlying_enumerator_named_tuple( size_t N,
Names<EnumType, Literal<_names...>, N, _enums...>) { StringLiteral... _names,
return make_named_tuple(Field<_names, std::underlying_type_t<EnumType>>{ auto... _enums>
static_cast<std::underlying_type_t<EnumType>>(_enums)}...); auto names_to_underlying_enumerator_named_tuple(
} Names<EnumType, Literal<_names...>, N, _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, StringLiteral... _names, auto... _enums> template <class EnumType,
constexpr std::array<std::pair<std::string_view, EnumType>, N> size_t N,
names_to_enumerator_array(Names<EnumType, Literal<_names...>, N, _enums...>) { StringLiteral... _names,
return { auto... _enums>
std::make_pair(LiteralHelper<_names>::field_.string_view(), _enums)...}; constexpr std::array<std::pair<std::string_view, EnumType>, N>
} names_to_enumerator_array(
Names<EnumType, Literal<_names...>, N, _enums...>) {
return {std::make_pair(LiteralHelper<_names>::field_.string_view(),
_enums)...};
}
template <class EnumType, size_t N, StringLiteral... _names, auto... _enums> template <class EnumType,
constexpr std::array< size_t N,
std::pair<std::string_view, std::underlying_type_t<EnumType>>, N> StringLiteral... _names,
names_to_underlying_enumerator_array( auto... _enums>
Names<EnumType, Literal<_names...>, N, _enums...>) { constexpr std::array<
return { std::pair<std::string_view, std::underlying_type_t<EnumType>>,
std::make_pair(LiteralHelper<_names>::field_.string_view(), N>
static_cast<std::underlying_type_t<EnumType>>(_enums))...}; names_to_underlying_enumerator_array(
} Names<EnumType, Literal<_names...>, N, _enums...>) {
return {std::make_pair(
LiteralHelper<_names>::field_.string_view(),
static_cast<std::underlying_type_t<EnumType>>(_enums))...};
}
} // namespace enums } // namespace enums
} // namespace internal } // namespace internal
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -15,118 +15,121 @@
#include "is_flag_enum.hpp" #include "is_flag_enum.hpp"
namespace rfl { namespace rfl {
namespace internal { namespace internal {
namespace enums { namespace enums {
template <class EnumType> template <class EnumType>
class StringConverter { class StringConverter {
public: public:
static constexpr bool is_flag_enum_ = is_flag_enum<EnumType>; static constexpr bool is_flag_enum_ = is_flag_enum<EnumType>;
static constexpr auto names_ = get_enum_names<EnumType, is_flag_enum_>(); static constexpr auto names_ =
get_enum_names<EnumType, is_flag_enum_>();
using NamesLiteral = typename decltype(names_)::Literal; using NamesLiteral = typename decltype(names_)::Literal;
public: public:
/// Transform an enum to a matching string. /// Transform an enum to a matching string.
static std::string enum_to_string(const EnumType _enum) { static std::string enum_to_string(const EnumType _enum) {
if constexpr (is_flag_enum_) { if constexpr (is_flag_enum_) {
return flag_enum_to_string(_enum); return flag_enum_to_string(_enum);
} else { } else {
return enum_to_single_string(_enum); return enum_to_single_string(_enum);
} }
} }
/// Transforms a string to the matching enum. /// Transforms a string to the matching enum.
static Result<EnumType> string_to_enum(const std::string& _str) { static Result<EnumType> string_to_enum(const std::string& _str) {
static_assert(names_.size != 0, static_assert(
"No enum could be identified. Please choose enum values " names_.size != 0,
"between 0 to 127 or for flag enums choose 1,2,4,8,16,..."); "No enum could be identified. Please choose enum values "
if constexpr (is_flag_enum_) { "between 0 to 127 or for flag enums choose 1,2,4,8,16,...");
return string_to_flag_enum(_str); if constexpr (is_flag_enum_) {
} else { return string_to_flag_enum(_str);
return single_string_to_enum(_str); } else {
} return single_string_to_enum(_str);
} }
}
private: private:
/// Iterates through the enum bit by bit and matches it against the flags. /// Iterates through the enum bit by bit and matches it against the
static std::string flag_enum_to_string(const EnumType _e) { /// flags.
using T = std::underlying_type_t<EnumType>; static std::string flag_enum_to_string(const EnumType _e) {
auto val = static_cast<T>(_e); using T = std::underlying_type_t<EnumType>;
int i = 0; auto val = static_cast<T>(_e);
std::vector<std::string> flags; int i = 0;
while (val != 0) { std::vector<std::string> flags;
const auto bit = val & static_cast<T>(1); while (val != 0) {
if (bit == 1) { const auto bit = val & static_cast<T>(1);
auto str = enum_to_single_string( if (bit == 1) {
static_cast<EnumType>(static_cast<T>(1) << i)); auto str = enum_to_single_string(
flags.emplace_back(std::move(str)); static_cast<EnumType>(static_cast<T>(1) << i));
} flags.emplace_back(std::move(str));
++i; }
val >>= 1; ++i;
} val >>= 1;
return strings::join("|", flags); }
} return strings::join("|", flags);
}
/// This assumes that _enum can be exactly matched to one of the names and /// This assumes that _enum can be exactly matched to one of the names
/// does not have to be combined using |. /// and does not have to be combined using |.
static std::string enum_to_single_string(const EnumType _enum) { static std::string enum_to_single_string(const EnumType _enum) {
const auto to_str = [](const auto _l) { return _l.str(); }; const auto to_str = [](const auto _l) { return _l.str(); };
for (size_t i = 0; i < names_.size; ++i) { for (size_t i = 0; i < names_.size; ++i) {
if (names_.enums_[i] == _enum) { if (names_.enums_[i] == _enum) {
return NamesLiteral::from_value( return NamesLiteral::from_value(
static_cast<typename NamesLiteral::ValueType>(i)) static_cast<typename NamesLiteral::ValueType>(i))
.transform(to_str) .transform(to_str)
.value(); .value();
} }
} }
return std::to_string(static_cast<std::underlying_type_t<EnumType>>(_enum)); return std::to_string(
} static_cast<std::underlying_type_t<EnumType>>(_enum));
}
/// Finds the enum matching the literal. /// Finds the enum matching the literal.
static EnumType literal_to_enum(const NamesLiteral _lit) noexcept { static EnumType literal_to_enum(const NamesLiteral _lit) noexcept {
return names_.enums_[_lit.value()]; return names_.enums_[_lit.value()];
} }
/// This assumes that _enum can be exactly matched to one of the names and /// This assumes that _enum can be exactly matched to one of the names
/// does not have to be combined using |. /// and does not have to be combined using |.
static Result<EnumType> single_string_to_enum(const std::string& _str) { static Result<EnumType> single_string_to_enum(const std::string& _str) {
const auto r = NamesLiteral::from_string(_str).transform(literal_to_enum); const auto r =
if (r) { NamesLiteral::from_string(_str).transform(literal_to_enum);
return r; if (r) {
} else { return r;
try { } else {
return static_cast<EnumType>(std::stoi(_str)); try {
} catch (std::exception& exp) { return static_cast<EnumType>(std::stoi(_str));
return Error(exp.what()); } catch (std::exception& exp) { return Error(exp.what()); }
} }
} }
}
/// Only relevant if this is a flag enum - combines the different matches /// Only relevant if this is a flag enum - combines the different
/// using |. /// matches using |.
static Result<EnumType> string_to_flag_enum( static Result<EnumType> string_to_flag_enum(
const std::string& _str) noexcept { const std::string& _str) noexcept {
using T = std::underlying_type_t<EnumType>; using T = std::underlying_type_t<EnumType>;
const auto split = strings::split(_str, "|"); const auto split = strings::split(_str, "|");
auto res = static_cast<T>(0); auto res = static_cast<T>(0);
for (const auto& s : split) { for (const auto& s : split) {
const auto r = single_string_to_enum(s); const auto r = single_string_to_enum(s);
if (r) { if (r) {
res |= static_cast<T>(*r); res |= static_cast<T>(*r);
} else { } else {
return r; return r;
} }
} }
return static_cast<EnumType>(res); return static_cast<EnumType>(res);
} }
}; };
} // namespace enums } // namespace enums
} // namespace internal } // namespace internal
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -36,116 +36,127 @@
// defined. // defined.
namespace rfl { namespace rfl {
namespace internal { namespace internal {
namespace enums { namespace enums {
template <auto e> template <auto e>
consteval auto get_enum_name_str_view() { consteval auto get_enum_name_str_view() {
// Unfortunately, we cannot avoid the use of a compiler-specific macro for // Unfortunately, we cannot avoid the use of a compiler-specific macro
// Clang on Windows. For all other compilers, function_name works as intended. // for Clang on Windows. For all other compilers, function_name works as
// intended.
#if defined(__clang__) && defined(_MSC_VER) #if defined(__clang__) && defined(_MSC_VER)
const auto func_name = std::string_view{__PRETTY_FUNCTION__}; const auto func_name = std::string_view {__PRETTY_FUNCTION__};
#else #else
const auto func_name = const auto func_name =
std::string_view{std::source_location::current().function_name()}; std::string_view {std::source_location::current().function_name()};
#endif #endif
#if defined(__clang__) #if defined(__clang__)
const auto split = func_name.substr(0, func_name.size() - 1); const auto split = func_name.substr(0, func_name.size() - 1);
return split.substr(split.find("e = ") + 4); return split.substr(split.find("e = ") + 4);
#elif defined(__GNUC__) #elif defined(__GNUC__)
const auto split = func_name.substr(0, func_name.size() - 1); const auto split = func_name.substr(0, func_name.size() - 1);
return split.substr(split.find("e = ") + 4); return split.substr(split.find("e = ") + 4);
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
const auto split = func_name.substr(0, func_name.size() - 7); const auto split = func_name.substr(0, func_name.size() - 7);
return split.substr(split.find("get_enum_name_str_view<") + 23); return split.substr(split.find("get_enum_name_str_view<") + 23);
#else #else
static_assert(false, static_assert(
"You are using an unsupported compiler. Please use GCC, Clang " false,
"or MSVC or use rfl::Literal."); "You are using an unsupported compiler. Please use GCC, Clang "
"or MSVC or use rfl::Literal.");
#endif #endif
} }
template <auto e> template <auto e>
consteval auto get_enum_name() { consteval auto get_enum_name() {
constexpr auto name = get_enum_name_str_view<e>(); constexpr auto name = get_enum_name_str_view<e>();
const auto to_str_lit = [&]<auto... Ns>(std::index_sequence<Ns...>) { const auto to_str_lit = [&]<auto... Ns>(std::index_sequence<Ns...>) {
return StringLiteral<sizeof...(Ns) + 1>{name[Ns]...}; return StringLiteral<sizeof...(Ns) + 1> {name[Ns]...};
}; };
return to_str_lit(std::make_index_sequence<name.size()>{}); return to_str_lit(std::make_index_sequence<name.size()> {});
} }
template <class T> template <class T>
consteval T calc_greatest_power_of_two() { consteval T calc_greatest_power_of_two() {
if constexpr (std::is_signed_v<T>) { if constexpr (std::is_signed_v<T>) {
return static_cast<T>(1) << (sizeof(T) * 8 - 2); return static_cast<T>(1) << (sizeof(T) * 8 - 2);
} else { } else {
return static_cast<T>(1) << (sizeof(T) * 8 - 1); return static_cast<T>(1) << (sizeof(T) * 8 - 1);
} }
} }
template <class T, bool _is_flag> template <class T, bool _is_flag>
consteval T get_max() { consteval T get_max() {
if constexpr (_is_flag) { if constexpr (_is_flag) {
return calc_greatest_power_of_two<T>(); return calc_greatest_power_of_two<T>();
} else { } else {
return std::numeric_limits<T>::max() > 127 ? static_cast<T>(127) return std::numeric_limits<T>::max() > 127
: std::numeric_limits<T>::max(); ? static_cast<T>(127)
} : std::numeric_limits<T>::max();
} }
}
template <class T, bool _is_flag, int _i> template <class T, bool _is_flag, int _i>
consteval T calc_j() { consteval T calc_j() {
if constexpr (_is_flag) { if constexpr (_is_flag) {
return static_cast<T>(1) << _i; return static_cast<T>(1) << _i;
} else { } else {
return static_cast<T>(_i); return static_cast<T>(_i);
} }
} }
template <class EnumType, class NamesType, auto _max, bool _is_flag, int _i> template <class EnumType,
consteval auto get_enum_names_impl() { class NamesType,
using T = std::underlying_type_t<EnumType>; auto _max,
bool _is_flag,
int _i>
consteval auto get_enum_names_impl() {
using T = std::underlying_type_t<EnumType>;
constexpr T j = calc_j<T, _is_flag, _i>(); constexpr T j = calc_j<T, _is_flag, _i>();
constexpr auto name = get_enum_name<static_cast<EnumType>(j)>(); constexpr auto name = get_enum_name<static_cast<EnumType>(j)>();
if constexpr (std::get<0>(name.arr_) == '(') { if constexpr (std::get<0>(name.arr_) == '(') {
if constexpr (j == _max) { if constexpr (j == _max) {
return NamesType{}; return NamesType {};
} else { } else {
return get_enum_names_impl<EnumType, NamesType, _max, _is_flag, _i + 1>(); return get_enum_names_impl<EnumType, NamesType, _max, _is_flag,
} _i + 1>();
} else { }
using NewNames = typename NamesType::template AddOneType< } else {
Literal<remove_namespaces<name>()>, static_cast<EnumType>(j)>; using NewNames = typename NamesType::template AddOneType<
Literal<remove_namespaces<name>()>, static_cast<EnumType>(j)>;
if constexpr (j == _max) { if constexpr (j == _max) {
return NewNames{}; return NewNames {};
} else { } else {
return get_enum_names_impl<EnumType, NewNames, _max, _is_flag, _i + 1>(); return get_enum_names_impl<EnumType, NewNames, _max, _is_flag,
} _i + 1>();
} }
} }
}
template <class EnumType, bool _is_flag> template <class EnumType, bool _is_flag>
consteval auto get_enum_names() { consteval auto get_enum_names() {
static_assert(is_scoped_enum<EnumType>, static_assert(
"You must use scoped enums (using class or struct) for the " is_scoped_enum<EnumType>,
"parsing to work!"); "You must use scoped enums (using class or struct) for the "
"parsing to work!");
static_assert(std::is_integral_v<std::underlying_type_t<EnumType>>, static_assert(std::is_integral_v<std::underlying_type_t<EnumType>>,
"The underlying type of any Enum must be integral!"); "The underlying type of any Enum must be integral!");
constexpr auto max = get_max<std::underlying_type_t<EnumType>, _is_flag>(); constexpr auto max =
get_max<std::underlying_type_t<EnumType>, _is_flag>();
using EmptyNames = Names<EnumType, rfl::Literal<"">, 0>; using EmptyNames = Names<EnumType, rfl::Literal<"">, 0>;
return get_enum_names_impl<EnumType, EmptyNames, max, _is_flag, 0>(); return get_enum_names_impl<EnumType, EmptyNames, max, _is_flag, 0>();
} }
} // namespace enums } // namespace enums
} // namespace internal } // namespace internal
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -6,17 +6,17 @@
#include "is_scoped_enum.hpp" #include "is_scoped_enum.hpp"
namespace rfl { namespace rfl {
namespace internal { namespace internal {
namespace enums { namespace enums {
template <class EnumType> template <class EnumType>
concept is_flag_enum = is_scoped_enum<EnumType> && concept is_flag_enum =
requires(EnumType e1, EnumType e2) { is_scoped_enum<EnumType> && requires(EnumType e1, EnumType e2) {
{ e1 | e2 } -> std::same_as<EnumType>; { e1 | e2 } -> std::same_as<EnumType>;
}; };
} // namespace enums } // namespace enums
} // namespace internal } // namespace internal
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -5,15 +5,16 @@
#include <type_traits> #include <type_traits>
namespace rfl { namespace rfl {
namespace internal { namespace internal {
namespace enums { namespace enums {
template <class EnumType> template <class EnumType>
concept is_scoped_enum = std::is_enum_v<EnumType> && concept is_scoped_enum =
!std::is_convertible_v<EnumType, std::underlying_type_t<EnumType>>; std::is_enum_v<EnumType> &&
!std::is_convertible_v<EnumType, std::underlying_type_t<EnumType>>;
} // namespace enums } // namespace enums
} // namespace internal } // namespace internal
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -8,18 +8,19 @@
#include "../field_type.hpp" #include "../field_type.hpp"
namespace rfl { namespace rfl {
namespace internal { namespace internal {
template <class TaggedUnionType> template <class TaggedUnionType>
struct extract_discriminators; struct extract_discriminators;
template <StringLiteral _discriminator, class... NamedTupleType> template <StringLiteral _discriminator, class... NamedTupleType>
struct extract_discriminators<TaggedUnion<_discriminator, NamedTupleType...>> { struct extract_discriminators<
using type = define_literal_t< TaggedUnion<_discriminator, NamedTupleType...>> {
std::remove_cvref_t<field_type_t<_discriminator, NamedTupleType>>...>; using type = define_literal_t<
}; std::remove_cvref_t<field_type_t<_discriminator, NamedTupleType>>...>;
};
} // namespace internal } // namespace internal
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -8,13 +8,13 @@
#include "copy_to_field_tuple.hpp" #include "copy_to_field_tuple.hpp"
namespace rfl { namespace rfl {
namespace internal { namespace internal {
template <class T> template <class T>
using field_tuple_t = using field_tuple_t =
typename std::invoke_result<decltype(copy_to_field_tuple<T>), T>::type; typename std::invoke_result<decltype(copy_to_field_tuple<T>), T>::type;
} }
} // namespace rfl } // namespace rfl
#endif #endif

View file

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

View file

@ -7,33 +7,33 @@
#include "StringLiteral.hpp" #include "StringLiteral.hpp"
namespace rfl { namespace rfl {
namespace internal { namespace internal {
/// Finds the index of the field signified by _field_name /// Finds the index of the field signified by _field_name
template <StringLiteral _field_name, class Fields, int I = 0> template <StringLiteral _field_name, class Fields, int I = 0>
constexpr static int find_index() { constexpr static int find_index() {
using FieldType = using FieldType =
std::remove_cvref_t<typename std::tuple_element<I, Fields>::type>; std::remove_cvref_t<typename std::tuple_element<I, Fields>::type>;
constexpr bool name_i_matches = (FieldType::name_ == _field_name); constexpr bool name_i_matches = (FieldType::name_ == _field_name);
if constexpr (name_i_matches) { if constexpr (name_i_matches) {
return I; return I;
} else { } else {
constexpr bool out_of_range = I + 1 == std::tuple_size_v<Fields>; constexpr bool out_of_range = I + 1 == std::tuple_size_v<Fields>;
static_assert(!out_of_range, "Field name not found!"); static_assert(!out_of_range, "Field name not found!");
if constexpr (out_of_range) { if constexpr (out_of_range) {
// This is to avoid very confusing error messages. // This is to avoid very confusing error messages.
return I; return I;
} else { } else {
return find_index<_field_name, Fields, I + 1>(); return find_index<_field_name, Fields, I + 1>();
}
}
} }
}
}
} // namespace internal } // namespace internal
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -7,13 +7,14 @@
#include "to_flattened_ptr_tuple.hpp" #include "to_flattened_ptr_tuple.hpp"
namespace rfl { namespace rfl {
namespace internal { namespace internal {
template <class T> template <class T>
using flattened_ptr_tuple_t = using flattened_ptr_tuple_t =
typename std::invoke_result<decltype(to_flattened_ptr_tuple<T>), T>::type; typename std::invoke_result<decltype(to_flattened_ptr_tuple<T>),
T>::type;
} // namespace internal } // namespace internal
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -5,18 +5,18 @@
#include <tuple> #include <tuple>
#include <type_traits> #include <type_traits>
#include "../to_named_tuple.hpp"
#include "flattened_ptr_tuple_t.hpp" #include "flattened_ptr_tuple_t.hpp"
#include "remove_ptrs_tup.hpp" #include "remove_ptrs_tup.hpp"
#include "../to_named_tuple.hpp"
namespace rfl { namespace rfl {
namespace internal { namespace internal {
template <class T> template <class T>
using flattened_tuple_t = using flattened_tuple_t =
typename remove_ptrs_tup<flattened_ptr_tuple_t<T>>::TupleType; typename remove_ptrs_tup<flattened_ptr_tuple_t<T>>::TupleType;
} // namespace internal } // namespace internal
} // namespace rfl } // namespace rfl
#endif #endif

View file

@ -2,7 +2,7 @@
#define RFL_INTERNAL_GETFAKEOBJECT_HPP_ #define RFL_INTERNAL_GETFAKEOBJECT_HPP_
namespace rfl { namespace rfl {
namespace internal { namespace internal {
#if __GNUC__ #if __GNUC__
#ifndef __clang__ #ifndef __clang__
@ -21,16 +21,16 @@ namespace internal {
#pragma warning(disable : 7631) #pragma warning(disable : 7631)
#endif #endif
template <class T> template <class T>
struct wrapper { struct wrapper {
const T value; const T value;
static const wrapper<T> report_if_you_see_a_link_error_with_this_object; static const wrapper<T> report_if_you_see_a_link_error_with_this_object;
}; };
template <class T> template <class T>
consteval const T& get_fake_object() noexcept { consteval const T& get_fake_object() noexcept {
return wrapper<T>::report_if_you_see_a_link_error_with_this_object.value; return wrapper<T>::report_if_you_see_a_link_error_with_this_object.value;
} }
#ifdef __clang__ #ifdef __clang__
#pragma clang diagnostic pop #pragma clang diagnostic pop
@ -40,7 +40,7 @@ consteval const T& get_fake_object() noexcept {
#pragma warning(pop) #pragma warning(pop)
#endif #endif
} // namespace internal } // namespace internal
} // namespace rfl } // namespace rfl
#endif #endif

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