| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | /** | |||
| 2 | * @file expected.h | |||
| 3 | * @brief This utility class provides a way for a function to accept or return a value that may be invalid. | |||
| 4 | * | |||
| 5 | * For example, suppose there needs to be a solution for prevention of divide by zero. One could write this function: | |||
| 6 | * | |||
| 7 | * expected<int> my_divide(int num, int denom) { | |||
| 8 | * if (denom == 0) return unexpected; | |||
| 9 | * return num / denom; | |||
| 10 | * } | |||
| 11 | * | |||
| 12 | * todo: how does this compare to std::optional? | |||
| 13 | * | |||
| 14 | * @date April 18, 2020 | |||
| 15 | * @author Matthew Kennedy, (c) 2020 | |||
| 16 | */ | |||
| 17 | ||||
| 18 | #pragma once | |||
| 19 | ||||
| 20 | struct unexpected_t {}; | |||
| 21 | ||||
| 22 | enum class UnexpectedCode : char { | |||
| 23 | Unknown = 0, | |||
| 24 | ||||
| 25 | // Too much time has passed | |||
| 26 | Timeout, | |||
| 27 | ||||
| 28 | // The decoded value was impossibly high/low | |||
| 29 | High, | |||
| 30 | Low, | |||
| 31 | ||||
| 32 | // An inconsistency was detected using multiple sources of information | |||
| 33 | Inconsistent, | |||
| 34 | ||||
| 35 | // A value is unavailable due to configuration | |||
| 36 | Configuration, | |||
| 37 | }; | |||
| 38 | template <class TValue> | |||
| 39 | struct expected { | |||
| 40 | bool Valid; | |||
| 41 | ||||
| 42 | union { | |||
| 43 | TValue Value; | |||
| 44 | UnexpectedCode Code; | |||
| 45 | }; | |||
| 46 | ||||
| 47 | // Implicit constructor to construct in the invalid state | |||
| 48 | 112849 | constexpr expected(const unexpected_t&) : Valid(false), Code{UnexpectedCode::Unknown} {} | ||
| 49 | ||||
| 50 | 34533564 | constexpr expected(UnexpectedCode code) : Valid(false), Code{code} {} | ||
| 51 | ||||
| 52 | // Implicit constructor to convert from TValue (for valid values, so an expected<T> behaves like a T) | |||
| 53 | 2043839 | constexpr expected(TValue validValue) | ||
| 54 | 2043839 | : Valid(true) | ||
| 55 | 2043839 | , Value(validValue) | ||
| 56 | { | |||
| 57 | 2043839 | } | ||
| 58 | ||||
| 59 | // Implicit conversion operator to bool, so you can do things like if (myResult) { ... } | |||
| 60 | 772119 | constexpr explicit operator bool() const { | ||
| 61 | 772119 | return Valid; | ||
| 62 | } | |||
| 63 | ||||
| 64 | // Easy default value handling | |||
| 65 | 36442372 | constexpr TValue value_or(TValue valueIfInvalid) const { | ||
| 66 |
3/6expected<float>::value_or(float) const:
✓ Branch 0 taken 1912966 times.
✓ Branch 1 taken 34529403 times.
expected<long>::value_or(long) const:
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
expected<int>::value_or(int) const:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
|
36442372 | return Valid ? Value : valueIfInvalid; | |
| 67 | } | |||
| 68 | ||||
| 69 | 32 | bool operator ==(const expected<TValue>& other) const { | ||
| 70 | // If validity mismatch, not equal | |||
| 71 |
2/4expected<long>::operator==(expected<long> const&) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
expected<float>::operator==(expected<float> const&) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
|
2/4expected<long>::operator==(expected<long> const&) const:
✗ Decision 'true' not taken.
✓ Decision 'false' taken 2 times.
expected<float>::operator==(expected<float> const&) const:
✗ Decision 'true' not taken.
✓ Decision 'false' taken 30 times.
|
32 | if (Valid != other.Valid) { |
| 72 | ✗ | return false; | ||
| 73 | } | |||
| 74 | ||||
| 75 | // If both are invalid, they are equal | |||
| 76 |
5/8expected<long>::operator==(expected<long> const&) const:
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
expected<float>::operator==(expected<float> const&) const:
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
|
3/4expected<long>::operator==(expected<long> const&) const:
✓ Decision 'true' taken 2 times.
✗ Decision 'false' not taken.
expected<float>::operator==(expected<float> const&) const:
✓ Decision 'true' taken 14 times.
✓ Decision 'false' taken 16 times.
|
32 | if (!Valid && !other.Valid) { |
| 77 | 16 | return true; | ||
| 78 | } | |||
| 79 | ||||
| 80 | // Both are guaranteed valid - simply compare values | |||
| 81 | 16 | return Value == other.Value; | ||
| 82 | } | |||
| 83 | }; | |||
| 84 | ||||
| 85 | constexpr unexpected_t unexpected{}; | |||
| 86 |