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 | 111853 | constexpr expected(const unexpected_t&) : Valid(false), Code{UnexpectedCode::Unknown} {} | ||
49 | ||||
50 | 34005788 | 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 | 2008052 | constexpr expected(TValue validValue) | ||
54 | 2008052 | : Valid(true) | ||
55 | 2008052 | , Value(validValue) | ||
56 | { | |||
57 | 2008052 | } | ||
58 | ||||
59 | // Implicit conversion operator to bool, so you can do things like if (myResult) { ... } | |||
60 | 742913 | constexpr explicit operator bool() const { | ||
61 | 742913 | return Valid; | ||
62 | } | |||
63 | ||||
64 | // Easy default value handling | |||
65 | 35877652 | constexpr TValue value_or(TValue valueIfInvalid) const { | ||
66 |
3/6expected<float>::value_or(float) const:
✓ Branch 0 taken 1876042 times.
✓ Branch 1 taken 34001607 times.
expected<int>::value_or(int) const:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
expected<long>::value_or(long) const:
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
35877652 | return Valid ? Value : valueIfInvalid; | |
67 | } | |||
68 | ||||
69 | 32 | bool operator ==(const expected<TValue>& other) const { | ||
70 | // If validity mismatch, not equal | |||
71 |
2/4expected<float>::operator==(expected<float> const&) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
expected<long>::operator==(expected<long> const&) const:
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2/4expected<float>::operator==(expected<float> const&) const:
✗ Decision 'true' not taken.
✓ Decision 'false' taken 30 times.
expected<long>::operator==(expected<long> const&) const:
✗ Decision 'true' not taken.
✓ Decision 'false' taken 2 times.
|
32 | if (Valid != other.Valid) { |
72 | ✗ | return false; | ||
73 | } | |||
74 | ||||
75 | // If both are invalid, they are equal | |||
76 |
5/8expected<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.
expected<long>::operator==(expected<long> const&) const:
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
3/4expected<float>::operator==(expected<float> const&) const:
✓ Decision 'true' taken 14 times.
✓ Decision 'false' taken 16 times.
expected<long>::operator==(expected<long> const&) const:
✓ Decision 'true' taken 2 times.
✗ Decision 'false' not taken.
|
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 |