rippled
Expected.h
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2021 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #ifndef RIPPLE_BASICS_EXPECTED_H_INCLUDED
21 #define RIPPLE_BASICS_EXPECTED_H_INCLUDED
22 
23 #include <ripple/basics/contract.h>
24 
25 #include <boost/outcome.hpp>
26 
27 #include <concepts>
28 #include <stdexcept>
29 #include <type_traits>
30 
31 namespace ripple {
32 
40 // Exception thrown by an invalid access to Expected.
42 {
43  bad_expected_access() : runtime_error("bad expected access")
44  {
45  }
46 };
47 
48 namespace detail {
49 
50 // Custom policy for Expected. Always throw on an invalid access.
51 struct throw_policy : public boost::outcome_v2::policy::base
52 {
53  template <class Impl>
54  static constexpr void
55  wide_value_check(Impl&& self)
56  {
57  if (!base::_has_value(std::forward<Impl>(self)))
58  Throw<bad_expected_access>();
59  }
60 
61  template <class Impl>
62  static constexpr void
63  wide_error_check(Impl&& self)
64  {
65  if (!base::_has_error(std::forward<Impl>(self)))
66  Throw<bad_expected_access>();
67  }
68 
69  template <class Impl>
70  static constexpr void
71  wide_exception_check(Impl&& self)
72  {
73  if (!base::_has_exception(std::forward<Impl>(self)))
74  Throw<bad_expected_access>();
75  }
76 };
77 
78 } // namespace detail
79 
80 // Definition of Unexpected, which is used to construct the unexpected
81 // return type of an Expected.
82 template <class E>
84 {
85 public:
86  static_assert(!std::is_same<E, void>::value, "E must not be void");
87 
88  Unexpected() = delete;
89 
90  constexpr explicit Unexpected(E const& e) : val_(e)
91  {
92  }
93 
94  constexpr explicit Unexpected(E&& e) : val_(std::move(e))
95  {
96  }
97 
98  constexpr const E&
99  value() const&
100  {
101  return val_;
102  }
103 
104  constexpr E&
105  value() &
106  {
107  return val_;
108  }
109 
110  constexpr E&&
111  value() &&
112  {
113  return std::move(val_);
114  }
115 
116  constexpr const E&&
117  value() const&&
118  {
119  return std::move(val_);
120  }
121 
122 private:
123  E val_;
124 };
125 
126 // Unexpected deduction guide that converts array to const*.
127 template <typename E, std::size_t N>
128 Unexpected(E (&)[N]) -> Unexpected<E const*>;
129 
130 // Definition of Expected. All of the machinery comes from boost::result.
131 template <class T, class E>
132 class [[nodiscard]] Expected
133  : private boost::outcome_v2::result<T, E, detail::throw_policy>
134 {
135  using Base = boost::outcome_v2::result<T, E, detail::throw_policy>;
136 
137 public:
138  template <typename U>
139  requires std::convertible_to<U, T> constexpr Expected(U && r)
140  : Base(T(std::forward<U>(r)))
141  {
142  }
143 
144  template <typename U>
145  requires std::convertible_to<U, E> &&
146  (!std::is_reference_v<U>)constexpr Expected(Unexpected<U> e)
147  : Base(E(std::move(e.value())))
148  {
149  }
150 
151  constexpr bool has_value() const
152  {
153  return Base::has_value();
154  }
155 
156  constexpr T const& value() const
157  {
158  return Base::value();
159  }
160 
161  constexpr T& value()
162  {
163  return Base::value();
164  }
165 
166  constexpr E const& error() const
167  {
168  return Base::error();
169  }
170 
171  constexpr E& error()
172  {
173  return Base::error();
174  }
175 
176  constexpr explicit operator bool() const
177  {
178  return has_value();
179  }
180 
181  // Add operator* and operator-> so the Expected API looks a bit more like
182  // what std::expected is likely to look like. See:
183  // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0323r10.html
184  [[nodiscard]] constexpr T& operator*()
185  {
186  return this->value();
187  }
188 
189  [[nodiscard]] constexpr T const& operator*() const
190  {
191  return this->value();
192  }
193 
194  [[nodiscard]] constexpr T* operator->()
195  {
196  return &this->value();
197  }
198 
199  [[nodiscard]] constexpr T const* operator->() const
200  {
201  return &this->value();
202  }
203 };
204 
205 // Specialization of Expected<void, E>. Allows returning either success
206 // (without a value) or the reason for the failure.
207 template <class E>
208 class [[nodiscard]] Expected<void, E>
209  : private boost::outcome_v2::result<void, E, detail::throw_policy>
210 {
211  using Base = boost::outcome_v2::result<void, E, detail::throw_policy>;
212 
213 public:
214  // The default constructor makes a successful Expected<void, E>.
215  // This aligns with std::expected behavior proposed in P0323R10.
216  constexpr Expected() : Base(boost::outcome_v2::success())
217  {
218  }
219 
220  template <typename U>
221  requires std::convertible_to<U, E> &&
222  (!std::is_reference_v<U>)constexpr Expected(Unexpected<U> e)
223  : Base(E(std::move(e.value())))
224  {
225  }
226 
227  constexpr E const& error() const
228  {
229  return Base::error();
230  }
231 
232  constexpr E& error()
233  {
234  return Base::error();
235  }
236 
237  constexpr explicit operator bool() const
238  {
239  return Base::has_value();
240  }
241 };
242 
243 } // namespace ripple
244 
245 #endif // RIPPLE_BASICS_EXPECTED_H_INCLUDED
ripple::bad_expected_access::bad_expected_access
bad_expected_access()
Definition: Expected.h:43
std::is_same
ripple::Expected::error
constexpr E & error()
Definition: Expected.h:171
ripple::Expected::value
constexpr T const & value() const
Definition: Expected.h:156
ripple::bad_expected_access
Expected is an approximation of std::expected (hoped for in C++23)
Definition: Expected.h:41
ripple::Expected::error
constexpr E const & error() const
Definition: Expected.h:166
ripple::Expected::Expected
requires constexpr std::convertible_to< U, T > Expected(U &&r)
Definition: Expected.h:139
ripple::Unexpected::Unexpected
constexpr Unexpected(E const &e)
Definition: Expected.h:90
ripple::Unexpected
Unexpected(E(&)[N]) -> Unexpected< E const * >
ripple::Expected::value
constexpr T & value()
Definition: Expected.h:161
boost
Definition: IPAddress.h:103
ripple::Expected::has_value
constexpr bool has_value() const
Definition: Expected.h:151
ripple::Unexpected::value
constexpr const E && value() const &&
Definition: Expected.h:117
ripple::detail::throw_policy
Definition: Expected.h:51
concepts
ripple::Expected< void, E >::error
constexpr E & error()
Definition: Expected.h:232
stdexcept
ripple::Expected::Expected
requires std::convertible_to< U, E > &&!std constexpr ::is_reference_v< U > Expected(Unexpected< U > e)
Definition: Expected.h:146
ripple::Unexpected::value
constexpr const E & value() const &
Definition: Expected.h:99
ripple::Unexpected::value
constexpr E & value() &
Definition: Expected.h:105
ripple::Expected::Base
boost::outcome_v2::result< T, E, detail::throw_policy > Base
Definition: Expected.h:135
ripple::Expected
Definition: Expected.h:132
ripple::Expected< void, E >::Expected
constexpr Expected()
Definition: Expected.h:216
ripple::detail::throw_policy::wide_value_check
static constexpr void wide_value_check(Impl &&self)
Definition: Expected.h:55
ripple::Expected::operator->
constexpr T * operator->()
Definition: Expected.h:194
std::runtime_error
STL class.
ripple::detail::throw_policy::wide_error_check
static constexpr void wide_error_check(Impl &&self)
Definition: Expected.h:63
ripple::Expected::operator*
constexpr T const & operator*() const
Definition: Expected.h:189
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::Unexpected::Unexpected
constexpr Unexpected(E &&e)
Definition: Expected.h:94
ripple::Expected< void, E >::Expected
requires std::convertible_to< U, E > &&!std constexpr ::is_reference_v< U > Expected(Unexpected< U > e)
Definition: Expected.h:222
ripple::Expected< void, E >::Base
boost::outcome_v2::result< void, E, detail::throw_policy > Base
Definition: Expected.h:211
std
STL namespace.
ripple::Unexpected::value
constexpr E && value() &&
Definition: Expected.h:111
ripple::Expected< void, E >::error
constexpr E const & error() const
Definition: Expected.h:227
ripple::Unexpected
Definition: Expected.h:83
ripple::Expected::operator*
constexpr T & operator*()
Definition: Expected.h:184
ripple::Unexpected::Unexpected
Unexpected()=delete
ripple::Expected::operator->
constexpr T const * operator->() const
Definition: Expected.h:199
type_traits
ripple::detail::throw_policy::wide_exception_check
static constexpr void wide_exception_check(Impl &&self)
Definition: Expected.h:71
ripple::Unexpected::val_
E val_
Definition: Expected.h:123