diff --git a/src/coresult.h b/src/coresult.h index a4458c3..4f3914b 100644 --- a/src/coresult.h +++ b/src/coresult.h @@ -207,8 +207,10 @@ namespace NDiRes { }; template - struct [[nodiscard]] TInternalAwaitabler { // Awaiter + Awaitable + struct [[nodiscard]] TCommonInternalAwaitabler { // Awaiter + Awaitable std::variant OkRefOrErrorValue; + template + TCommonInternalAwaitabler(Ts && ... Vs): OkRefOrErrorValue(std::forward(Vs)...) {} bool await_ready() noexcept { return OkRefOrErrorValue.index() == NPrivate::OkIndex; @@ -219,47 +221,91 @@ namespace NDiRes { assert(!await_ready()); Handle.promise().return_error_value(std::move(std::get(OkRefOrErrorValue))); } + }; + // clang-format off + template + struct [[nodiscard]] TLValueInternalAwaitabler: public TCommonInternalAwaitabler { + template + TLValueInternalAwaitabler(Ts&&... Vs): + TCommonInternalAwaitabler(std::forward(Vs)...) {} TOk &await_resume() noexcept { // Result is Ok => Return its Ok() and continue execution - assert(await_ready()); - return *std::get(OkRefOrErrorValue); + assert(this->await_ready()); + return *std::get(this->OkRefOrErrorValue); } }; + template + struct [[nodiscard]] TRValueInternalAwaitabler: public TCommonInternalAwaitabler { + template + TRValueInternalAwaitabler(Ts&&... Vs): + TCommonInternalAwaitabler(std::forward(Vs)...) {} + TOk &&await_resume() noexcept { // Result is Ok => Return its Ok() and continue execution + assert(this->await_ready()); + return std::move(*std::get(this->OkRefOrErrorValue)); + } + }; + // clang-format on public: - TCoResult(TReturn && Return) noexcept: TBase(std::move(Return.Handle.promise().ReturnResult)) {} + TCoResult(TReturn &&Return) noexcept: TBase(std::move(Return.Handle.promise().ReturnResult)) {} // Each Company has its own implementation of string template - requires StringPrependable TInternalAwaitabler OrPrependErrMsgAndReturn( + requires StringPrependable TLValueInternalAwaitabler OrPrependErrMsgAndReturn( TStringView Prefix = NPrivate::DefaultErrMsgPrefix, const char *Function = __builtin_FUNCTION(), // Replace with std::source_location - const int Line = __builtin_LINE()) noexcept { + const int Line = __builtin_LINE()) & noexcept { if(this->IsOk()) { - return {.OkRefOrErrorValue {std::in_place_index_t<0>(), &this->Ok()}}; + return {std::in_place_index_t<0>(), &this->Ok()}; } else { - return {.OkRefOrErrorValue { - std::in_place_index_t<1>(), - NPrivate::ErrorMessageFrom(Prefix, Function, Line, this->Err())}}; + return {std::in_place_index_t<1>(), + NPrivate::ErrorMessageFrom(Prefix, Function, Line, this->Err())}; + } + } + template + requires StringPrependable TRValueInternalAwaitabler OrPrependErrMsgAndReturn( + TStringView Prefix = NPrivate::DefaultErrMsgPrefix, + const char *Function = __builtin_FUNCTION(), // Replace with std::source_location + const int Line = __builtin_LINE()) && noexcept { + if(this->IsOk()) { + return {std::in_place_index_t<0>(), &this->Ok()}; + } else { + return {std::in_place_index_t<1>(), + NPrivate::ErrorMessageFrom(Prefix, Function, Line, this->Err())}; } } template TCreateNewErr> - auto OrReturnNewErr(TCreateNewErr && CreateNewErr) noexcept { - using TRet = TInternalAwaitabler>>; + auto OrReturnNewErr(TCreateNewErr &&CreateNewErr) & noexcept { + using TRet = TLValueInternalAwaitabler>>; + if(this->IsOk()) { + return TRet {std::in_place_index_t<0>(), &this->Ok()}; + } else { + return TRet {std::in_place_index_t<1>(), CreateNewErr(std::move(this->Err()))}; + } + } + template TCreateNewErr> + auto OrReturnNewErr(TCreateNewErr &&CreateNewErr) && noexcept { + using TRet = TRValueInternalAwaitabler>>; if(this->IsOk()) { - return TRet {.OkRefOrErrorValue {std::in_place_index_t<0>(), &this->Ok()}}; + return TRet {std::in_place_index_t<0>(), &this->Ok()}; } else { - return TRet { - .OkRefOrErrorValue {std::in_place_index_t<1>(), CreateNewErr(std::move(this->Err()))}}; + return TRet {std::in_place_index_t<1>(), CreateNewErr(std::move(this->Err()))}; } } template - TInternalAwaitabler> OrReturn(T && Something) noexcept { + TLValueInternalAwaitabler> OrReturn(T &&Something) & noexcept { + if(this->IsOk()) { + return {std::in_place_index_t<0>(), &this->Ok()}; + } else { + return {std::in_place_index_t<1>(), std::forward(Something)}; + } + } + template + TRValueInternalAwaitabler> OrReturn(T &&Something) && noexcept { if(this->IsOk()) { - return {.OkRefOrErrorValue {std::in_place_index_t<0>(), &this->Ok()}}; + return {std::in_place_index_t<0>(), &this->Ok()}; } else { - return {.OkRefOrErrorValue {std::in_place_index_t<1>(), std::forward(Something)}}; + return {std::in_place_index_t<1>(), std::forward(Something)}; } } }; - } // namespace NDiRes diff --git a/ut/coresult.t.cpp b/ut/coresult.t.cpp index 0b07c39..cd5d20c 100644 --- a/ut/coresult.t.cpp +++ b/ut/coresult.t.cpp @@ -3,11 +3,13 @@ #include #include #include +#include #include #include using ::testing::EndsWith; using ::testing::StartsWith; +using namespace std::string_literals; // Help: // https://blog.panicsoftware.com/your-first-coroutine/ @@ -42,6 +44,43 @@ TEST_F(TCoResult_NonVoid_SameTypes, Test) { } } + +// ------------------------------------------------------------------------------------------------------ + +struct TCoResult_NonVoid_CoAwaitTemporary: public ::testing::Test { + TCoResult, std::string> F1() { + return MakeUnique(2); + } + TCoResult, std::string> OrPrependErrMsgAndReturn() { + // std::unique_ptr &Ok = co_await F1().OrReturn(2); // Should not compile: rvalue cannot bind to non-const lvalue + std::unique_ptr Ok = co_await F1().OrPrependErrMsgAndReturn(); + std::cout << *Ok << std::endl; + co_return std::move(Ok); + } + TCoResult, std::string> OrReturnNewErr() { + // TMyStruct &Ok = co_await F1().OrReturn(2); // Should not compile: rvalue cannot bind to non-const + // lvalue + std::unique_ptr Ok = co_await F1().OrReturnNewErr([](std::string &&) { return ""; }); + std::cout << *Ok << std::endl; + co_return std::move(Ok); + } + TCoResult, std::string> OrReturn() { + // std::unique_ptr &Ok = co_await F1().OrReturn(""); // Should not compile: rvalue cannot bind to non-const lvalue + std::unique_ptr Ok = co_await F1().OrReturn(""); + std::cout << *Ok << std::endl; + co_return std::move(Ok); + } +}; +TEST_F(TCoResult_NonVoid_CoAwaitTemporary, OrPrependErrMsgAndReturn) { + (void)OrPrependErrMsgAndReturn(); +} +TEST_F(TCoResult_NonVoid_CoAwaitTemporary, OrReturnNewErr) { + (void)OrReturnNewErr(); +} +TEST_F(TCoResult_NonVoid_CoAwaitTemporary, OrReturn) { + (void)OrReturn(); +} + // ------------------------------------------------------------------------------------------------------ struct TCoResult_NonVoid_BothAreMovableOnly: public ::testing::Test {