From a9b2a91d48a684fe4b0531305137f912df32e4d1 Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Mon, 9 Jun 2025 11:44:22 +0200 Subject: [PATCH 1/4] Used better references for NamedTuple --- include/rfl/NamedTuple.hpp | 50 +++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/include/rfl/NamedTuple.hpp b/include/rfl/NamedTuple.hpp index 3bb270e4..e4e23efa 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,15 +147,18 @@ 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...); } @@ -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 @@ -383,9 +390,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 +400,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 +442,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 +454,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_)); From 77dadea46401659aef74dff6e7f556eef880f74c Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Mon, 9 Jun 2025 11:59:11 +0200 Subject: [PATCH 2/4] Completed the references of NamedTuple --- include/rfl/NamedTuple.hpp | 52 ++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/include/rfl/NamedTuple.hpp b/include/rfl/NamedTuple.hpp index e4e23efa..ca759526 100644 --- a/include/rfl/NamedTuple.hpp +++ b/include/rfl/NamedTuple.hpp @@ -165,21 +165,21 @@ class NamedTuple { /// 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()...); }; @@ -283,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)...); } } @@ -298,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) { @@ -312,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 { @@ -335,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)...); } @@ -345,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...); } @@ -355,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))...); }; @@ -480,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_; @@ -499,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)); } @@ -509,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)...); }; From 8419e5a86d937a720b81046cbb6544da94e1f5a6 Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Mon, 9 Jun 2025 12:33:01 +0200 Subject: [PATCH 3/4] Better reference handling for rfl::Result --- include/rfl/Result.hpp | 95 ++++++++++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 37 deletions(-) diff --git a/include/rfl/Result.hpp b/include/rfl/Result.hpp index a466b6e8..ca95c05a 100644 --- a/include/rfl/Result.hpp +++ b/include/rfl/Result.hpp @@ -119,19 +119,19 @@ class Result { /// Monadic operation - F must be a function of type T -> Result. template - auto and_then(const F& _f) { + auto and_then(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 +146,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 +184,7 @@ class Result { new (&get_err()) Error(_err.error()); return *this; } + Result& operator=(const Unexpected& _err) noexcept { destroy(); success_ = false; @@ -199,18 +204,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 +225,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,9 +249,9 @@ 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 get_t(); + return std::move(*this).get_t(); } else { throw std::runtime_error(get_err().what()); } @@ -254,7 +259,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 +268,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 +285,55 @@ 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& { if (success_) throw std::runtime_error("Expected does not contain value"); return get_err(); } - Error& error() & { + + Error& error() && { if (success_) throw std::runtime_error("Expected does not contain value"); - return get_err(); + return std::move(*this).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 +365,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())); } From c17ac8279a4f549e1d3cbcfb8a661dfcdab1c7a2 Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Mon, 9 Jun 2025 14:11:38 +0200 Subject: [PATCH 4/4] Removed minor inconsistencies in Result --- include/rfl/Result.hpp | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/include/rfl/Result.hpp b/include/rfl/Result.hpp index ca95c05a..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,7 +125,7 @@ class Result { /// Monadic operation - F must be a function of type T -> Result. template - auto and_then(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_) { @@ -257,6 +263,16 @@ 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() & { + 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& { @@ -308,14 +324,19 @@ class Result { 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 std::move(*this).get_err(); + } + + Error& error() & { if (success_) throw std::runtime_error("Expected does not contain value"); return get_err(); } - Error& error() && { + const Error& error() const& { if (success_) throw std::runtime_error("Expected does not contain value"); - return std::move(*this).get_err(); + return get_err(); } T* operator->() noexcept { return &get_t(); }