libquentier 0.8.0
The library for rich desktop clients of Evernote service
Loading...
Searching...
No Matches
Result.h
1/*
2 * Copyright 2023-2024 Dmitry Ivanov
3 *
4 * This file is part of libquentier
5 *
6 * libquentier is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, version 3 of the License.
9 *
10 * libquentier is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libquentier. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21#include <quentier/exception/RuntimeError.h>
22#include <quentier/types/ErrorString.h>
23
24#include <type_traits>
25#include <variant>
26
27namespace quentier {
28
29template <
30 class ValueType, class ErrorType,
31 typename =
32 typename std::enable_if_t<!std::is_void_v<std::decay_t<ErrorType>>>>
33class Result
34{
35private:
36 template <typename T>
37 struct ValueWrapper
38 {
39 T value;
40 };
41
42 using ValueWrapperInnerType = std::conditional_t<
43 std::is_void_v<std::decay_t<ValueType>>, std::nullptr_t,
44 std::decay_t<ValueType>>;
45
46public:
47 template <
48 typename T1 = ValueType,
49 typename std::enable_if_t<!std::is_void_v<std::decay_t<T1>>> * =
50 nullptr>
51 explicit Result(T1 t) :
52 m_valueOrError{ValueWrapper<std::decay_t<ValueType>>{std::move(t)}}
53 {}
54
55 template <
56 typename T1 = ValueType,
57 typename std::enable_if_t<std::is_void_v<std::decay_t<T1>>> * = nullptr>
58 explicit Result() : m_valueOrError{ValueWrapper<std::nullptr_t>{}}
59 {}
60
61 explicit Result(ErrorType error) : m_valueOrError{std::move(error)} {}
62
63 Result(const Result<ValueType, ErrorType> & other) :
64 m_valueOrError{other.m_valueOrError}
65 {}
66
67 Result(Result<ValueType, ErrorType> && other) :
68 m_valueOrError{std::move(other.m_valueOrError)}
69 {}
70
71 Result & operator=(const Result<ValueType, ErrorType> & other)
72 {
73 if (this != &other) {
74 m_valueOrError = other.m_valueOrError;
75 }
76
77 return *this;
78 }
79
80 Result & operator=(Result<ValueType, ErrorType> && other)
81 {
82 if (this != &other) {
83 m_valueOrError = std::move(other.m_valueOrError);
84 }
85
86 return *this;
87 }
88
92 [[nodiscard]] bool isValid() const noexcept
93 {
94 return std::holds_alternative<ValueWrapper<ValueWrapperInnerType>>(
95 m_valueOrError);
96 }
97
98 operator bool() const noexcept
99 {
100 return isValid();
101 }
102
103 template <
104 typename T1 = ValueType,
105 typename std::enable_if_t<!std::is_void_v<std::decay_t<T1>>> * =
106 nullptr>
107 [[nodiscard]] T1 & get()
108 {
109 // NOTE: std::get also performs the check of what is stored inside the
110 // variant but it throws std::bad_variant_access which doesn't implement
111 // QException so this exception is not representable inside QFuture
112 // in Qt5. Due to this for Qt5 also performing another check and using
113 // another exception type
114#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
115 if (Q_UNLIKELY(!isValid())) {
116 throw RuntimeError{
117 ErrorString{"Detected attempt to get value from empty Result"}};
118 }
119#endif
120
121 return std::get<ValueWrapper<std::decay_t<ValueType>>>(m_valueOrError)
122 .value;
123 }
124
125 template <
126 typename T1 = ValueType,
127 typename std::enable_if_t<!std::is_void_v<std::decay_t<T1>>> * =
128 nullptr>
129 [[nodiscard]] const T1 & get() const
130 {
131 // NOTE: std::get also performs the check of what is stored inside the
132 // variant but it throws std::bad_variant_access which doesn't implement
133 // QException so this exception is not representable inside QFuture
134 // in Qt5. Due to this for Qt5 also performing another check and using
135 // another exception type
136#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
137 if (Q_UNLIKELY(!isValid())) {
138 throw RuntimeError{
139 ErrorString{"Detected attempt to get value from empty Result"}};
140 }
141#endif
142
143 return std::get<ValueWrapper<std::decay_t<ValueType>>>(m_valueOrError)
144 .value;
145 }
146
147 template <
148 typename T1 = ValueType,
149 typename std::enable_if_t<!std::is_void_v<std::decay_t<T1>>> * =
150 nullptr>
151 [[nodiscard]] T1 & operator*()
152 {
153 return get();
154 }
155
156 template <
157 typename T1 = ValueType,
158 typename std::enable_if_t<!std::is_void_v<std::decay_t<T1>>> * =
159 nullptr>
160 [[nodiscard]] const T1 & operator*() const
161 {
162 return get();
163 }
164
165 [[nodiscard]] const ErrorType & error() const
166 {
167 // NOTE: std::get also performs the check of what is stored inside the
168 // variant but it throws std::bad_variant_access which doesn't implement
169 // QException so this exception is not representable inside QFuture
170 // in Qt5. Due to this for Qt5 also performing another check and using
171 // another exception type
172#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
173 if (Q_UNLIKELY(isValid())) {
174 throw RuntimeError{ErrorString{
175 "Detected attempt to get error from non-empty Result"}};
176 }
177#endif
178
179 return std::get<ErrorType>(m_valueOrError);
180 }
181
182 [[nodiscard]] ErrorType & error()
183 {
184 // NOTE: std::get also performs the check of what is stored inside the
185 // variant but it throws std::bad_variant_access which doesn't implement
186 // QException so this exception is not representable inside QFuture
187 // in Qt5. Due to this for Qt5 also performing another check and using
188 // another exception type
189#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
190 if (Q_UNLIKELY(isValid())) {
191 throw RuntimeError{ErrorString{
192 "Detected attempt to get error from non-empty Result"}};
193 }
194#endif
195
196 return std::get<ErrorType>(m_valueOrError);
197 }
198
199private:
200 std::variant<ValueWrapper<ValueWrapperInnerType>, ErrorType> m_valueOrError;
201};
202
203} // namespace quentier
bool isValid() const noexcept
Definition Result.h:92