C/C++ API Reference
Loading...
Searching...
No Matches
result.h
1// Copyright 2022 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14//
15// -----------------------------------------------------------------------------
16// File: result.h
17// -----------------------------------------------------------------------------
18//
19// An `Result<T>` represents a union of an `pw::Status` object and an object of
20// type `T`. The `Result<T>` will either contain an object of type `T`
21// (indicating a successful operation), or an error (of type `Status`)
22// explaining why such a value is not present.
23//
24// In general, check the success of an operation returning a `Result<T>` like
25// you would an `pw::Status` by using the `ok()` member function.
26//
27// Example:
28//
29// Result<Foo> result = Calculation();
30// if (result.ok()) {
31// result->DoSomethingCool();
32// } else {
33// PW_LOG_ERROR("Calculation failed: %s", result.status().str());
34// }
35#pragma once
36
37#include <exception>
38#include <functional>
39#include <initializer_list>
40#include <new>
41#include <string>
42#include <type_traits>
43#include <utility>
44
45#include "pw_containers/internal/optional_data.h"
46#include "pw_preprocessor/compiler.h"
47#include "pw_result/internal/result_internal.h"
48#include "pw_status/status.h"
49
50namespace pw {
51
53
54// Returned Result objects may not be ignored.
55template <typename T>
56class [[nodiscard]] Result;
57
139template <typename T>
141 : private containers::internal::OptionalData<T, pw_Status, PW_STATUS_OK>,
142 private containers::internal::CopyCtorBase<T>,
143 private containers::internal::MoveCtorBase<T>,
144 private containers::internal::CopyAssignBase<T>,
145 private containers::internal::MoveAssignBase<T> {
146 template <typename U>
147 friend class Result;
148
149 using Base = containers::internal::OptionalData<T, pw_Status, PW_STATUS_OK>;
150
151 public:
155 typedef T value_type;
156
157 // Constructors
158
164 explicit constexpr Result();
165
167 constexpr Result(const Result&) = default;
170 constexpr Result& operator=(const Result&) = default;
171
173 constexpr Result(Result&&) = default;
176 constexpr Result& operator=(Result&&) = default;
177
178 // Converting Constructors
179
187 template <
188 typename U,
189 std::enable_if_t<
190 std::conjunction<
191 std::negation<std::is_same<T, U>>,
192 std::is_constructible<T, const U&>,
193 std::is_convertible<const U&, T>,
194 std::negation<internal_result::
195 IsConstructibleOrConvertibleFromResult<T, U>>>::
196 value,
197 int> = 0>
198 constexpr Result(const Result<U>& other) // NOLINT
199 : Base(static_cast<const typename Result<U>::Base&>(other)) {}
200 template <
201 typename U,
202 std::enable_if_t<
203 std::conjunction<
204 std::negation<std::is_same<T, U>>,
205 std::is_constructible<T, const U&>,
206 std::negation<std::is_convertible<const U&, T>>,
207 std::negation<internal_result::
208 IsConstructibleOrConvertibleFromResult<T, U>>>::
209 value,
210 int> = 0>
211 explicit constexpr Result(const Result<U>& other)
212 : Base(static_cast<const typename Result<U>::Base&>(other)) {}
213
214 template <
215 typename U,
216 std::enable_if_t<
217 std::conjunction<
218 std::negation<std::is_same<T, U>>,
219 std::is_constructible<T, U&&>,
220 std::is_convertible<U&&, T>,
221 std::negation<internal_result::
222 IsConstructibleOrConvertibleFromResult<T, U>>>::
223 value,
224 int> = 0>
225 constexpr Result(Result<U>&& other) // NOLINT
226 : Base(static_cast<typename Result<U>::Base&&>(other)) {}
227 template <
228 typename U,
229 std::enable_if_t<
230 std::conjunction<
231 std::negation<std::is_same<T, U>>,
232 std::is_constructible<T, U&&>,
233 std::negation<std::is_convertible<U&&, T>>,
234 std::negation<internal_result::
235 IsConstructibleOrConvertibleFromResult<T, U>>>::
236 value,
237 int> = 0>
238 explicit constexpr Result(Result<U>&& other)
239 : Base(static_cast<typename Result<U>::Base&&>(other)) {}
240
241 // Converting Assignment Operators
242
256 template <typename U,
257 std::enable_if_t<
258 std::conjunction<
259 std::negation<std::is_same<T, U>>,
260 std::is_constructible<T, const U&>,
261 std::is_assignable<T, const U&>,
262 std::negation<
263 internal_result::
264 IsConstructibleOrConvertibleOrAssignableFromResult<
265 T,
266 U>>>::value,
267 int> = 0>
268 constexpr Result& operator=(const Result<U>& other) {
269 this->Assign(other);
270 return *this;
271 }
272 template <typename U,
273 std::enable_if_t<
274 std::conjunction<
275 std::negation<std::is_same<T, U>>,
276 std::is_constructible<T, U&&>,
277 std::is_assignable<T, U&&>,
278 std::negation<
279 internal_result::
280 IsConstructibleOrConvertibleOrAssignableFromResult<
281 T,
282 U>>>::value,
283 int> = 0>
284 constexpr Result& operator=(Result<U>&& other) {
285 this->Assign(std::move(other));
286 return *this;
287 }
288
301 template <
302 typename U = Status,
303 std::enable_if_t<
304 std::conjunction<
305 std::is_convertible<U&&, Status>,
306 std::is_constructible<Status, U&&>,
307 std::negation<std::is_same<std::decay_t<U>, Result<T>>>,
308 std::negation<std::is_same<std::decay_t<U>, T>>,
309 std::negation<std::is_same<std::decay_t<U>, std::in_place_t>>,
310 std::negation<internal_result::
311 HasConversionOperatorToResult<T, U&&>>>::value,
312 int> = 0>
313 constexpr Result(U&& v)
314 : Base(static_cast<Status>(std::forward<U>(v)).code()) {}
315
316 template <
317 typename U = Status,
318 std::enable_if_t<
319 std::conjunction<
320 std::negation<std::is_convertible<U&&, Status>>,
321 std::is_constructible<Status, U&&>,
322 std::negation<std::is_same<std::decay_t<U>, Result<T>>>,
323 std::negation<std::is_same<std::decay_t<U>, T>>,
324 std::negation<std::is_same<std::decay_t<U>, std::in_place_t>>,
325 std::negation<internal_result::
326 HasConversionOperatorToResult<T, U&&>>>::value,
327 int> = 0>
328 constexpr explicit Result(U&& v)
329 : Base(static_cast<Status>(std::forward<U>(v)).code()) {}
330
331 template <
332 typename U = Status,
333 std::enable_if_t<
334 std::conjunction<
335 std::is_convertible<U&&, Status>,
336 std::is_constructible<Status, U&&>,
337 std::negation<std::is_same<std::decay_t<U>, Result<T>>>,
338 std::negation<std::is_same<std::decay_t<U>, T>>,
339 std::negation<std::is_same<std::decay_t<U>, std::in_place_t>>,
340 std::negation<internal_result::
341 HasConversionOperatorToResult<T, U&&>>>::value,
342 int> = 0>
343 constexpr Result& operator=(U&& v) {
344 this->AssignState(static_cast<Status>(std::forward<U>(v)).code());
345 return *this;
346 }
347
366 template <
367 typename U = T,
368 typename = typename std::enable_if<std::conjunction<
369 std::is_constructible<T, U&&>,
370 std::is_assignable<T&, U&&>,
371 std::disjunction<
372 std::is_same<std::remove_cv_t<std::remove_reference_t<U>>, T>,
373 std::conjunction<
374 std::negation<std::is_convertible<U&&, Status>>,
375 std::negation<
376 internal_result::HasConversionOperatorToResult<T, U&&>>>>,
377 internal_result::IsForwardingAssignmentValid<T, U&&>>::value>::type>
378 constexpr Result& operator=(U&& v) {
379 this->Assign(std::forward<U>(v));
380 return *this;
381 }
382
385 template <typename... Args>
386 explicit constexpr Result(std::in_place_t, Args&&... args);
387 template <typename U, typename... Args>
388 explicit constexpr Result(std::in_place_t,
389 std::initializer_list<U> ilist,
390 Args&&... args);
391
401 template <
402 typename U = T,
403 std::enable_if_t<
404 std::conjunction<
405 internal_result::IsDirectInitializationValid<T, U&&>,
406 std::is_constructible<T, U&&>,
407 std::is_convertible<U&&, T>,
408 std::disjunction<
409 std::is_same<std::remove_cv_t<std::remove_reference_t<U>>, T>,
410 std::conjunction<
411 std::negation<std::is_convertible<U&&, Status>>,
412 std::negation<
413 internal_result::
414 HasConversionOperatorToResult<T, U&&>>>>>::value,
415 int> = 0>
416 constexpr Result(U&& u) // NOLINT
417 : Result(std::in_place, std::forward<U>(u)) {}
418
419 template <
420 typename U = T,
421 std::enable_if_t<
422 std::conjunction<
423 internal_result::IsDirectInitializationValid<T, U&&>,
424 std::disjunction<
425 std::is_same<std::remove_cv_t<std::remove_reference_t<U>>, T>,
426 std::conjunction<
427 std::negation<std::is_constructible<Status, U&&>>,
428 std::negation<
429 internal_result::
430 HasConversionOperatorToResult<T, U&&>>>>,
431 std::is_constructible<T, U&&>,
432 std::negation<std::is_convertible<U&&, T>>>::value,
433 int> = 0>
434 explicit constexpr Result(U&& u) // NOLINT
435 : Result(std::in_place, std::forward<U>(u)) {}
436
451 [[nodiscard]] constexpr bool ok() const {
452 return this->state_ == OkStatus().code();
453 }
454
457 constexpr Status status() const;
458
488 constexpr const T& value() const& PW_ATTRIBUTE_LIFETIME_BOUND;
489 constexpr T& value() & PW_ATTRIBUTE_LIFETIME_BOUND;
490 constexpr const T&& value() const&& PW_ATTRIBUTE_LIFETIME_BOUND;
491 constexpr T&& value() && PW_ATTRIBUTE_LIFETIME_BOUND;
492
501 constexpr const T& operator*() const& PW_ATTRIBUTE_LIFETIME_BOUND;
502 constexpr T& operator*() & PW_ATTRIBUTE_LIFETIME_BOUND;
503 constexpr const T&& operator*() const&& PW_ATTRIBUTE_LIFETIME_BOUND;
504 constexpr T&& operator*() && PW_ATTRIBUTE_LIFETIME_BOUND;
505
511 constexpr const T* operator->() const PW_ATTRIBUTE_LIFETIME_BOUND;
512 constexpr T* operator->() PW_ATTRIBUTE_LIFETIME_BOUND;
513
527 template <typename U>
528 constexpr T value_or(U&& default_value) const&;
529 template <typename U>
530 constexpr T value_or(U&& default_value) &&;
531
534 constexpr void IgnoreError() const;
535
540 template <typename... Args>
541 T& emplace(Args&&... args) {
542 if (ok()) {
543 this->Clear();
544 this->MakeValue(std::forward<Args>(args)...);
545 } else {
546 this->MakeValue(std::forward<Args>(args)...);
547 this->state_ = OkStatus().code();
548 }
549 return this->data_;
550 }
551
552 template <
553 typename U,
554 typename... Args,
555 std::enable_if_t<
556 std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value,
557 int> = 0>
558 T& emplace(std::initializer_list<U> ilist, Args&&... args) {
559 if (ok()) {
560 this->Clear();
561 this->MakeValue(ilist, std::forward<Args>(args)...);
562 } else {
563 this->MakeValue(ilist, std::forward<Args>(args)...);
564 this->state_ = OkStatus().code();
565 }
566 return this->data_;
567 }
568
588 template <typename Fn,
589 typename Ret = internal_result::InvokeResultType<Fn, T&>,
590 std::enable_if_t<std::is_copy_constructible_v<Ret>, int> = 0>
591 constexpr Ret and_then(Fn&& function) & {
592 static_assert(internal_result::IsResult<Ret>,
593 "Fn must return a pw::Result");
594 return ok() ? std::invoke(std::forward<Fn>(function), value())
595 : Ret(status());
596 }
597
598 template <typename Fn,
599 typename Ret = internal_result::InvokeResultType<Fn, T&&>,
600 std::enable_if_t<std::is_move_constructible_v<Ret>, int> = 0>
601 constexpr auto and_then(Fn&& function) && {
602 static_assert(internal_result::IsResult<Ret>,
603 "Fn must return a pw::Result");
604 return ok() ? std::invoke(std::forward<Fn>(function), std::move(value()))
605 : Ret(status());
606 }
607
608 template <typename Fn,
609 typename Ret = internal_result::InvokeResultType<Fn, const T&>,
610 std::enable_if_t<std::is_copy_constructible_v<Ret>, int> = 0>
611 constexpr auto and_then(Fn&& function) const& {
612 static_assert(internal_result::IsResult<Ret>,
613 "Fn must return a pw::Result");
614 return ok() ? std::invoke(std::forward<Fn>(function), value())
615 : Ret(status());
616 }
617
618 template <typename Fn,
619 typename Ret = internal_result::InvokeResultType<Fn, const T&&>,
620 std::enable_if_t<std::is_move_constructible_v<Ret>, int> = 0>
621 constexpr auto and_then(Fn&& function) const&& {
622 static_assert(internal_result::IsResult<Ret>,
623 "Fn must return a pw::Result");
624 return ok() ? std::invoke(std::forward<Fn>(function), std::move(value()))
625 : Ret(status());
626 }
627
650 template <typename Fn,
651 typename Ret = internal_result::InvokeResultType<Fn, const Status&>,
652 std::enable_if_t<!std::is_void_v<Ret>, int> = 0>
653 constexpr Result<T> or_else(Fn&& function) const& {
654 static_assert(std::is_convertible_v<Ret, Result<T>>,
655 "Fn must be convertible to a pw::Result");
656 return ok() ? *this : std::invoke(std::forward<Fn>(function), status());
657 }
658
659 template <typename Fn,
660 typename Ret = internal_result::InvokeResultType<Fn, const Status&>,
661 std::enable_if_t<std::is_void_v<Ret>, int> = 0>
662 constexpr Result<T> or_else(Fn&& function) const& {
663 if (ok()) {
664 return *this;
665 }
666 std::invoke(std::forward<Fn>(function), status());
667 return *this;
668 }
669
670 template <typename Fn,
671 typename Ret = internal_result::InvokeResultType<Fn, Status&&>,
672 std::enable_if_t<!std::is_void_v<Ret>, int> = 0>
673 constexpr Result<T> or_else(Fn&& function) && {
674 static_assert(std::is_convertible_v<Ret, Result<T>>,
675 "Fn must be convertible to a pw::Result");
676 return ok() ? std::move(*this)
677 : std::invoke(std::forward<Fn>(function), std::move(status()));
678 }
679
680 template <typename Fn,
681 typename Ret = internal_result::InvokeResultType<Fn, Status&&>,
682 std::enable_if_t<std::is_void_v<Ret>, int> = 0>
683 constexpr Result<T> or_else(Fn&& function) && {
684 if (ok()) {
685 return *this;
686 }
687 std::invoke(std::forward<Fn>(function), status());
688 return std::move(*this);
689 }
690
701 template <typename Fn,
702 typename Ret = internal_result::InvokeResultType<Fn, T&>,
703 std::enable_if_t<std::is_copy_constructible_v<Ret>, int> = 0>
704 constexpr Result<Ret> transform(Fn&& function) & {
705 if (!ok()) {
706 return status();
707 }
708 return std::invoke(std::forward<Fn>(function), value());
709 }
710
711 template <typename Fn,
712 typename Ret = internal_result::InvokeResultType<Fn, T&&>,
713 std::enable_if_t<std::is_move_constructible_v<Ret>, int> = 0>
714 constexpr Result<Ret> transform(Fn&& function) && {
715 if (!ok()) {
716 return std::move(status());
717 }
718 return std::invoke(std::forward<Fn>(function), std::move(value()));
719 }
720
721 template <typename Fn,
722 typename Ret = internal_result::InvokeResultType<Fn, T&>,
723 std::enable_if_t<std::is_copy_constructible_v<Ret>, int> = 0>
724 constexpr Result<Ret> transform(Fn&& function) const& {
725 if (!ok()) {
726 return status();
727 }
728 return std::invoke(std::forward<Fn>(function), value());
729 }
730
731 template <typename Fn,
732 typename Ret = internal_result::InvokeResultType<Fn, T&&>,
733 std::enable_if_t<std::is_move_constructible_v<Ret>, int> = 0>
734 constexpr Result<Ret> transform(Fn&& function) const&& {
735 if (!ok()) {
736 return std::move(status());
737 }
738 return std::invoke(std::forward<Fn>(function), std::move(value()));
739 }
740
741 private:
742 using Base::Assign;
743 template <typename U>
744 constexpr void Assign(const Result<U>& other);
745 template <typename U>
746 constexpr void Assign(Result<U>&& other);
747};
748
750template <typename T>
751Result(T value) -> Result<T>;
752
754template <typename T>
755constexpr bool operator==(const Result<T>& lhs, const Result<T>& rhs) {
756 if (lhs.ok() && rhs.ok()) {
757 return *lhs == *rhs;
758 }
759 return lhs.status() == rhs.status();
760}
761
763template <typename T>
764constexpr bool operator!=(const Result<T>& lhs, const Result<T>& rhs) {
765 return !(lhs == rhs);
766}
767
769
770//------------------------------------------------------------------------------
771// Implementation details for Result<T>
772//------------------------------------------------------------------------------
773
774template <typename T>
775constexpr Result<T>::Result() : Base(Status::Unknown().code()) {}
776
777template <typename T>
778template <typename U>
779constexpr inline void Result<T>::Assign(const Result<U>& other) {
780 if (other.ok()) {
781 this->Assign(*other);
782 } else {
783 this->AssignState(other.status().code());
784 }
785}
786
787template <typename T>
788template <typename U>
789constexpr inline void Result<T>::Assign(Result<U>&& other) {
790 if (other.ok()) {
791 this->Assign(*std::move(other));
792 } else {
793 this->AssignState(std::move(other).status().code());
794 }
795}
796template <typename T>
797template <typename... Args>
798constexpr Result<T>::Result(std::in_place_t, Args&&... args)
799 : Base(std::in_place, std::forward<Args>(args)...) {}
800
801template <typename T>
802template <typename U, typename... Args>
803constexpr Result<T>::Result(std::in_place_t,
804 std::initializer_list<U> ilist,
805 Args&&... args)
806 : Base(std::in_place, ilist, std::forward<Args>(args)...) {}
807
808template <typename T>
809constexpr Status Result<T>::status() const {
810 return this->state_;
811}
812
813template <typename T>
814constexpr const T& Result<T>::value() const& {
815 PW_ASSERT(ok());
816 return this->data_;
817}
818
819template <typename T>
820constexpr T& Result<T>::value() & {
821 PW_ASSERT(ok());
822 return this->data_;
823}
824
825template <typename T>
826constexpr const T&& Result<T>::value() const&& {
827 PW_ASSERT(ok());
828 return std::move(this->data_);
829}
830
831template <typename T>
832constexpr T&& Result<T>::value() && {
833 PW_ASSERT(ok());
834 return std::move(this->data_);
835}
836
837template <typename T>
838constexpr const T& Result<T>::operator*() const& {
839 PW_ASSERT(ok());
840 return this->data_;
841}
842
843template <typename T>
844constexpr T& Result<T>::operator*() & {
845 PW_ASSERT(ok());
846 return this->data_;
847}
848
849template <typename T>
850constexpr const T&& Result<T>::operator*() const&& {
851 PW_ASSERT(ok());
852 return std::move(this->data_);
853}
854
855template <typename T>
856constexpr T&& Result<T>::operator*() && {
857 PW_ASSERT(ok());
858 return std::move(this->data_);
859}
860
861template <typename T>
862constexpr const T* Result<T>::operator->() const {
863 PW_ASSERT(ok());
864 return &this->data_;
865}
866
867template <typename T>
868constexpr T* Result<T>::operator->() {
869 PW_ASSERT(ok());
870 return &this->data_;
871}
872
873template <typename T>
874template <typename U>
875constexpr T Result<T>::value_or(U&& default_value) const& {
876 if (ok()) {
877 return this->data_;
878 }
879 return std::forward<U>(default_value);
880}
881
882template <typename T>
883template <typename U>
884constexpr T Result<T>::value_or(U&& default_value) && {
885 if (ok()) {
886 return std::move(this->data_);
887 }
888 return std::forward<U>(default_value);
889}
890
891template <typename T>
892constexpr void Result<T>::IgnoreError() const {
893 // no-op
894}
895
896namespace internal {
897
898template <typename T>
899constexpr Status ConvertToStatus(const Result<T>& result) {
900 return result.status();
901}
902
903template <typename T>
904constexpr T&& ConvertToValue(Result<T>& result) {
905 return std::move(result).value();
906}
907
908} // namespace internal
909} // namespace pw
Definition: result.h:145
constexpr const T & operator*() const &PW_ATTRIBUTE_LIFETIME_BOUND
Definition: result.h:838
constexpr Result(const Result &)=default
Result<T> is copy constructible if T is copy constructible.
constexpr Result(const Result< U > &other)
Definition: result.h:198
T & emplace(Args &&... args)
Definition: result.h:541
constexpr Result & operator=(Result &&)=default
constexpr Result(U &&u)
Definition: result.h:416
constexpr Result< T > or_else(Fn &&function) const &
Definition: result.h:653
constexpr void IgnoreError() const
Definition: result.h:892
constexpr Result & operator=(U &&v)
Definition: result.h:378
constexpr bool ok() const
Definition: result.h:451
constexpr const T & value() const &PW_ATTRIBUTE_LIFETIME_BOUND
Definition: result.h:814
constexpr const T * operator->() const PW_ATTRIBUTE_LIFETIME_BOUND
Definition: result.h:862
constexpr Result & operator=(const Result< U > &other)
Definition: result.h:268
constexpr T value_or(U &&default_value) const &
constexpr Result & operator=(const Result &)=default
constexpr Ret and_then(Fn &&function) &
Definition: result.h:591
constexpr Status status() const
Definition: result.h:809
T value_type
Definition: result.h:155
constexpr Result< Ret > transform(Fn &&function) &
Definition: result.h:704
constexpr Result(U &&v)
Definition: result.h:313
constexpr Result(Result &&)=default
Result<T> is move constructible if T is move constructible.
Definition: status.h:120
constexpr Code code() const
Definition: status.h:341
#define PW_ATTRIBUTE_LIFETIME_BOUND
Definition: compiler.h:273
Result(T value) -> Result< T >
Deduction guide to allow Result(v) rather than Result<T>(v).
constexpr Status OkStatus()
Definition: status.h:450
Status Assign(InlineString<> &string, std::string_view view)
Definition: util.h:142
The Pigweed namespace.
Definition: alignment.h:27