:3
This commit is contained in:
parent
a743cdabe5
commit
bd402f57f5
|
@ -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
|
||||||
|
|
35
flake.lock
35
flake.lock
|
@ -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"
|
||||||
|
|
22
flake.nix
22
flake.nix
|
@ -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 =
|
||||||
|
|
14453
include/ctre.hpp
14453
include/ctre.hpp
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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_
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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_
|
||||||
|
|
|
@ -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
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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_
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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_
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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_
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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_
|
||||||
|
|
|
@ -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_
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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_
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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_
|
|
||||||
|
|
|
@ -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_
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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_
|
||||||
|
|
|
@ -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_
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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_
|
||||||
|
|
|
@ -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_
|
||||||
|
|
|
@ -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_
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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_
|
||||||
|
|
|
@ -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
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
Loading…
Reference in a new issue