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