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_preprocessor/compiler.h"
46#include "pw_result/internal/result_internal.h"
47#include "pw_status/status.h"
48
49namespace pw {
50
52
53// Returned Result objects may not be ignored.
54template <typename T>
55class [[nodiscard]] Result;
56
138template <typename T>
139class Result : private internal_result::StatusOrData<T>,
140 private internal_result::CopyCtorBase<T>,
141 private internal_result::MoveCtorBase<T>,
142 private internal_result::CopyAssignBase<T>,
143 private internal_result::MoveAssignBase<T> {
144 template <typename U>
145 friend class Result;
146
147 using Base = internal_result::StatusOrData<T>;
148
149 public:
153 typedef T value_type;
154
155 // Constructors
156
162 explicit constexpr Result();
163
165 constexpr Result(const Result&) = default;
168 constexpr Result& operator=(const Result&) = default;
169
171 constexpr Result(Result&&) = default;
174 constexpr Result& operator=(Result&&) = default;
175
176 // Converting Constructors
177
185 template <
186 typename U,
187 std::enable_if_t<
188 std::conjunction<
189 std::negation<std::is_same<T, U>>,
190 std::is_constructible<T, const U&>,
191 std::is_convertible<const U&, T>,
192 std::negation<internal_result::
193 IsConstructibleOrConvertibleFromResult<T, U>>>::
194 value,
195 int> = 0>
196 constexpr Result(const Result<U>& other) // NOLINT
197 : Base(static_cast<const typename Result<U>::Base&>(other)) {}
198 template <
199 typename U,
200 std::enable_if_t<
201 std::conjunction<
202 std::negation<std::is_same<T, U>>,
203 std::is_constructible<T, const U&>,
204 std::negation<std::is_convertible<const U&, T>>,
205 std::negation<internal_result::
206 IsConstructibleOrConvertibleFromResult<T, U>>>::
207 value,
208 int> = 0>
209 explicit constexpr Result(const Result<U>& other)
210 : Base(static_cast<const typename Result<U>::Base&>(other)) {}
211
212 template <
213 typename U,
214 std::enable_if_t<
215 std::conjunction<
216 std::negation<std::is_same<T, U>>,
217 std::is_constructible<T, U&&>,
218 std::is_convertible<U&&, T>,
219 std::negation<internal_result::
220 IsConstructibleOrConvertibleFromResult<T, U>>>::
221 value,
222 int> = 0>
223 constexpr Result(Result<U>&& other) // NOLINT
224 : Base(static_cast<typename Result<U>::Base&&>(other)) {}
225 template <
226 typename U,
227 std::enable_if_t<
228 std::conjunction<
229 std::negation<std::is_same<T, U>>,
230 std::is_constructible<T, U&&>,
231 std::negation<std::is_convertible<U&&, T>>,
232 std::negation<internal_result::
233 IsConstructibleOrConvertibleFromResult<T, U>>>::
234 value,
235 int> = 0>
236 explicit constexpr Result(Result<U>&& other)
237 : Base(static_cast<typename Result<U>::Base&&>(other)) {}
238
239 // Converting Assignment Operators
240
254 template <typename U,
255 std::enable_if_t<
256 std::conjunction<
257 std::negation<std::is_same<T, U>>,
258 std::is_constructible<T, const U&>,
259 std::is_assignable<T, const U&>,
260 std::negation<
261 internal_result::
262 IsConstructibleOrConvertibleOrAssignableFromResult<
263 T,
264 U>>>::value,
265 int> = 0>
266 constexpr Result& operator=(const Result<U>& other) {
267 this->Assign(other);
268 return *this;
269 }
270 template <typename U,
271 std::enable_if_t<
272 std::conjunction<
273 std::negation<std::is_same<T, U>>,
274 std::is_constructible<T, U&&>,
275 std::is_assignable<T, U&&>,
276 std::negation<
277 internal_result::
278 IsConstructibleOrConvertibleOrAssignableFromResult<
279 T,
280 U>>>::value,
281 int> = 0>
282 constexpr Result& operator=(Result<U>&& other) {
283 this->Assign(std::move(other));
284 return *this;
285 }
286
299 template <
300 typename U = Status,
301 std::enable_if_t<
302 std::conjunction<
303 std::is_convertible<U&&, Status>,
304 std::is_constructible<Status, U&&>,
305 std::negation<std::is_same<std::decay_t<U>, Result<T>>>,
306 std::negation<std::is_same<std::decay_t<U>, T>>,
307 std::negation<std::is_same<std::decay_t<U>, std::in_place_t>>,
308 std::negation<internal_result::
309 HasConversionOperatorToResult<T, U&&>>>::value,
310 int> = 0>
311 constexpr Result(U&& v) : Base(std::forward<U>(v)) {}
312
313 template <
314 typename U = Status,
315 std::enable_if_t<
316 std::conjunction<
317 std::negation<std::is_convertible<U&&, Status>>,
318 std::is_constructible<Status, U&&>,
319 std::negation<std::is_same<std::decay_t<U>, Result<T>>>,
320 std::negation<std::is_same<std::decay_t<U>, T>>,
321 std::negation<std::is_same<std::decay_t<U>, std::in_place_t>>,
322 std::negation<internal_result::
323 HasConversionOperatorToResult<T, U&&>>>::value,
324 int> = 0>
325 constexpr explicit Result(U&& v) : Base(std::forward<U>(v)) {}
326
327 template <
328 typename U = Status,
329 std::enable_if_t<
330 std::conjunction<
331 std::is_convertible<U&&, Status>,
332 std::is_constructible<Status, U&&>,
333 std::negation<std::is_same<std::decay_t<U>, Result<T>>>,
334 std::negation<std::is_same<std::decay_t<U>, T>>,
335 std::negation<std::is_same<std::decay_t<U>, std::in_place_t>>,
336 std::negation<internal_result::
337 HasConversionOperatorToResult<T, U&&>>>::value,
338 int> = 0>
339 constexpr Result& operator=(U&& v) {
340 this->AssignStatus(std::forward<U>(v));
341 return *this;
342 }
343
362 template <
363 typename U = T,
364 typename = typename std::enable_if<std::conjunction<
365 std::is_constructible<T, U&&>,
366 std::is_assignable<T&, U&&>,
367 std::disjunction<
368 std::is_same<std::remove_cv_t<std::remove_reference_t<U>>, T>,
369 std::conjunction<
370 std::negation<std::is_convertible<U&&, Status>>,
371 std::negation<
372 internal_result::HasConversionOperatorToResult<T, U&&>>>>,
373 internal_result::IsForwardingAssignmentValid<T, U&&>>::value>::type>
374 constexpr Result& operator=(U&& v) {
375 this->Assign(std::forward<U>(v));
376 return *this;
377 }
378
381 template <typename... Args>
382 explicit constexpr Result(std::in_place_t, Args&&... args);
383 template <typename U, typename... Args>
384 explicit constexpr Result(std::in_place_t,
385 std::initializer_list<U> ilist,
386 Args&&... args);
387
397 template <
398 typename U = T,
399 std::enable_if_t<
400 std::conjunction<
401 internal_result::IsDirectInitializationValid<T, U&&>,
402 std::is_constructible<T, U&&>,
403 std::is_convertible<U&&, T>,
404 std::disjunction<
405 std::is_same<std::remove_cv_t<std::remove_reference_t<U>>, T>,
406 std::conjunction<
407 std::negation<std::is_convertible<U&&, Status>>,
408 std::negation<
409 internal_result::
410 HasConversionOperatorToResult<T, U&&>>>>>::value,
411 int> = 0>
412 constexpr Result(U&& u) // NOLINT
413 : Result(std::in_place, std::forward<U>(u)) {}
414
415 template <
416 typename U = T,
417 std::enable_if_t<
418 std::conjunction<
419 internal_result::IsDirectInitializationValid<T, U&&>,
420 std::disjunction<
421 std::is_same<std::remove_cv_t<std::remove_reference_t<U>>, T>,
422 std::conjunction<
423 std::negation<std::is_constructible<Status, U&&>>,
424 std::negation<
425 internal_result::
426 HasConversionOperatorToResult<T, U&&>>>>,
427 std::is_constructible<T, U&&>,
428 std::negation<std::is_convertible<U&&, T>>>::value,
429 int> = 0>
430 explicit constexpr Result(U&& u) // NOLINT
431 : Result(std::in_place, std::forward<U>(u)) {}
432
447 [[nodiscard]] constexpr bool ok() const { return this->status_.ok(); }
448
451 constexpr Status status() const;
452
482 constexpr const T& value() const& PW_ATTRIBUTE_LIFETIME_BOUND;
483 constexpr T& value() & PW_ATTRIBUTE_LIFETIME_BOUND;
484 constexpr const T&& value() const&& PW_ATTRIBUTE_LIFETIME_BOUND;
485 constexpr T&& value() && PW_ATTRIBUTE_LIFETIME_BOUND;
486
495 constexpr const T& operator*() const& PW_ATTRIBUTE_LIFETIME_BOUND;
496 constexpr T& operator*() & PW_ATTRIBUTE_LIFETIME_BOUND;
497 constexpr const T&& operator*() const&& PW_ATTRIBUTE_LIFETIME_BOUND;
498 constexpr T&& operator*() && PW_ATTRIBUTE_LIFETIME_BOUND;
499
505 constexpr const T* operator->() const PW_ATTRIBUTE_LIFETIME_BOUND;
506 constexpr T* operator->() PW_ATTRIBUTE_LIFETIME_BOUND;
507
521 template <typename U>
522 constexpr T value_or(U&& default_value) const&;
523 template <typename U>
524 constexpr T value_or(U&& default_value) &&;
525
528 constexpr void IgnoreError() const;
529
534 template <typename... Args>
535 T& emplace(Args&&... args) {
536 if (ok()) {
537 this->Clear();
538 this->MakeValue(std::forward<Args>(args)...);
539 } else {
540 this->MakeValue(std::forward<Args>(args)...);
541 this->status_ = OkStatus();
542 }
543 return this->data_;
544 }
545
546 template <
547 typename U,
548 typename... Args,
549 std::enable_if_t<
550 std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value,
551 int> = 0>
552 T& emplace(std::initializer_list<U> ilist, Args&&... args) {
553 if (ok()) {
554 this->Clear();
555 this->MakeValue(ilist, std::forward<Args>(args)...);
556 } else {
557 this->MakeValue(ilist, std::forward<Args>(args)...);
558 this->status_ = OkStatus();
559 }
560 return this->data_;
561 }
562
582 template <typename Fn,
583 typename Ret = internal_result::InvokeResultType<Fn, T&>,
584 std::enable_if_t<std::is_copy_constructible_v<Ret>, int> = 0>
585 constexpr Ret and_then(Fn&& function) & {
586 static_assert(internal_result::IsResult<Ret>,
587 "Fn must return a pw::Result");
588 return ok() ? std::invoke(std::forward<Fn>(function), value())
589 : Ret(status());
590 }
591
592 template <typename Fn,
593 typename Ret = internal_result::InvokeResultType<Fn, T&&>,
594 std::enable_if_t<std::is_move_constructible_v<Ret>, int> = 0>
595 constexpr auto and_then(Fn&& function) && {
596 static_assert(internal_result::IsResult<Ret>,
597 "Fn must return a pw::Result");
598 return ok() ? std::invoke(std::forward<Fn>(function), std::move(value()))
599 : Ret(status());
600 }
601
602 template <typename Fn,
603 typename Ret = internal_result::InvokeResultType<Fn, const T&>,
604 std::enable_if_t<std::is_copy_constructible_v<Ret>, int> = 0>
605 constexpr auto and_then(Fn&& function) const& {
606 static_assert(internal_result::IsResult<Ret>,
607 "Fn must return a pw::Result");
608 return ok() ? std::invoke(std::forward<Fn>(function), value())
609 : Ret(status());
610 }
611
612 template <typename Fn,
613 typename Ret = internal_result::InvokeResultType<Fn, const T&&>,
614 std::enable_if_t<std::is_move_constructible_v<Ret>, int> = 0>
615 constexpr auto and_then(Fn&& function) const&& {
616 static_assert(internal_result::IsResult<Ret>,
617 "Fn must return a pw::Result");
618 return ok() ? std::invoke(std::forward<Fn>(function), std::move(value()))
619 : Ret(status());
620 }
621
644 template <typename Fn,
645 typename Ret = internal_result::InvokeResultType<Fn, const Status&>,
646 std::enable_if_t<!std::is_void_v<Ret>, int> = 0>
647 constexpr Result<T> or_else(Fn&& function) const& {
648 static_assert(std::is_convertible_v<Ret, Result<T>>,
649 "Fn must be convertible to a pw::Result");
650 return ok() ? *this : std::invoke(std::forward<Fn>(function), status());
651 }
652
653 template <typename Fn,
654 typename Ret = internal_result::InvokeResultType<Fn, const Status&>,
655 std::enable_if_t<std::is_void_v<Ret>, int> = 0>
656 constexpr Result<T> or_else(Fn&& function) const& {
657 if (ok()) {
658 return *this;
659 }
660 std::invoke(std::forward<Fn>(function), status());
661 return *this;
662 }
663
664 template <typename Fn,
665 typename Ret = internal_result::InvokeResultType<Fn, Status&&>,
666 std::enable_if_t<!std::is_void_v<Ret>, int> = 0>
667 constexpr Result<T> or_else(Fn&& function) && {
668 static_assert(std::is_convertible_v<Ret, Result<T>>,
669 "Fn must be convertible to a pw::Result");
670 return ok() ? std::move(*this)
671 : std::invoke(std::forward<Fn>(function), std::move(status()));
672 }
673
674 template <typename Fn,
675 typename Ret = internal_result::InvokeResultType<Fn, Status&&>,
676 std::enable_if_t<std::is_void_v<Ret>, int> = 0>
677 constexpr Result<T> or_else(Fn&& function) && {
678 if (ok()) {
679 return *this;
680 }
681 std::invoke(std::forward<Fn>(function), status());
682 return std::move(*this);
683 }
684
695 template <typename Fn,
696 typename Ret = internal_result::InvokeResultType<Fn, T&>,
697 std::enable_if_t<std::is_copy_constructible_v<Ret>, int> = 0>
698 constexpr Result<Ret> transform(Fn&& function) & {
699 if (!ok()) {
700 return status();
701 }
702 return std::invoke(std::forward<Fn>(function), value());
703 }
704
705 template <typename Fn,
706 typename Ret = internal_result::InvokeResultType<Fn, T&&>,
707 std::enable_if_t<std::is_move_constructible_v<Ret>, int> = 0>
708 constexpr Result<Ret> transform(Fn&& function) && {
709 if (!ok()) {
710 return std::move(status());
711 }
712 return std::invoke(std::forward<Fn>(function), std::move(value()));
713 }
714
715 template <typename Fn,
716 typename Ret = internal_result::InvokeResultType<Fn, T&>,
717 std::enable_if_t<std::is_copy_constructible_v<Ret>, int> = 0>
718 constexpr Result<Ret> transform(Fn&& function) const& {
719 if (!ok()) {
720 return status();
721 }
722 return std::invoke(std::forward<Fn>(function), value());
723 }
724
725 template <typename Fn,
726 typename Ret = internal_result::InvokeResultType<Fn, T&&>,
727 std::enable_if_t<std::is_move_constructible_v<Ret>, int> = 0>
728 constexpr Result<Ret> transform(Fn&& function) const&& {
729 if (!ok()) {
730 return std::move(status());
731 }
732 return std::invoke(std::forward<Fn>(function), std::move(value()));
733 }
734
735 private:
736 using Base::Assign;
737 template <typename U>
738 constexpr void Assign(const Result<U>& other);
739 template <typename U>
740 constexpr void Assign(Result<U>&& other);
741};
742
744template <typename T>
745Result(T value) -> Result<T>;
746
748template <typename T>
749constexpr bool operator==(const Result<T>& lhs, const Result<T>& rhs) {
750 if (lhs.ok() && rhs.ok()) {
751 return *lhs == *rhs;
752 }
753 return lhs.status() == rhs.status();
754}
755
757template <typename T>
758constexpr bool operator!=(const Result<T>& lhs, const Result<T>& rhs) {
759 return !(lhs == rhs);
760}
761
763
764//------------------------------------------------------------------------------
765// Implementation details for Result<T>
766//------------------------------------------------------------------------------
767
768template <typename T>
769constexpr Result<T>::Result() : Base(Status::Unknown()) {}
770
771template <typename T>
772template <typename U>
773constexpr inline void Result<T>::Assign(const Result<U>& other) {
774 if (other.ok()) {
775 this->Assign(*other);
776 } else {
777 this->AssignStatus(other.status());
778 }
779}
780
781template <typename T>
782template <typename U>
783constexpr inline void Result<T>::Assign(Result<U>&& other) {
784 if (other.ok()) {
785 this->Assign(*std::move(other));
786 } else {
787 this->AssignStatus(std::move(other).status());
788 }
789}
790template <typename T>
791template <typename... Args>
792constexpr Result<T>::Result(std::in_place_t, Args&&... args)
793 : Base(std::in_place, std::forward<Args>(args)...) {}
794
795template <typename T>
796template <typename U, typename... Args>
797constexpr Result<T>::Result(std::in_place_t,
798 std::initializer_list<U> ilist,
799 Args&&... args)
800 : Base(std::in_place, ilist, std::forward<Args>(args)...) {}
801
802template <typename T>
803constexpr Status Result<T>::status() const {
804 return this->status_;
805}
806
807template <typename T>
808constexpr const T& Result<T>::value() const& {
809 PW_ASSERT(this->status_.ok());
810 return this->data_;
811}
812
813template <typename T>
814constexpr T& Result<T>::value() & {
815 PW_ASSERT(this->status_.ok());
816 return this->data_;
817}
818
819template <typename T>
820constexpr const T&& Result<T>::value() const&& {
821 PW_ASSERT(this->status_.ok());
822 return std::move(this->data_);
823}
824
825template <typename T>
826constexpr T&& Result<T>::value() && {
827 PW_ASSERT(this->status_.ok());
828 return std::move(this->data_);
829}
830
831template <typename T>
832constexpr const T& Result<T>::operator*() const& {
833 PW_ASSERT(this->status_.ok());
834 return this->data_;
835}
836
837template <typename T>
838constexpr T& Result<T>::operator*() & {
839 PW_ASSERT(this->status_.ok());
840 return this->data_;
841}
842
843template <typename T>
844constexpr const T&& Result<T>::operator*() const&& {
845 PW_ASSERT(this->status_.ok());
846 return std::move(this->data_);
847}
848
849template <typename T>
850constexpr T&& Result<T>::operator*() && {
851 PW_ASSERT(this->status_.ok());
852 return std::move(this->data_);
853}
854
855template <typename T>
856constexpr const T* Result<T>::operator->() const {
857 PW_ASSERT(this->status_.ok());
858 return &this->data_;
859}
860
861template <typename T>
862constexpr T* Result<T>::operator->() {
863 PW_ASSERT(this->status_.ok());
864 return &this->data_;
865}
866
867template <typename T>
868template <typename U>
869constexpr T Result<T>::value_or(U&& default_value) const& {
870 if (ok()) {
871 return this->data_;
872 }
873 return std::forward<U>(default_value);
874}
875
876template <typename T>
877template <typename U>
878constexpr T Result<T>::value_or(U&& default_value) && {
879 if (ok()) {
880 return std::move(this->data_);
881 }
882 return std::forward<U>(default_value);
883}
884
885template <typename T>
886constexpr void Result<T>::IgnoreError() const {
887 // no-op
888}
889
890namespace internal {
891
892template <typename T>
893constexpr Status ConvertToStatus(const Result<T>& result) {
894 return result.status();
895}
896
897template <typename T>
898constexpr T&& ConvertToValue(Result<T>& result) {
899 return std::move(result).value();
900}
901
902} // namespace internal
903} // namespace pw
Definition: result.h:143
constexpr const T & operator*() const &PW_ATTRIBUTE_LIFETIME_BOUND
Definition: result.h:832
constexpr Result(const Result &)=default
Result<T> is copy constructible if T is copy constructible.
constexpr Result(const Result< U > &other)
Definition: result.h:196
T & emplace(Args &&... args)
Definition: result.h:535
constexpr Result & operator=(Result &&)=default
constexpr Result(U &&u)
Definition: result.h:412
constexpr Result< T > or_else(Fn &&function) const &
Definition: result.h:647
constexpr void IgnoreError() const
Definition: result.h:886
constexpr Result & operator=(U &&v)
Definition: result.h:374
constexpr bool ok() const
Definition: result.h:447
constexpr const T & value() const &PW_ATTRIBUTE_LIFETIME_BOUND
Definition: result.h:808
constexpr const T * operator->() const PW_ATTRIBUTE_LIFETIME_BOUND
Definition: result.h:856
constexpr Result & operator=(const Result< U > &other)
Definition: result.h:266
constexpr T value_or(U &&default_value) const &
constexpr Result & operator=(const Result &)=default
constexpr Ret and_then(Fn &&function) &
Definition: result.h:585
constexpr Status status() const
Definition: result.h:803
T value_type
Definition: result.h:153
constexpr Result< Ret > transform(Fn &&function) &
Definition: result.h:698
constexpr Result(U &&v)
Definition: result.h:311
constexpr Result(Result &&)=default
Result<T> is move constructible if T is move constructible.
Definition: status.h:120
#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