Line data Source code
1 : /**
2 : * Copyright Soramitsu Co., Ltd. 2018 All Rights Reserved.
3 : * http://soramitsu.co.jp
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : #ifndef IROHA_RESULT_HPP
19 : #define IROHA_RESULT_HPP
20 :
21 : #include <ciso646>
22 :
23 : #include <boost/optional.hpp>
24 : #include <boost/variant.hpp>
25 :
26 : #include "common/visitor.hpp"
27 :
28 : /*
29 : * Result is a type which represents value or an error, and values and errors
30 : * are template parametrized. Working with value wrapped in result is done using
31 : * match() function, which accepts 2 functions: for value and error cases. No
32 : * accessor functions are provided.
33 : */
34 :
35 : namespace iroha {
36 : namespace expected {
37 :
38 : /*
39 : * Value and error types can be constructed from any value or error, if
40 : * underlying types are constructible. Example:
41 : *
42 : * @code
43 : * Value<std::string> v = Value<const char *>("hello");
44 : * @nocode
45 : */
46 :
47 : template <typename T>
48 : struct Value {
49 : T value;
50 : template <typename V>
51 : operator Value<V>() {
52 2199 : return {value};
53 : }
54 : };
55 :
56 : template <>
57 : struct Value<void> {};
58 :
59 : template <typename E>
60 : struct Error {
61 : E error;
62 : template <typename V>
63 : operator Error<V>() {
64 60 : return {error};
65 : }
66 : };
67 :
68 : template <>
69 : struct Error<void> {};
70 :
71 : /**
72 : * Result is a specialization of a variant type with value or error
73 : * semantics.
74 : * @tparam V type of value
75 : * @tparam E error type
76 : */
77 : template <typename V, typename E>
78 : class Result : public boost::variant<Value<V>, Error<E>> {
79 : using variant_type = boost::variant<Value<V>, Error<E>>;
80 : using variant_type::variant_type; // inherit constructors
81 :
82 : public:
83 : using ValueType = Value<V>;
84 : using ErrorType = Error<E>;
85 :
86 : /**
87 : * match is a function which allows working with result's underlying
88 : * types, you must provide 2 functions to cover success and failure cases.
89 : * Return type of both functions must be the same. Example usage:
90 : * @code
91 : * result.match([](Value<int> v) { std::cout << v.value; },
92 : * [](Error<std::string> e) { std::cout << e.error; });
93 : * @nocode
94 : */
95 : template <typename ValueMatch, typename ErrorMatch>
96 : constexpr auto match(ValueMatch &&value_func, ErrorMatch &&error_func) {
97 21503 : return visit_in_place(*this,
98 21503 : std::forward<ValueMatch>(value_func),
99 21503 : std::forward<ErrorMatch>(error_func));
100 : }
101 :
102 : /**
103 : * Const alternative for match function
104 : */
105 : template <typename ValueMatch, typename ErrorMatch>
106 : constexpr auto match(ValueMatch &&value_func,
107 : ErrorMatch &&error_func) const {
108 815 : return visit_in_place(*this,
109 815 : std::forward<ValueMatch>(value_func),
110 815 : std::forward<ErrorMatch>(error_func));
111 : }
112 :
113 : /**
114 : * Lazy error AND-chaining
115 : * Works by the following table (aka boolean lazy AND):
116 : * err1 * any -> err1
117 : * val1 * err2 -> err2
118 : * val1 * val2 -> val2
119 : *
120 : * @param new_res second chain argument
121 : * @return new_res if this Result contains a value
122 : * otherwise return this
123 : */
124 : template <typename Value>
125 : constexpr Result<Value, E> and_res(const Result<Value, E> &new_res) const
126 : noexcept {
127 2 : return visit_in_place(
128 : *this,
129 : [res = new_res](ValueType) { return res; },
130 : [](ErrorType err) -> Result<Value, E> { return err; });
131 0 : }
132 :
133 : /**
134 : * Lazy error OR-chaining
135 : * Works by the following table (aka boolean lazy OR):
136 : * val1 * any -> val1
137 : * err1 * val2 -> val2
138 : * err1 * err2 -> err2
139 : *
140 : * @param new_res second chain argument
141 : * @return new_res if this Result contains a error
142 : * otherwise return this
143 : */
144 : template <typename Value>
145 : constexpr Result<Value, E> or_res(const Result<Value, E> &new_res) const
146 : noexcept {
147 2 : return visit_in_place(
148 : *this,
149 : [](ValueType val) -> Result<Value, E> { return val; },
150 : [res = new_res](ErrorType) { return res; });
151 0 : }
152 : };
153 :
154 : template <typename ResultType>
155 : using ValueOf = typename ResultType::ValueType;
156 : template <typename ResultType>
157 : using ErrorOf = typename ResultType::ErrorType;
158 :
159 : /**
160 : * Get a new result with the copied value or mapped error
161 : * @param res base Result for getting new one
162 : * @param map callback for error mapping
163 : * @return result with changed error
164 : */
165 : template <typename Err1, typename Err2, typename V, typename Fn>
166 : Result<V, Err1> map_error(const Result<V, Err2> &res, Fn &&map) noexcept {
167 1 : return visit_in_place(res,
168 : [](Value<V> val) -> Result<V, Err1> { return val; },
169 : [map](Error<Err2> err) -> Result<V, Err1> {
170 1 : return Error<Err1>{map(err.error)};
171 : });
172 : }
173 :
174 : // Factory methods for avoiding type specification
175 : template <typename T>
176 : Value<T> makeValue(T &&value) {
177 14563 : return Value<T>{std::forward<T>(value)};
178 : }
179 :
180 : template <typename E>
181 : Error<E> makeError(E &&error) {
182 1240 : return Error<E>{std::forward<E>(error)};
183 : }
184 :
185 : /**
186 : * Bind operator allows chaining several functions which return result. If
187 : * result contains error, it returns this error, if it contains value,
188 : * function f is called.
189 : * @param f function which return type must be compatible with original
190 : * result
191 : */
192 : template <typename T, typename E, typename Transform>
193 : constexpr auto operator|(Result<T, E> r, Transform &&f) ->
194 : typename std::enable_if<
195 : not std::is_same<decltype(f(std::declval<T>())), void>::value,
196 : decltype(f(std::declval<T>()))>::type {
197 : using return_type = decltype(f(std::declval<T>()));
198 7 : return r.match(
199 : [&f](const Value<T> &v) { return f(v.value); },
200 : [](const Error<E> &e) { return return_type(makeError(e.error)); });
201 : }
202 :
203 : /**
204 : * Bind operator overload for functions which do not accept anything as a
205 : * parameter. Allows execution of a sequence of unrelated functions, given
206 : * that all of them return Result
207 : * @param f function which accepts no parameters and returns result
208 : */
209 : template <typename T, typename E, typename Procedure>
210 : constexpr auto operator|(Result<T, E> r, Procedure f) ->
211 : typename std::enable_if<not std::is_same<decltype(f()), void>::value,
212 : decltype(f())>::type {
213 : using return_type = decltype(f());
214 725 : return r.match(
215 : [&f](const Value<T> &v) { return f(); },
216 : [](const Error<E> &e) { return return_type(makeError(e.error)); });
217 : }
218 :
219 : /**
220 : * Polymorphic Result is simple alias for result type, which can be used to
221 : * work with polymorphic objects. It is achieved by wrapping V and E in a
222 : * polymorphic container (std::shared_ptr is used by default). This
223 : * simplifies declaration of polymorphic result.
224 : *
225 : * Note: ordinary result itself stores both V and E directly inside itself
226 : * (on the stack), polymorphic result stores objects wherever VContainer and
227 : * EContainer store them, but since you need polymorphic behavior, it will
228 : * probably be on the heap. That is why polymorphic result is generally
229 : * slower, and should be used ONLY when polymorphic behaviour is required,
230 : * hence the name. For all other use cases, stick to basic Result
231 : */
232 : template <typename V,
233 : typename E,
234 : typename VContainer = std::shared_ptr<V>,
235 : typename EContainer = std::shared_ptr<E>>
236 : using PolymorphicResult = Result<VContainer, EContainer>;
237 :
238 : } // namespace expected
239 : } // namespace iroha
240 : #endif // IROHA_RESULT_HPP
|