diff --git a/include/rfl/NamedTuple.hpp b/include/rfl/NamedTuple.hpp index 3bb270e4..ca759526 100644 --- a/include/rfl/NamedTuple.hpp +++ b/include/rfl/NamedTuple.hpp @@ -85,27 +85,29 @@ class NamedTuple { /// Move constructor. template NamedTuple(NamedTuple&& _other) - : NamedTuple(retrieve_fields(_other.fields(), seq_)) {} + : NamedTuple(retrieve_fields( + std::forward>(_other).fields(), + seq_)) {} ~NamedTuple() = default; /// Returns a new named tuple with additional fields. template - auto add(Field<_name, FType>&& _head, Tail&&... _tail) { + auto add(Field<_name, FType>&& _head, Tail&&... _tail) && { using Head = Field<_name, FType>; if constexpr (sizeof...(Tail) > 0) { return NamedTuple>( - make_fields(seq_, std::forward(_head))) + std::move(*this).make_fields(seq_, std::forward(_head))) .add(std::forward(_tail)...); } else { return NamedTuple>( - make_fields(seq_, std::forward(_head))); + std::move(*this).make_fields(seq_, std::forward(_head))); } } /// Returns a new named tuple with additional fields. template - auto add(Field<_name, FType> _head, const Tail&... _tail) const { + auto add(Field<_name, FType> _head, const Tail&... _tail) const& { using Head = Field<_name, FType>; if constexpr (sizeof...(Tail) > 0) { return NamedTuple>( @@ -120,19 +122,21 @@ class NamedTuple { /// Template specialization for rfl::Tuple, so we can pass fields from other /// named tuples. template - auto add(rfl::Tuple&& _tuple, Tail&&... _tail) { + auto add(rfl::Tuple&& _tuple, Tail&&... _tail) && { if constexpr (sizeof...(Tail) > 0) { - return add_tuple(std::forward>(_tuple)) + return std::move(*this) + .add_tuple(std::forward>(_tuple)) .add(std::forward(_tail)...); } else { - return add_tuple(std::forward>(_tuple)); + return std::move(*this).add_tuple( + std::forward>(_tuple)); } } /// Template specialization for rfl::Tuple, so we can pass fields from other /// named tuples. template - auto add(rfl::Tuple _tuple, const Tail&... _tail) const { + auto add(rfl::Tuple _tuple, const Tail&... _tail) const& { if constexpr (sizeof...(Tail) > 0) { return add_tuple(std::move(_tuple)).add(_tail...); } else { @@ -143,36 +147,39 @@ class NamedTuple { /// Template specialization for NamedTuple, so we can pass fields from other /// named tuples. template - auto add(NamedTuple&& _named_tuple, Tail&&... _tail) { - return add(std::forward>(_named_tuple.fields()), - std::forward(_tail)...); + auto add(NamedTuple&& _named_tuple, Tail&&... _tail) && { + return std::move(*this).add( + std::forward>( + std::forward>(_named_tuple).fields()), + std::forward(_tail)...); } /// Template specialization for NamedTuple, so we can pass fields from other /// named tuples. template - auto add(NamedTuple _named_tuple, const Tail&... _tail) const { + auto add(NamedTuple _named_tuple, + const Tail&... _tail) const& { return add(_named_tuple.fields(), _tail...); } /// Creates a new named tuple by applying the supplied function to /// field. The function is expected to return a named tuple itself. template - auto and_then(const F& _f) { + auto and_then(const F& _f) && { const auto transform_field = [&_f](auto... _fields) { return rfl::tuple_cat(_f(std::move(_fields)).fields()...); }; const auto to_nt = [](rfl::Tuple&& _tup) { return NamedTuple(_tup); }; - auto new_fields = rfl::apply(transform_field, std::move(fields())); + auto new_fields = rfl::apply(transform_field, std::move(*this).fields()); return to_nt(std::move(new_fields)); } /// Creates a new named tuple by applying the supplied function to /// field. The function is expected to return a named tuple itself. template - auto and_then(const F& _f) const { + auto and_then(const F& _f) const& { const auto transform_field = [&_f](auto... _fields) { return rfl::tuple_cat(_f(std::move(_fields)).fields()...); }; @@ -193,10 +200,10 @@ class NamedTuple { } /// Returns a tuple containing the fields. - Fields fields() { return make_fields(seq_); } + Fields fields() && { return std::move(*this).make_fields(seq_); } /// Returns a tuple containing the fields. - Fields fields() const { return make_fields(seq_); } + Fields fields() const& { return make_fields(seq_); } /// Gets a field by index. template @@ -276,13 +283,15 @@ class NamedTuple { /// Replaces one or several fields, returning a new version /// with the non-replaced fields left unchanged. template - auto replace(Field<_name, FType>&& _field, OtherRFields&&... _other_fields) { + auto replace(Field<_name, FType>&& _field, + OtherRFields&&... _other_fields) && { using RField = Field<_name, FType>; constexpr auto num_other_fields = sizeof...(OtherRFields); if constexpr (num_other_fields == 0) { - return replace_value(_field.value_); + return std::move(*this).template replace_value(_field.value_); } else { - return replace_value(_field.value_) + return std::move(*this) + .template replace_value(_field.value_) .replace(std::forward(_other_fields)...); } } @@ -291,7 +300,7 @@ class NamedTuple { /// with the non-replaced fields left unchanged. template auto replace(Field<_name, FType> _field, - const OtherRFields&... _other_fields) const { + const OtherRFields&... _other_fields) const& { using RField = Field<_name, FType>; constexpr auto num_other_fields = sizeof...(OtherRFields); if constexpr (num_other_fields == 0) { @@ -305,19 +314,21 @@ class NamedTuple { /// Template specialization for rfl::Tuple, so we can pass fields from other /// named tuples. template - auto replace(rfl::Tuple&& _tuple, Tail&&... _tail) { + auto replace(rfl::Tuple&& _tuple, Tail&&... _tail) && { if constexpr (sizeof...(Tail) > 0) { - return replace_tuple(std::forward>(_tuple)) + return std::move(*this) + .replace_tuple(std::forward>(_tuple)) .replace(std::forward(_tail)...); } else { - return replace_tuple(std::forward>(_tuple)); + return std::move(*this).replace_tuple( + std::forward>(_tuple)); } } /// Template specialization for rfl::Tuple, so we can pass fields from other /// named tuples. template - auto replace(rfl::Tuple _tuple, const Tail&... _tail) const { + auto replace(rfl::Tuple _tuple, const Tail&... _tail) const& { if constexpr (sizeof...(Tail) > 0) { return replace_tuple(std::move(_tuple)).replace(_tail...); } else { @@ -328,8 +339,8 @@ class NamedTuple { /// Template specialization for NamedTuple, so we can pass fields from other /// named tuples. template - auto replace(NamedTuple&& _named_tuple, Tail&&... _tail) { - return replace( + auto replace(NamedTuple&& _named_tuple, Tail&&... _tail) && { + return std::move(*this).replace( std::forward>(_named_tuple).fields(), std::forward(_tail)...); } @@ -338,7 +349,7 @@ class NamedTuple { /// named tuples. template auto replace(NamedTuple _named_tuple, - const Tail&... _tail) const { + const Tail&... _tail) const& { return replace(_named_tuple.fields(), _tail...); } @@ -348,21 +359,21 @@ class NamedTuple { /// Creates a new named tuple by applying the supplied function to every /// field. template - auto transform(const F& _f) { + auto transform(const F& _f) && { const auto transform_field = [&_f](auto... fields) { return rfl::make_tuple(_f(std::move(fields))...); }; const auto to_nt = [](rfl::Tuple&& _tup) { return NamedTuple(_tup); }; - auto new_fields = rfl::apply(transform_field, std::move(fields())); + auto new_fields = rfl::apply(transform_field, std::move(*this).fields()); return to_nt(std::move(new_fields)); } /// Creates a new named tuple by applying the supplied function to every /// field. template - auto transform(const F& _f) const { + auto transform(const F& _f) const& { const auto transform_field = [&_f](auto... fields) { return rfl::make_tuple(_f(std::move(fields))...); }; @@ -383,9 +394,9 @@ class NamedTuple { /// Adds the elements of a tuple to a newly created named tuple, /// and other elements to a newly created named tuple. template - constexpr auto add_tuple(rfl::Tuple&& _tuple) { + constexpr auto add_tuple(rfl::Tuple&& _tuple) && { const auto a = [this](auto&&... _fields) { - return this->add(std::forward(_fields)...); + return std::move(*this).add(std::forward(_fields)...); }; return rfl::apply(a, std::forward>(_tuple)); } @@ -393,7 +404,7 @@ class NamedTuple { /// Adds the elements of a tuple to a newly created named tuple, /// and other elements to a newly created named tuple. template - constexpr auto add_tuple(rfl::Tuple&& _tuple) const { + constexpr auto add_tuple(rfl::Tuple&& _tuple) const& { const auto a = [this](auto&&... _fields) { return this->add(std::forward(_fields)...); }; @@ -435,11 +446,10 @@ class NamedTuple { /// Generates the fields. template auto make_fields(std::integer_sequence, - AdditionalArgs&&... _args) { + AdditionalArgs&&... _args) && { const auto wrap = [this](Index<_i>) { using FieldType = internal::nth_element_t<_i, FieldTypes...>; - using T = std::remove_cvref_t; - return FieldType(std::forward(rfl::get<_i>(values_))); + return FieldType(std::move(rfl::get<_i>(values_))); }; return rfl::make_tuple(wrap(Index<_is>{})..., std::forward(_args)...); @@ -448,7 +458,7 @@ class NamedTuple { /// Generates the fields. template auto make_fields(std::integer_sequence, - AdditionalArgs... _args) const { + AdditionalArgs... _args) const& { const auto wrap = [this](Index<_i>) { using FieldType = internal::nth_element_t<_i, FieldTypes...>; return FieldType(rfl::get<_i>(values_)); @@ -474,16 +484,16 @@ class NamedTuple { /// Replaced the field signified by the field type. template - NamedTuple replace_value(T&& _val) { + NamedTuple replace_value(T&& _val) && { using FieldType = std::remove_cvref_t; constexpr auto index = internal::find_index(); - return make_replaced(std::forward(values_), - std::forward(_val), seq_); + return make_replaced(std::move(values_), std::forward(_val), + seq_); } /// Replaced the field signified by the field type. template - NamedTuple replace_value(T&& _val) const { + NamedTuple replace_value(T&& _val) const& { using FieldType = std::remove_cvref_t; constexpr auto index = internal::find_index(); auto values = values_; @@ -493,9 +503,9 @@ class NamedTuple { /// Adds the elements of a tuple to a newly created named tuple, /// and other elements to a newly created named tuple. template - auto replace_tuple(rfl::Tuple&& _tuple) { + auto replace_tuple(rfl::Tuple&& _tuple) && { const auto r = [this](auto&&... _fields) { - return this->replace(std::forward(_fields)...); + return std::move(*this).replace(std::forward(_fields)...); }; return rfl::apply(r, std::forward>(_tuple)); } @@ -503,7 +513,7 @@ class NamedTuple { /// Adds the elements of a tuple to a newly created named tuple, /// and other elements to a newly created named tuple. template - auto replace_tuple(rfl::Tuple&& _tuple) const { + auto replace_tuple(rfl::Tuple&& _tuple) const& { const auto r = [this](auto&&... _fields) { return this->replace(std::forward(_fields)...); }; diff --git a/include/rfl/Result.hpp b/include/rfl/Result.hpp index a466b6e8..33b0925b 100644 --- a/include/rfl/Result.hpp +++ b/include/rfl/Result.hpp @@ -21,7 +21,9 @@ namespace rfl { class Error { public: Error(const std::string& _what) : what_(_what) {} + Error(const Error& e) = default; + Error& operator=(const Error&) = default; /// Returns the error message, equivalent to .what() in std::exception. @@ -50,13 +52,21 @@ using Result = std::expected; template struct Unexpected { Unexpected(E&& _err) : err_{std::forward(_err)} {} + Unexpected(const E& _err) : err_{_err} {} + Unexpected(Unexpected&&) = default; + Unexpected(const Unexpected&) = default; + Unexpected& operator=(Unexpected&&) = default; + Unexpected& operator=(const Unexpected&) = default; + const E& error() const& { return err_; } + E&& error() && { return std::move(err_); } + E& error() & { return err_; } private: @@ -88,10 +98,6 @@ class Result { new (&get_err()) Error(std::move(_err.error())); } - // Result(Error&& _err) noexcept : success_(false) { - // new (&get_err()) Error(std::move(_err)); - // } - Result(Result&& _other) noexcept : success_(_other.success_) { move_from_other(_other); } @@ -119,19 +125,19 @@ class Result { /// Monadic operation - F must be a function of type T -> Result. template - auto and_then(const F& _f) { + auto and_then(const F& _f) && { /// Result_U is expected to be of type Result. using Result_U = typename std::invoke_result::type; if (success_) { - return Result_U(_f(std::forward(get_t()))); + return Result_U(_f(std::move(*this).get_t())); } else { - return Result_U(std::forward(get_err())); + return Result_U(std::move(*this).get_err()); } } /// Monadic operation - F must be a function of type T -> Result. template - auto and_then(const F& _f) const { + auto and_then(const F& _f) const& { /// Result_U is expected to be of type Result. using Result_U = typename std::invoke_result::type; if (success_) { @@ -146,11 +152,15 @@ class Result { /// Allows access to the underlying value. Careful: Will result in undefined /// behavior, if the result contains an error. - T& operator*() noexcept { return get_t(); } + T&& operator*() && noexcept { return std::move(*this).get_t(); } + + /// Allows access to the underlying value. Careful: Will result in undefined + /// behavior, if the result contains an error. + T& operator*() & noexcept { return get_t(); } /// Allows read access to the underlying value. Careful: Will result in /// undefined behavior, if the result contains an error. - const T& operator*() const noexcept { return get_t(); } + const T& operator*() const& noexcept { return get_t(); } /// Assigns the underlying object. Result& operator=(const Result& _other) { @@ -180,6 +190,7 @@ class Result { new (&get_err()) Error(_err.error()); return *this; } + Result& operator=(const Unexpected& _err) noexcept { destroy(); success_ = false; @@ -199,18 +210,18 @@ class Result { /// Expects a function that takes of type Error -> Result and returns /// Result. template - Result or_else(const F& _f) { + Result or_else(const F& _f) && { if (success_) { - return std::forward(get_t()); + return std::move(*this).get_t(); } else { - return _f(std::forward(get_err())); + return _f(std::move(*this).get_err()); } } /// Expects a function that takes of type Error -> Result and returns /// Result. template - Result or_else(const F& _f) const { + Result or_else(const F& _f) const& { if (success_) { return get_t(); } else { @@ -220,19 +231,19 @@ class Result { /// Functor operation - F must be a function of type T -> U. template - auto transform(const F& _f) { + auto transform(const F& _f) && { /// Result_U is expected to be of type Result. - using U = typename std::invoke_result::type; + using U = std::invoke_result_t; if (success_) { - return rfl::Result(_f(std::forward(get_t()))); + return rfl::Result(_f(std::move(*this).get_t())); } else { - return rfl::Result(rfl::Unexpected(std::forward(get_err()))); + return rfl::Result(rfl::Unexpected(std::move(*this).get_err())); } } /// Functor operation - F must be a function of type T -> U. template - auto transform(const F& _f) const { + auto transform(const F& _f) const& { /// Result_U is expected to be of type Result. using U = typename std::invoke_result::type; if (success_) { @@ -244,7 +255,17 @@ class Result { /// Returns the value if the result does not contain an error, throws an /// exceptions if not. Similar to .unwrap() in Rust. - T& value() { + T&& value() && { + if (success_) { + return std::move(*this).get_t(); + } else { + throw std::runtime_error(get_err().what()); + } + } + + /// Returns the value if the result does not contain an error, throws an + /// exceptions if not. Similar to .unwrap() in Rust. + T& value() & { if (success_) { return get_t(); } else { @@ -254,7 +275,7 @@ class Result { /// 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 { + const T& value() const& { if (success_) { return get_t(); } else { @@ -263,16 +284,16 @@ class Result { } /// Returns the value or a default. - T value_or(T&& _default) noexcept { + T&& value_or(T&& _default) && noexcept { if (success_) { - return std::forward(get_t()); + return std::move(*this).get_t(); } else { return std::forward(_default); } } /// Returns the value or a default. - T value_or(const T& _default) const noexcept { + T value_or(const T& _default) const& noexcept { if (success_) { return get_t(); } else { @@ -280,47 +301,60 @@ class Result { } } - // As specified by the standard : - // https://en.cppreference.com/w/cpp/utility/expected - // Observers template - rfl::Error error_or(G&& _default) const& { + rfl::Error error_or(G&& _default) && { if (success_) { - return _default; + return std::forward(_default); } else { - return get_err(); + return std::move(*this).get_err(); } } + + // As specified by the standard : + // https://en.cppreference.com/w/cpp/utility/expected + // Observers template - rfl::Error error_or(G&& _default) && { + rfl::Error error_or(G&& _default) const& { if (success_) { - return _default; + return std::forward(_default); } else { return get_err(); } } + bool has_value() const noexcept { return success_; } - const Error& error() const& { + + Error& error() && { if (success_) throw std::runtime_error("Expected does not contain value"); - return get_err(); + return std::move(*this).get_err(); } + Error& error() & { if (success_) throw std::runtime_error("Expected does not contain value"); return get_err(); } + + const Error& error() const& { + if (success_) throw std::runtime_error("Expected does not contain value"); + return get_err(); + } + T* operator->() noexcept { return &get_t(); } + const T* operator->() const noexcept { return &get_t(); } + template rfl::Result transform_error(F&& f) && { static_assert( std::is_same, rfl::Error>(), "A function passed to transform_error must return an error."); if (!has_value()) { - return rfl::Result{std::invoke(f, std::move(get_err()))}; + return rfl::Result{std::invoke(f, std::move(*this).get_err())}; } else { - return rfl::Result{std::move(value())}; + return rfl::Result{std::move(*this).value()}; } } + template rfl::Result transform_error(F&& f) const& { static_assert( @@ -352,19 +386,27 @@ class Result { } } - T& get_t() noexcept { + T&& get_t() && noexcept { + return std::move(*std::launder(reinterpret_cast(t_or_err_.data()))); + } + + T& get_t() & noexcept { return *std::launder(reinterpret_cast(t_or_err_.data())); } - const T& get_t() const noexcept { + const T& get_t() const& noexcept { return *std::launder(reinterpret_cast(t_or_err_.data())); } - Error& get_err() noexcept { + Error&& get_err() && noexcept { + return std::move(*std::launder(reinterpret_cast(t_or_err_.data()))); + } + + Error& get_err() & noexcept { return *std::launder(reinterpret_cast(t_or_err_.data())); } - const Error& get_err() const noexcept { + const Error& get_err() const& noexcept { return *std::launder(reinterpret_cast(t_or_err_.data())); }