Monero
Loading...
Searching...
No Matches
expect.h
Go to the documentation of this file.
1// Copyright (c) 2018-2022, The Monero Project
2
3//
4// All rights reserved.
5//
6// Redistribution and use in source and binary forms, with or without modification, are
7// permitted provided that the following conditions are met:
8//
9// 1. Redistributions of source code must retain the above copyright notice, this list of
10// conditions and the following disclaimer.
11//
12// 2. Redistributions in binary form must reproduce the above copyright notice, this list
13// of conditions and the following disclaimer in the documentation and/or other
14// materials provided with the distribution.
15//
16// 3. Neither the name of the copyright holder nor the names of its contributors may be
17// used to endorse or promote products derived from this software without specific
18// prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
21// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#pragma once
31
32#include <cassert>
33#include <system_error>
34#include <type_traits>
35#include <utility>
36
37#include "common/error.h"
38
40#define MONERO_PRECOND(...) \
41 do \
42 { \
43 if (!( __VA_ARGS__ )) \
44 return {::common_error::kInvalidArgument}; \
45 } while (0)
46
48#define MONERO_CHECK(...) \
49 do \
50 { \
51 const ::expect<void> result = __VA_ARGS__ ; \
52 if (!result) \
53 return result.error(); \
54 } while (0)
55
61#define MONERO_UNWRAP(...) \
62 ::detail::expect::unwrap( __VA_ARGS__ , nullptr, __FILE__ , __LINE__ )
63
64/* \throw std::system_error with `code` and `msg` as part of the details. The
65filename and line number will automatically be injected into the explanation
66string. `code` can be any enum convertible to `std::error_code`. */
67#define MONERO_THROW(code, msg) \
68 ::detail::expect::throw_( code , msg , __FILE__ , __LINE__ )
69
70
71template<typename> class expect;
72
73namespace detail
74{
75 // Shortens the characters in the places that `enable_if` is used below.
76 template<bool C>
77 using enable_if = typename std::enable_if<C>::type;
78
79 struct expect
80 {
82 static void throw_(std::error_code ec, const char* msg, const char* file, unsigned line);
83
85 template<typename T>
86 static T unwrap(::expect<T>&& result, const char* error_msg, const char* file, unsigned line)
87 {
88 if (!result)
89 throw_(result.error(), error_msg, file, line);
90 return std::move(*result);
91 }
92
94 static void unwrap(::expect<void>&& result, const char* error_msg, const char* file, unsigned line);
95 };
96}
97
132template<typename T>
134{
135 static_assert(std::is_nothrow_destructible<T>(), "T must have a nothrow destructor");
136
137 template<typename U>
138 static constexpr bool is_convertible() noexcept
139 {
140 return std::is_constructible<T, U>() &&
141 std::is_convertible<U, T>();
142 }
143
144 // MEMBERS
145 std::error_code code_;
146 typename std::aligned_storage<sizeof(T), alignof(T)>::type storage_;
147 // MEMBERS
148
149 T& get() noexcept
150 {
151 assert(has_value());
152 return *reinterpret_cast<T*>(std::addressof(storage_));
153 }
154
155 T const& get() const noexcept
156 {
157 assert(has_value());
158 return *reinterpret_cast<T const*>(std::addressof(storage_));
159 }
160
161 template<typename U>
162 void store(U&& value) noexcept(std::is_nothrow_constructible<T, U>())
163 {
164 new (std::addressof(storage_)) T{std::forward<U>(value)};
165 code_ = std::error_code{};
166 }
167
168 void maybe_throw() const
169 {
170 if (has_error())
171 ::detail::expect::throw_(error(), nullptr, nullptr, 0);
172 }
173
174public:
175 using value_type = T;
176 using error_type = std::error_code;
177
178 expect() = delete;
179
183 expect(std::error_code const& code) noexcept
184 : code_(code), storage_()
185 {
186 if (!has_error())
188 }
189
191 expect(T val) noexcept(std::is_nothrow_move_constructible<T>())
192 : code_(), storage_()
193 {
194 store(std::move(val));
195 }
196
197 expect(expect const& src) noexcept(std::is_nothrow_copy_constructible<T>())
198 : code_(src.error()), storage_()
199 {
200 if (src.has_value())
201 store(src.get());
202 }
203
205 template<typename U, typename = detail::enable_if<is_convertible<U const&>()>>
206 expect(expect<U> const& src) noexcept(std::is_nothrow_constructible<T, U const&>())
207 : code_(src.error()), storage_()
208 {
209 if (src.has_value())
210 store(*src);
211 }
212
213 expect(expect&& src) noexcept(std::is_nothrow_move_constructible<T>())
214 : code_(src.error()), storage_()
215 {
216 if (src.has_value())
217 store(std::move(src.get()));
218 }
219
221 template<typename U, typename = detail::enable_if<is_convertible<U>()>>
222 expect(expect<U>&& src) noexcept(std::is_nothrow_constructible<T, U>())
223 : code_(src.error()), storage_()
224 {
225 if (src.has_value())
226 store(std::move(*src));
227 }
228
229 ~expect() noexcept
230 {
231 if (has_value())
232 get().~T();
233 }
234
235 expect& operator=(expect const& src) noexcept(std::is_nothrow_copy_constructible<T>() && std::is_nothrow_copy_assignable<T>())
236 {
237 if (this != std::addressof(src))
238 {
239 if (has_value() && src.has_value())
240 get() = src.get();
241 else if (has_value())
242 get().~T();
243 else if (src.has_value())
244 store(src.get());
245 code_ = src.error();
246 }
247 return *this;
248 }
249
252 expect& operator=(expect&& src) noexcept(std::is_nothrow_move_constructible<T>() && std::is_nothrow_move_assignable<T>())
253 {
254 if (this != std::addressof(src))
255 {
256 if (has_value() && src.has_value())
257 get() = std::move(src.get());
258 else if (has_value())
259 get().~T();
260 else if (src.has_value())
261 store(std::move(src.get()));
262 code_ = src.error();
263 }
264 return *this;
265 }
266
268 explicit operator bool() const noexcept { return has_value(); }
269
271 bool has_error() const noexcept { return bool(code_); }
272
274 bool has_value() const noexcept { return !has_error(); }
275
277 std::error_code error() const noexcept { return code_; }
278
280 T& value() &
281 {
282 maybe_throw();
283 return get();
284 }
285
287 T const& value() const &
288 {
289 maybe_throw();
290 return get();
291 }
292
295 T&& value() &&
296 {
297 maybe_throw();
298 return std::move(get());
299 }
300
301 //! \return Value, \pre `has_value()`.
302 T* operator->() noexcept { return std::addressof(get()); }
303
303 //! \return Value, \pre `has_value()`.
304 T const* operator->() const noexcept { return std::addressof(get()); }
305
305 //! \return Value, \pre `has_value()`.
306 T& operator*() noexcept { return get(); }
307
307 //! \return Value, \pre `has_value()`.
308 T const& operator*() const noexcept { return get(); }
309
314 template<typename U>
315 bool equal(expect<U> const& rhs) const noexcept(noexcept(*std::declval<expect<T>>() == *rhs))
316 {
317 return has_value() && rhs.has_value() ?
318 get() == *rhs : error() == rhs.error();
319 }
320
321 //! \return False if `has_value()`, otherwise `error() == rhs`.
322 bool equal(std::error_code const& rhs) const noexcept
323 {
324 return has_error() && error() == rhs;
325 }
326
331 template<typename U, typename = detail::enable_if<!std::is_constructible<std::error_code, U>::value>>
332 bool equal(U const& rhs) const noexcept(noexcept(*std::declval<expect<T>>() == rhs))
333 {
334 return has_value() && get() == rhs;
335 }
336
338 bool matches(std::error_condition const& rhs) const noexcept
339 {
340 return has_error() && error() == rhs;
341 }
342};
343
344template<>
345class expect<void>
346{
347 std::error_code code_;
348
349public:
350 using value_type = void;
351 using error_type = std::error_code;
352
354 expect() noexcept
355 : code_()
356 {}
357
358 expect(std::error_code const& code) noexcept
359 : code_(code)
360 {
361 if (!has_error())
363 }
364
365 expect(expect const&) = default;
366 ~expect() = default;
367 expect& operator=(expect const&) = default;
368
370 explicit operator bool() const noexcept { return !has_error(); }
371
373 bool has_error() const noexcept { return bool(code_); }
374
376 std::error_code error() const noexcept { return code_; }
377
378 //! \return `error() == rhs.error()`.
379 bool equal(expect const& rhs) const noexcept
380 {
381 return error() == rhs.error();
382 }
383
385 bool equal(std::error_code const& rhs) const noexcept
386 {
387 return has_error() && error() == rhs;
388 }
389
391 bool matches(std::error_condition const& rhs) const noexcept
392 {
393 return has_error() && error() == rhs;
394 }
395};
396
397//! \return An `expect<void>` object with `!has_error()`.
398inline expect<void> success() noexcept { return expect<void>{}; }
399
400template<typename T, typename U>
401inline
402bool operator==(expect<T> const& lhs, expect<U> const& rhs) noexcept(noexcept(lhs.equal(rhs)))
403{
404 return lhs.equal(rhs);
405}
406
407template<typename T, typename U>
408inline
409bool operator==(expect<T> const& lhs, U const& rhs) noexcept(noexcept(lhs.equal(rhs)))
410{
411 return lhs.equal(rhs);
412}
413
414template<typename T, typename U>
415inline
416bool operator==(T const& lhs, expect<U> const& rhs) noexcept(noexcept(rhs.equal(lhs)))
417{
418 return rhs.equal(lhs);
419}
420
421template<typename T, typename U>
422inline
423bool operator!=(expect<T> const& lhs, expect<U> const& rhs) noexcept(noexcept(lhs.equal(rhs)))
424{
425 return !lhs.equal(rhs);
426}
427
428template<typename T, typename U>
429inline
430bool operator!=(expect<T> const& lhs, U const& rhs) noexcept(noexcept(lhs.equal(rhs)))
431{
432 return !lhs.equal(rhs);
433}
434
435template<typename T, typename U>
436inline
437bool operator!=(T const& lhs, expect<U> const& rhs) noexcept(noexcept(rhs.equal(lhs)))
438{
439 return !rhs.equal(lhs);
440}
441
442namespace detail
443{
444 inline void expect::unwrap(::expect<void>&& result, const char* error_msg, const char* file, unsigned line)
445 {
446 if (!result)
447 throw_(result.error(), error_msg, file, line);
448 }
449}
450
*return True if this is storing an error instead of a value bool has_error() const noexcept
Definition expect.h:373
std::error_code error_type
Definition expect.h:351
std::error_code code_
Definition expect.h:347
~expect()=default
*Create a successful object expect() noexcept
Definition expect.h:354
expect(std::error_code const &code) noexcept
Definition expect.h:358
expect(expect const &)=default
expect & operator=(expect const &)=default
*return Error alway std::error_code error() const noexcept
Definition expect.h:376
void value_type
Definition expect.h:350
Definition expect.h:134
expect & operator=(expect const &src) noexcept(std::is_nothrow_copy_constructible< T >() &&std::is_nothrow_copy_assignable< T >())
Definition expect.h:235
T const & get() const noexcept
Definition expect.h:155
T && value() &&
Definition expect.h:295
void maybe_throw() const
Definition expect.h:168
expect(expect< U > &&src) noexcept(std::is_nothrow_constructible< T, U >())
Move conversion from U to T.
Definition expect.h:222
std::aligned_storage< sizeof(T), alignof(T)>::type storage_
Definition expect.h:146
~expect() noexcept
Definition expect.h:229
T value_type
Definition expect.h:175
expect()=delete
*return False if otherwise error()
static constexpr bool is_convertible() noexcept
Definition expect.h:138
std::error_code code_
Definition expect.h:145
expect(expect< U > const &src) noexcept(std::is_nothrow_constructible< T, U const & >())
Copy conversion from U to T.
Definition expect.h:206
expect(expect const &src) noexcept(std::is_nothrow_copy_constructible< T >())
Definition expect.h:197
*return pre has_value()`. T *operator->() noexcept
Definition expect.h:301
expect(T val) noexcept(std::is_nothrow_move_constructible< T >())
Store a value, val, in the expect object.
Definition expect.h:191
void store(U &&value) noexcept(std::is_nothrow_constructible< T, U >())
Definition expect.h:162
expect(expect &&src) noexcept(std::is_nothrow_move_constructible< T >())
Definition expect.h:213
T & get() noexcept
Definition expect.h:149
bool equal(expect< U > const &rhs) const noexcept(noexcept(*std::declval< expect< T > >()== *rhs))
Definition expect.h:315
std::error_code error_type
Definition expect.h:176
expect(std::error_code const &code) noexcept
Definition expect.h:183
bool success
Definition cold-transaction.cpp:57
bool operator!=(expect< T > const &lhs, expect< U > const &rhs) noexcept(noexcept(lhs.equal(rhs)))
Definition expect.h:423
bool operator==(expect< T > const &lhs, expect< U > const &rhs) noexcept(noexcept(lhs.equal(rhs)))
Definition expect.h:402
#define const
Definition ipfrdr.c:80
int bool
Definition hash.h:36
declaration and default definition for the functions used the API
Definition expect.cpp:34
typename std::enable_if< C >::type enable_if
Definition expect.h:77
const GenericPointer< typename T::ValueType > T2 value
Definition pointer.h:1225
const portMappingElt code
Definition portlistingparse.c:22
tools::wallet2::message_signature_result_t result
Definition signature.cpp:62
@ kInvalidErrorCode
Default std::error_code given to expect<T>.
Definition error.h:37
Definition expect.h:80
static T unwrap(::expect< T > &&result, const char *error_msg, const char *file, unsigned line)
If result.has_error() call throw_. Otherwise,.
Definition expect.h:86
static void throw_(std::error_code ec, const char *msg, const char *file, unsigned line)
Definition expect.cpp:64
#define T(x)