JSON Voorhees
Killer JSON for C++
Loading...
Searching...
No Matches
result.hpp
Go to the documentation of this file.
1/// \file jsonv/result.hpp
2///
3/// Copyright (c) 2020 by Travis Gockel. All rights reserved.
4///
5/// This program is free software: you can redistribute it and/or modify it under the terms of the Apache License
6/// as published by the Apache Software Foundation, either version 2 of the License, or (at your option) any later
7/// version.
8///
9/// \author Travis Gockel (travis@gockelhut.com)
10#pragma once
11
12#include <jsonv/config.hpp>
14#include <jsonv/optional.hpp>
15#include <jsonv/string_view.hpp>
16
17#include <exception>
18#include <iosfwd>
19#include <stdexcept>
20#include <utility>
21#include <variant>
22
23namespace jsonv
24{
25
26template <typename TValue, typename TError>
27class result;
28
29template <typename TValue>
30class ok;
31
32template <typename TError>
33class error;
34
35}
36
37namespace jsonv::detail
38{
39
40template <typename TResult1, typename TResult2, typename Enabler = void>
41struct result_common_type
42{ };
43
44template <typename TValue1, typename TError1, typename TValue2, typename TError2>
45struct result_common_type<jsonv::result<TValue1, TError1>,
46 jsonv::result<TValue2, TError2>,
47 std::void_t<std::common_type_t<TValue1, TValue2>, std::common_type_t<TError1, TError2>>
48 >
49{
50 using type = jsonv::result<std::common_type_t<TValue1, TValue2>, std::common_type_t<TError1, TError2>>;
51};
52
53}
54
55namespace std
56{
57
58template <typename TValue1, typename TError1, typename TValue2, typename TError2>
59struct common_type<jsonv::result<TValue1, TError1>, jsonv::result<TValue2, TError2>> :
60 public jsonv::detail::result_common_type<jsonv::result<TValue1, TError1>, jsonv::result<TValue2, TError2>>
61{ };
62
63}
64
65namespace jsonv::detail
66{
67
68template <template <typename...> class TT, typename Test>
69struct is_template_of : public std::false_type
70{ };
71
72template <template <typename...> class TT, typename... TArgs>
73struct is_template_of<TT, TT<TArgs...>> : public std::true_type
74{ };
75
76template <template <typename...> class TT, typename Test>
77inline constexpr bool is_template_of_v = is_template_of<TT, Test>::value;
78
79template <typename TResult, typename FUnary, typename Enabler = void>
80struct result_map_result
81{ };
82
83template <typename TInputValue, typename TInputError, typename FUnary>
84struct result_map_result<const result<TInputValue, TInputError>&,
85 FUnary,
86 std::void_t<std::invoke_result_t<FUnary, const TInputValue&>>
87 >
88{
89 struct info
90 {
91 using function_result_type = std::invoke_result_t<FUnary, const TInputValue&>;
92
93 using type = result<function_result_type, TInputError>;
94
95 static constexpr bool is_noexcept = std::is_nothrow_invocable_v<FUnary, const TInputValue&>
96 && std::is_nothrow_move_constructible_v<ok<function_result_type>>
97 && std::is_nothrow_copy_constructible_v<TInputError>;
98 };
99};
100
101template <typename TInputError, typename FUnary>
102struct result_map_result<const result<void, TInputError>&, FUnary, std::void_t<std::invoke_result_t<FUnary>>>
103{
104 struct info
105 {
106 using function_result_type = std::invoke_result_t<FUnary>;
107
108 using type = result<function_result_type, TInputError>;
109
110 static constexpr bool is_noexcept = std::is_nothrow_invocable_v<FUnary>
111 && std::is_nothrow_move_constructible_v<ok<function_result_type>>
112 && std::is_nothrow_copy_constructible_v<TInputError>;
113 };
114};
115
116template <typename TInputValue, typename TInputError, typename FUnary>
117struct result_map_result<result<TInputValue, TInputError>&&,
118 FUnary,
119 std::void_t<std::invoke_result_t<FUnary, TInputValue&&>>
120 >
121{
122 struct info
123 {
124 using function_result_type = std::invoke_result_t<FUnary, TInputValue&&>;
125
126 using type = result<function_result_type, TInputError>;
127
128 static constexpr bool is_noexcept = std::is_nothrow_invocable_v<FUnary, TInputValue&&>
129 && std::is_nothrow_move_constructible_v<ok<function_result_type>>
130 && std::is_nothrow_copy_constructible_v<TInputError>;
131 };
132};
133
134template <typename TInputError, typename FUnary>
135struct result_map_result<result<void, TInputError>&&, FUnary, std::void_t<std::invoke_result_t<FUnary>>>
136{
137 struct info
138 {
139 using function_result_type = std::invoke_result_t<FUnary>;
140
141 using type = result<function_result_type, TInputError>;
142
143 static constexpr bool is_noexcept = std::is_nothrow_invocable_v<FUnary>
144 && std::is_nothrow_move_constructible_v<ok<function_result_type>>
145 && std::is_nothrow_copy_constructible_v<TInputError>;
146 };
147};
148
149template <typename TResult, typename FUnary>
150using result_map_result_info = typename result_map_result<TResult, FUnary>::info;
151
152template <typename TResult, typename FUnary, typename Enabler = void>
153struct result_map_flat_result
154{ };
155
156template <typename TInputValue, typename TInputError, typename FUnary>
157struct result_map_flat_result<const result<TInputValue, TInputError>&,
158 FUnary,
159 std::void_t<std::invoke_result_t<FUnary, const TInputValue&>>
160 >
161{
162 struct info
163 {
164 using function_result_type = std::invoke_result_t<FUnary, const TInputValue&>;
165
166 static_assert(is_template_of_v<result, function_result_type>,
167 "Function provided to `map_flat` must return a `jsonv::result`"
168 );
169
170 using error_type = std::common_type_t<TInputError, typename function_result_type::error_type>;
171
172 using type = result<typename function_result_type::value_type, error_type>;
173
174 static constexpr bool is_noexcept = std::is_nothrow_invocable_v<FUnary, const TInputValue&>
175 && std::is_nothrow_constructible_v<type, const error<TInputError>&>
176 && std::is_nothrow_constructible_v<type, function_result_type&&>;
177 };
178};
179
180template <typename TInputError, typename FUnary>
181struct result_map_flat_result<const result<void, TInputError>&,
182 FUnary,
183 std::void_t<std::invoke_result_t<FUnary>>
184 >
185{
186 struct info
187 {
188 using function_result_type = std::invoke_result_t<FUnary>;
189
190 static_assert(is_template_of_v<result, function_result_type>,
191 "Function provided to `map_flat` must return a `jsonv::result`"
192 );
193
194 using error_type = std::common_type_t<TInputError, typename function_result_type::error_type>;
195
196 using type = result<typename function_result_type::value_type, error_type>;
197
198 static constexpr bool is_noexcept = std::is_nothrow_invocable_v<FUnary>
199 && std::is_nothrow_constructible_v<type, const error<TInputError>&>
200 && std::is_nothrow_constructible_v<type, function_result_type&&>;
201 };
202};
203
204template <typename TInputValue, typename TInputError, typename FUnary>
205struct result_map_flat_result<result<TInputValue, TInputError>&&,
206 FUnary,
207 std::void_t<std::invoke_result_t<FUnary, TInputValue&&>>
208 >
209{
210 struct info
211 {
212 using function_result_type = std::invoke_result_t<FUnary, TInputValue&&>;
213
214 static_assert(is_template_of_v<result, function_result_type>,
215 "Function provided to `map_flat` must return a `jsonv::result`"
216 );
217
218 using error_type = std::common_type_t<TInputError, typename function_result_type::error_type>;
219
220 using type = result<typename function_result_type::value_type, error_type>;
221
222 static constexpr bool is_noexcept = std::is_nothrow_invocable_v<FUnary, TInputValue&&>
223 && std::is_nothrow_constructible_v<type, error<TInputError>&&>
224 && std::is_nothrow_constructible_v<type, function_result_type&&>;
225 };
226};
227
228template <typename TInputError, typename FUnary>
229struct result_map_flat_result<result<void, TInputError>&&, FUnary, std::void_t<std::invoke_result_t<FUnary>>>
230{
231 struct info
232 {
233 using function_result_type = std::invoke_result_t<FUnary>;
234
235 static_assert(is_template_of_v<result, function_result_type>,
236 "Function provided to `map_flat` must return a `jsonv::result`"
237 );
238
239 using error_type = std::common_type_t<TInputError, typename function_result_type::error_type>;
240
241 using type = result<typename function_result_type::value_type, error_type>;
242
243 static constexpr bool is_noexcept = std::is_nothrow_invocable_v<FUnary>
244 && std::is_nothrow_constructible_v<type, error<TInputError>&&>
245 && std::is_nothrow_constructible_v<type, function_result_type&&>;
246 };
247};
248
249template <typename TResult, typename FUnary>
250using result_map_flat_result_info = typename result_map_flat_result<TResult, FUnary>::info;
251
252template <typename TResult, typename FUnary, typename Enabler = void>
253struct result_map_error_result
254{ };
255
256template <typename TInputValue, typename TInputError, typename FUnary>
257struct result_map_error_result<const result<TInputValue, TInputError>&,
258 FUnary,
259 std::void_t<std::invoke_result_t<FUnary, const TInputError&>>
260 >
261{
262 struct info
263 {
264 using function_result_type = std::invoke_result_t<FUnary, const TInputError&>;
265
266 using type = result<TInputValue, function_result_type>;
267
268 static constexpr bool is_noexcept = std::is_nothrow_invocable_v<FUnary, const TInputError&>
269 && std::is_nothrow_move_constructible_v<error<function_result_type>>
270 && std::is_nothrow_copy_constructible_v<TInputValue>;
271 };
272};
273
274template <typename TInputValue, typename FUnary>
275struct result_map_error_result<const result<TInputValue, void>&,
276 FUnary,
277 std::void_t<std::invoke_result_t<FUnary>>
278 >
279{
280 struct info
281 {
282 using function_result_type = std::invoke_result_t<FUnary>;
283
284 using type = result<TInputValue, function_result_type>;
285
286 static constexpr bool is_noexcept = std::is_nothrow_invocable_v<FUnary>
287 && std::is_nothrow_move_constructible_v<error<function_result_type>>
288 && std::is_nothrow_copy_constructible_v<TInputValue>;
289 };
290};
291
292template <typename TInputValue, typename TInputError, typename FUnary>
293struct result_map_error_result<result<TInputValue, TInputError>&&,
294 FUnary,
295 std::void_t<std::invoke_result_t<FUnary, TInputError&&>>
296 >
297{
298 struct info
299 {
300 using function_result_type = std::invoke_result_t<FUnary, TInputError&&>;
301
302 using type = result<TInputValue, function_result_type>;
303
304 static constexpr bool is_noexcept = std::is_nothrow_invocable_v<FUnary, TInputError&&>
305 && std::is_nothrow_move_constructible_v<error<function_result_type>>
306 && std::is_nothrow_move_constructible_v<TInputValue>;
307 };
308};
309
310template <typename TInputValue, typename FUnary>
311struct result_map_error_result<result<TInputValue, void>&&,
312 FUnary,
313 std::void_t<std::invoke_result_t<FUnary>>
314 >
315{
316 struct info
317 {
318 using function_result_type = std::invoke_result_t<FUnary>;
319
320 using type = result<TInputValue, function_result_type>;
321
322 static constexpr bool is_noexcept = std::is_nothrow_invocable_v<FUnary>
323 && std::is_nothrow_move_constructible_v<error<function_result_type>>
324 && std::is_nothrow_move_constructible_v<TInputValue>;
325 };
326};
327
328template <typename TResult, typename FUnary>
329using result_map_error_result_info = typename result_map_error_result<TResult, FUnary>::info;
330
331template <typename TResult, typename FUnary, typename Enabler = void>
332struct result_recover_result
333{ };
334
335template <typename TInputValue, typename TInputError, typename FUnary>
336struct result_recover_result<const result<TInputValue, TInputError>&,
337 FUnary,
338 std::void_t<std::invoke_result_t<FUnary, const TInputError&>>
339 >
340{
341 struct info
342 {
343 using function_result_type = std::invoke_result_t<FUnary, const TInputError&>;
344
345 static_assert(is_template_of_v<std::optional, function_result_type>,
346 "Function provided to `recover` must return a `jsonv::optional`"
347 );
348
349 using function_result_value_type = typename function_result_type::value_type;
350
351 using value_type = std::common_type_t<TInputValue, function_result_value_type>;
352
353 using type = result<value_type, TInputError>;
354
355 static constexpr bool is_noexcept = std::is_nothrow_invocable_v<FUnary, const TInputError&>
356 && std::is_nothrow_constructible_v<value_type, function_result_value_type&&>
357 && std::is_nothrow_constructible_v<value_type, const TInputValue&>
358 && std::is_nothrow_copy_constructible_v<TInputError>;
359 };
360};
361
362template <typename TInputValue, typename FUnary>
363struct result_recover_result<const result<TInputValue, void>&,
364 FUnary,
365 std::void_t<std::invoke_result_t<FUnary>>
366 >
367{
368 struct info
369 {
370 using function_result_type = std::invoke_result_t<FUnary>;
371
372 static_assert(is_template_of_v<std::optional, function_result_type>,
373 "Function provided to `recover` must return a `jsonv::optional`"
374 );
375
376 using function_result_value_type = typename function_result_type::value_type;
377
378 using value_type = std::common_type_t<TInputValue, function_result_value_type>;
379
380 using type = result<value_type, void>;
381
382 static constexpr bool is_noexcept = std::is_nothrow_invocable_v<FUnary>
383 && std::is_nothrow_constructible_v<value_type, function_result_value_type&&>
384 && std::is_nothrow_constructible_v<value_type, const TInputValue&>;
385 };
386};
387
388template <typename TInputError, typename FUnary>
389struct result_recover_result<const result<void, TInputError>&,
390 FUnary,
391 std::void_t<std::invoke_result_t<FUnary, const TInputError&>>
392 >
393{
394 struct info
395 {
396 using function_result_type = std::invoke_result_t<FUnary, const TInputError&>;
397
398 static_assert(std::is_same_v<optional<ok<void>>, function_result_type>,
399 "Function provided to `recover` with `value_type` of `void` must return a "
400 "`jsonv::optional<jsonv::ok<void>>`"
401 );
402
403 using type = result<void, TInputError>;
404
405 static constexpr bool is_noexcept = std::is_nothrow_invocable_v<FUnary, const TInputError&>
406 && std::is_nothrow_copy_constructible_v<TInputError>;
407 };
408};
409
410template <typename FUnary>
411struct result_recover_result<const result<void, void>&,
412 FUnary,
413 std::void_t<std::invoke_result_t<FUnary>>
414 >
415{
416 struct info
417 {
418 using function_result_type = std::invoke_result_t<FUnary>;
419
420 static_assert(std::is_same_v<optional<ok<void>>, function_result_type>,
421 "Function provided to `recover` with `value_type` of `void` must return a "
422 "`jsonv::optional<jsonv::ok<void>>`"
423 );
424
425 using type = result<void, void>;
426
427 static constexpr bool is_noexcept = std::is_nothrow_invocable_v<FUnary>;
428 };
429};
430
431template <typename TResult, typename FUnary>
432using result_recover_result_info = typename result_recover_result<TResult, FUnary>::info;
433
434template <typename TResult, typename FUnary, typename Enabler = void>
435struct result_recover_flat_result
436{ };
437
438template <typename TInputValue, typename TInputError, typename FUnary>
439struct result_recover_flat_result<const result<TInputValue, TInputError>&,
440 FUnary,
441 std::void_t<std::invoke_result_t<FUnary, const TInputError&>>
442 >
443{
444 struct info
445 {
446 using source_type = result<TInputValue, TInputError>;
447
448 using function_result_type = std::invoke_result_t<FUnary, const TInputError&>;
449
450 static_assert(is_template_of_v<result, function_result_type>,
451 "Function provided to `recover_flat` must return a `jsonv::result`"
452 );
453
454 using value_type = std::common_type_t<TInputValue, typename function_result_type::value_type>;
455
456 using type = result<value_type, typename function_result_type::error_type>;
457
458 static constexpr bool is_noexcept = std::is_nothrow_invocable_v<FUnary, const TInputError&>
459 && std::is_nothrow_constructible_v<type, function_result_type&&>;
460 };
461};
462
463template <typename TInputValue, typename FUnary>
464struct result_recover_flat_result<const result<TInputValue, void>&,
465 FUnary,
466 std::void_t<std::invoke_result_t<FUnary>>
467 >
468{
469 struct info
470 {
471 using source_type = result<TInputValue, void>;
472
473 using function_result_type = std::invoke_result_t<FUnary>;
474
475 static_assert(is_template_of_v<result, function_result_type>,
476 "Function provided to `recover_flat` must return a `jsonv::result`"
477 );
478
479 using value_type = std::common_type_t<TInputValue, typename function_result_type::value_type>;
480
481 using type = result<value_type, typename function_result_type::error_type>;
482
483 static constexpr bool is_noexcept = std::is_nothrow_invocable_v<FUnary>
484 && std::is_nothrow_constructible_v<type, function_result_type&&>;
485 };
486};
487
488template <typename TInputValue, typename TInputError, typename FUnary>
489struct result_recover_flat_result<result<TInputValue, TInputError>&&,
490 FUnary,
491 std::void_t<std::invoke_result_t<FUnary, TInputError&&>>
492 >
493{
494 struct info
495 {
496 using source_type = result<TInputValue, TInputError>;
497
498 using function_result_type = std::invoke_result_t<FUnary, TInputError&&>;
499
500 static_assert(is_template_of_v<result, function_result_type>,
501 "Function provided to `recover_flat` must return a `jsonv::result`"
502 );
503
504 using value_type = std::common_type_t<TInputValue, typename function_result_type::value_type>;
505
506 using type = result<value_type, typename function_result_type::error_type>;
507
508 static constexpr bool is_noexcept = std::is_nothrow_invocable_v<FUnary, TInputError&&>
509 && std::is_nothrow_constructible_v<type, function_result_type&&>;
510 };
511};
512
513template <typename TInputValue, typename FUnary>
514struct result_recover_flat_result<result<TInputValue, void>&&,
515 FUnary,
516 std::void_t<std::invoke_result_t<FUnary>>
517 >
518{
519 struct info
520 {
521 using source_type = result<TInputValue, void>;
522
523 using function_result_type = std::invoke_result_t<FUnary>;
524
525 static_assert(is_template_of_v<result, function_result_type>,
526 "Function provided to `recover_flat` must return a `jsonv::result`"
527 );
528
529 using value_type = std::common_type_t<TInputValue, typename function_result_type::value_type>;
530
531 using type = result<value_type, typename function_result_type::error_type>;
532
533 static constexpr bool is_noexcept = std::is_nothrow_invocable_v<FUnary>
534 && std::is_nothrow_constructible_v<type, function_result_type&&>;
535 };
536};
537
538template <typename TResult, typename FUnary>
539using result_recover_flat_result_info = typename result_recover_flat_result<TResult, FUnary>::info;
540
541}
542
543namespace jsonv
544{
545
546/// Describes the state of a \c result instance.
547enum class result_state : unsigned char
548{
549 empty,
550 ok,
551 error,
552};
553
554JSONV_PUBLIC std::ostream& operator<<(std::ostream&, const result_state&);
555
556/// A wrapper type for creating a \c result with \c result_state::ok. It is implicitly convertible to any \c result
557/// with a convertible-to \c value_type.
558///
559/// \code
560/// result<int, some_complicated_error_type> foo()
561/// {
562/// return ok(1);
563/// }
564/// \endcode
565///
566/// \see result
567template <typename TValue>
569{
570public:
571 /// \see result::value_type
573
574public:
575 /// Create an instance from the given \a args.
576 template <typename... TArgs>
577 explicit constexpr ok(TArgs&&... args) noexcept(std::is_nothrow_constructible_v<value_type, TArgs...>) :
578 _value(std::forward<TArgs>(args)...)
579 { }
580
581 /// \{
582 /// Get the value inside of this instance.
583 const value_type& get() const& noexcept { return _value; }
584 value_type& get() & noexcept { return _value; }
585 value_type&& get() && noexcept { return std::move(_value); }
586 /// \}
587
588private:
589 value_type _value;
590};
591
592/// The \c void specialization of \c ok is meant for the creation of \c result instances with \c value_type of \c void.
593///
594/// \code
595/// result<void> foo()
596/// {
597/// return ok{};
598/// }
599/// \endcode
600///
601/// \see result
602template <>
603class ok<void> final
604{
605public:
606 explicit constexpr ok() noexcept = default;
607};
608
609template <typename TValue>
611
612template <typename... TArgs>
613ok(TArgs...) -> ok<std::enable_if_t<sizeof...(TArgs) == 0, void>>;
614
615/// A wrapper type for creating a \c result with \c result_state::error. It is implicitly convertible to any \c result
616/// with a convertible-to \c error_type.
617///
618/// \code
619/// result<int, std::string> foo()
620/// {
621/// return error("foo went bad");
622/// }
623/// \endcode
624template <typename TError>
626{
627public:
628 /// \see result::error_type
630
631public:
632 template <typename... TArgs>
633 explicit constexpr error(TArgs&&... args) noexcept(std::is_nothrow_constructible_v<error_type, TArgs...>) :
634 _error(std::forward<TArgs>(args)...)
635 { }
636
637 const error_type& get() const& { return _error; }
638 error_type& get() & { return _error; }
639 error_type&& get() && { return std::move(_error); }
640
641private:
642 error_type _error;
643};
644
645/// The \c void specialization of \c error is meant for the creation of \c result instances with \c error_type of
646/// \c void.
647///
648/// \code
649/// result<int, void> foo()
650/// {
651/// return error{};
652/// }
653/// \endcode
654///
655/// \see result
656template <>
657class error<void> final
658{
659public:
660 explicit constexpr error() noexcept = default;
661};
662
663template <typename TError>
665
666template <typename... TArgs>
667error(TArgs...) -> error<std::enable_if_t<sizeof...(TArgs) == 0>>;
668
669/// Thrown when an attempt to access the contents of a \c result was illegal. For example, calling \c value when the
670/// \c result has \c result_state::error.
671class JSONV_PUBLIC bad_result_access : public std::logic_error
672{
673public:
674 /// Create an exception with the given \a description.
675 explicit bad_result_access(const std::string& description) noexcept;
676
677 /// Create an exception with a description noting the \a op_name must be performed in the \a expected state, but the
678 /// \c result had a different \a actual state.
680
681 /// Create an exception with a description noting the \a op_name must not be performed in the \a unexpected state.
683
685};
686
687/// The set of \c result operations which depend on the type of \c TValue. This is how \c result gets operations like
688/// \c value_or, \c operator*, and \c operator->. If \c TValue is \c void, those operations are not available.
689///
690/// \see result
693{
694 using self_type = TSelf;
695
696public:
697 /// See \ref result::value_type.
699
700 /// \{
701 /// Get the value inside of this result if it is \c result_state::ok.
702 ///
703 /// \throws bad_result_access if \c state is not \c result_state::ok.
704 const value_type& value() const& { return get_raw("value"); }
705 value_type& value() & { return get_raw("value"); }
706 value_type&& value() && { return std::move(get_raw("value")); }
707 /// \}
708
709 /// \{
710 /// Get the value inside of this result if it is \c result_state::ok and returns \a recovery_value in other cases.
711 template <typename UValue>
713 noexcept( std::is_nothrow_copy_constructible_v<value_type>
714 && std::is_nothrow_constructible_v<value_type, UValue>
715 )
716 {
717 auto& self = static_cast<self_type&>(*this);
718 return self.is_ok() ? value() : static_cast<value_type>(std::forward<UValue>(recovery_value));
719 }
720
721 template <typename UValue>
722 constexpr value_type value_or(UValue&& recovery_value) &&
723 noexcept( std::is_nothrow_move_constructible_v<value_type>
724 && std::is_nothrow_constructible_v<value_type, UValue>
725 )
726 {
727 auto& self = static_cast<self_type&>(*this);
728 return self.is_ok() ? std::move(value()) : static_cast<value_type>(std::forward<UValue>(recovery_value));
729 }
730 /// \}
731
732 /// \{
733 /// Get the value inside of this result if it is \c result_state::ok.
734 ///
735 /// \throws bad_result_access if \c state is not \c result_state::ok.
736 const value_type& operator*() const& { return get_raw("operator*"); }
737 value_type& operator*() & { return get_raw("operator*"); }
738 value_type&& operator*() && { return std::move(get_raw("operator*")); }
739 /// \}
740
741 /// \{
742 /// Get a pointer to the value inside of this result if it is \c result_state::ok.
743 ///
744 /// \throws bad_result_access if \c state is not \c result_state::ok.
745 const value_type* operator->() const { return &get_raw("operator->"); }
746 value_type* operator->() { return &get_raw("operator->"); }
747 /// \}
748
749private:
750 inline constexpr const value_type& get_raw(const char* op_name) const
751 {
752 const auto& self = static_cast<const self_type&>(*this);
753 self.ensure_state(op_name, result_state::ok);
754 return std::get<self_type::ok_index>(self._storage).get();
755 }
756
757 inline constexpr value_type& get_raw(const char* op_name)
758 {
759 auto& self = static_cast<self_type&>(*this);
760 self.ensure_state(op_name, result_state::ok);
761 return std::get<self_type::ok_index>(self._storage).get();
762 }
763};
764
765template <typename TSelf>
767{
768 using self_type = TSelf;
769
770public:
771 /// \throws bad_result_access if \c state is not \c result_state::ok.
772 void value() const
773 {
774 const auto& self = static_cast<const self_type&>(*this);
775 self.ensure_state("value", result_state::ok);
776 }
777};
778
779/// The set of \c result operations which depend on the type of \c TError.
780///
781/// \see result
782template <typename TSelf, typename TError>
784{
785 using self_type = TSelf;
786
787public:
788 /// See \ref result::error_type.
790
791public:
792 /// \{
793 /// Get the error value inside of this result if it is \c result_state::error.
794 ///
795 /// \throws bad_result_access if \c state is not \c result_state::error.
797 {
798 const auto& self = static_cast<const self_type&>(*this);
799 self.ensure_state("error", result_state::error);
800 return std::get<self_type::error_index>(self._storage).get();
801 }
802
803 error_type& error() &
804 {
805 auto& self = static_cast<self_type&>(*this);
806 self.ensure_state("error", result_state::error);
807 return std::get<self_type::error_index>(self._storage).get();
808 }
809
810 error_type&& error() &&
811 {
812 auto& self = static_cast<self_type&>(*this);
813 self.ensure_state("error", result_state::error);
814 return std::get<self_type::error_index>(std::move(self._storage)).get();
815 }
816 /// \}
817};
818
819template <typename TSelf>
821{
822 using self_type = TSelf;
823
824public:
825 /// \throws bad_result_access if \c state is not \c result_state::error.
826 void error() const
827 {
828 const auto& self = static_cast<const self_type&>(*this);
829 self.ensure_state("error", result_state::error);
830 }
831};
832
833/// \{
834/// Used to inform a \c result constructor that it should create a \c result_state::ok value in-place.
835///
836/// \code
837/// result<std::mutex> mut(in_place_ok);
838/// \endcode
839///
840/// \see result
841/// \see ok
843{ };
844
845static constexpr in_place_ok_t in_place_ok = in_place_ok_t();
846/// \}
847
848/// \{
849/// Used to inform a \c result constructor that it should create a \c result_state::error value in-place.
850///
851/// \see result
852/// \see error
854{ };
855
856static constexpr in_place_error_t in_place_error = in_place_error_t();
857/// \}
858
859/// \brief
860/// A type that represents a value which might be a success (\c is_ok) or a failure (\c is_error). It is similar to
861/// \c std::variant, but additional meaning is attached to the values, even if \c TValue and \c TError are the same
862/// type. Returning a \c result should be seen as an alternative to exception-throwing or the use of \c errno. While it
863/// is legal to throw from code which uses \c result, it is discouraged (see \ref result_error_handling_comparison for
864/// more information).
865///
866/// Instances are transformed through member functions:
867///
868/// - \ref map : Transform an \c ok \c result into another \c ok with a different type.
869/// - \ref map_flat : Transform an \c ok \c result into either an \c ok with a different type or an \c error with the
870/// same type.
871/// - \ref map_error : Transform an \c error \c result into another \c error with a different type.
872/// - \ref recover : Attempt to recover an \c error \c result into an \c ok result with the same type.
873/// - \ref recover_flat : Attempt to recover an \c error \c result into either an \c ok of the same type or an \c error
874/// of a different type.
875///
876/// \dot
877/// digraph result_states {
878/// label = "Result State Transitions";
879///
880/// subgraph cluster_oks {
881/// label = "";
882/// style = "invis";
883///
884/// ok [label="ok<TValue>", shape="rectangle", URL="\ref jsonv::ok"];
885/// result_ok [label="result<TValue, TError>\nstate=ok", shape="rectangle"];
886/// value [label="TValue", shape="rectangle"]
887/// }
888///
889/// subgraph cluster_errors {
890/// label = "";
891/// style = "invis";
892///
893/// error [label="error<TError>", shape="rectangle", URL="\ref jsonv::error"];
894/// result_err [label="result<TValue, TError>\nstate=error", shape="rectangle"];
895/// error_val [label="TError", shape="rectangle"]
896/// }
897///
898/// ok -> result_ok [label="ctor"]
899/// error -> result_err [label="ctor"]
900///
901/// result_ok:sw -> result_ok:nw [label="map(F)" , constraint="true", URL="\ref jsonv::result::map"]
902/// result_err:se -> result_err:ne [label="map_error(F)", constraint="true", URL="\ref jsonv::result::map_error"]
903/// result_ok:ne -> result_err:nw [label="map_flat(F)" , constraint="false", URL="\ref jsonv::result::map_flat"]
904/// result_err:sw -> result_ok:se [label="recover(F)" , constraint="false", URL="\ref jsonv::result::recover"]
905///
906/// result_ok -> value [label="value()", URL="\ref jsonv::result_value_operations::value"]
907/// result_err -> error_val [label="error()", URL="\ref jsonv::result_error_operations::error"]
908/// }
909/// \enddot
910///
911/// A \c result instance is created with the \c ok and \c error helper types from some function performing an operation
912/// which might fail.
913///
914/// \code
915/// enum class math_error { domain, range };
916///
917/// result<double, math_error> sqrt_checked(double x) noexcept
918/// {
919/// if (x >= 0.0)
920/// return ok{ std::sqrt(x) };
921/// else
922/// return error{ math_error::domain };
923/// }
924///
925/// std::ostream& operator<<(std::ostream& os, const result<double, math_error>& r)
926/// {
927/// if (r.is_ok())
928/// {
929/// os << "ok(" << r.value() << ")";
930/// }
931/// else if (r.is_error())
932/// {
933/// os << "error(" << r.error() << ")";
934/// }
935/// else
936/// {
937/// // The default state of result is empty.
938/// os << "empty";
939/// }
940/// }
941///
942/// int main()
943/// {
944/// std::cout << "sqrt(16) = " << sqrt_checked(16) << std::endl;
945/// std::cout << "sqrt(-5) = " << sqrt_checked(-5) << std::endl;
946/// }
947/// \endcode
948///
949/// Program output:
950///
951/// \code{.sh}
952/// sqrt(16) = ok(4)
953/// sqrt(-5) = error(domain)
954/// \endcode
955///
956/// The \c sqrt_checked function will give us \f$ \sqrt{x} \f$ when \f$ x \ge 0 \f$ and a \c math_error::domain error
957/// when \f$ x < 0 \f$. What if we wanted to make a \c sqrt_div_2_checked function to compute \f$ \frac{\sqrt{x}}{2} \f$
958/// using the result of \c sqrt_checked? While we could use \c is_ok to check if we should apply a `/2` operation, this
959/// is easier to do with \ref map.
960///
961/// \code
962/// result<double, math_error> sqrt_div_2_checked(double x) noexcept
963/// {
964/// return sqrt_checked(x)
965/// .map([](double sqrt_x) { return sqrt_x / 2; });
966/// }
967///
968/// int main()
969/// {
970/// std::cout << "sqrt(16) / 2 = " << sqrt_div_2_checked(16) << std::endl;
971/// std::cout << "sqrt(-5) / 2 = " << sqrt_div_2_checked(-5) << std::endl;
972/// }
973/// \endcode
974///
975/// Program output:
976///
977/// \code{.sh}
978/// sqrt(16) / 2 = ok(2)
979/// sqrt(-5) / 2 = error(domain)
980/// \endcode
981///
982/// The function provided to \ref map will not fail, as division by 2 works on all possible \c double values. But what
983/// if we try to compute \f$ \arcsin\sqrt{x} \f$? The \c std::asin function requires input with range
984/// \f$ \left[ -1, 1 \right] \f$, so let's write a checked version of it:
985///
986/// \code
987/// result<double, math_error> asin_checked(double x) noexcept
988/// {
989/// if (x < -1.0 || x > 1.0)
990/// return error{ math_error::domain };
991/// else
992/// return ok{ std::asin(x) };
993/// }
994///
995/// result<double, math_error> asin_sqrt_checked(double x) noexcept
996/// {
997/// return sqrt_checked(x)
998/// .map_flat(asin_checked);
999/// }
1000///
1001/// int main()
1002/// {
1003/// std::cout << "asin(sqrt(16)) = " << asin_sqrt_checked(16) << std::endl;
1004/// std::cout << "asin(sqrt(-5)) = " << asin_sqrt_checked(-5) << std::endl;
1005/// std::cout << "asin(sqrt(0.5)) = " << asin_sqrt_checked(0.5) << std::endl;
1006/// }
1007/// \endcode
1008///
1009/// Program output:
1010///
1011/// \code{.sh}
1012/// asin(sqrt(16)) = error(domain)
1013/// asin(sqrt(-5)) = error(domain)
1014/// asin(sqrt(0.5)) = ok(0.785398)
1015/// \endcode
1016///
1017/// \section result_error_handling_comparison Comparison to Other Error Handling Techniques
1018///
1019/// Use of \c result for communication of operations which might fail is one of many error-handling techniques available
1020/// in C++. It is used in cases where there is no reasonable fallback value to return (in contrast to
1021/// \c jsonv::value::find).
1022///
1023/// Exceptions are a common mechanism to represent error cases, but are not always the ideal choice. While code relying
1024/// on exceptions is fast in the normal case, throwing an exception is much slower. As such, exception-throwing works
1025/// best in situations where errors are relatively rare. In cases where errors occur at a high or unknown rate (such as
1026/// parsing or extraction), techniques that do not pay a massive penalty to communicate errors are preferable.
1027///
1028/// One technique is returning a value (such as a \c bool or a dedicated error code) to indicate operation error. The
1029/// use of the C++ attribute `[[nodiscard]]` helps prevent the common issue of forgetting to check the result.
1030///
1031/// \code
1032/// [[nodiscard]]
1033/// error_type foo(const input_type& input, output_type& output);
1034/// \endcode
1035///
1036/// Unfortunately, this technique only works when \c output_type has a default constructor so calling code can create an
1037/// initial value for \c output. Furthermore, \c output_type must be mutable or assignable-to so implementing code can
1038/// populate \c output. There is also a question as to what should happen if \c output is passed as a non-empty value
1039/// (such as a \c std::vector which already has values). Should that be an error, should \c output be concatenated, or
1040/// should it be overwritten?
1041///
1042/// To address all these questions, we could have \c foo return either an \c optional or \c variant.
1043///
1044/// \code
1045/// std::variant<some_output_type, error_type> foo(const input_type& input);
1046/// \endcode
1047///
1048/// This solves a lot of the issues with the code-returning function. Aside from forcing callers to use the slightly
1049/// awkward \c std::get to access the result, this is close to ideal. Problems arise when either \c output_type or
1050/// \c error_type is \c void, as a \c variant can not store a \c void. In these cases, we could rely on \c optional to
1051/// represent the value or error, with \c nullopt indicating failure when \c error_type is \c void or success when
1052/// \c output_type is \c void. If they are both \c void, use \c bool.
1053///
1054/// 1. `std::variant<output_type, error_type>` in cases where both \c output_type and \c error_type are not \c void
1055/// 2. `std::optional<output_type>` in cases where \c output_type is not \c void, but \c error_type is
1056/// 3. `std::optional<error_type>` in cases where \c output_type is \c void, but \c error_type is not
1057/// 4. `bool` in case where both \c output_type and \c error_type are \c void
1058///
1059/// This all works fine if we are writing a non-generic \c foo, but what if the \c output_type or \c error_type of
1060/// \c foo is dependent on some other generic function? The type signatures for doing this can get a bit unwieldy:
1061///
1062/// \code
1063/// template <typename F>
1064/// std::conditional_t<std::is_void_v<std::invoke_result_t<F>>,
1065/// std::optional<error_code>,
1066/// std::variant<std::invoke_result_t<F>, error_code>
1067/// >
1068/// foo(F&& func);
1069/// \endcode
1070///
1071/// Programmers now have a harder time figuring out what \c foo actually returns. Code relying this also needs to handle
1072/// this strange return type. Their usage is quite different to, as \c std::variant is accessed through the \c std::get
1073/// free function while \c std::optional has a member \c get and `operator bool` overload. The potential for accidental
1074/// misuse is quite high.
1075///
1076/// A \c result is meant to address all of the representational problems with error codes.
1077///
1078/// \code
1079/// template <typename F>
1080/// result<std::invoke_result_t<F>, error_code> foo(F&& func);
1081/// \endcode
1082///
1083/// The signature is quite a bit simpler.
1084///
1085/// JSON Voorhees uses a mixture of error-handling techniques depending on the situation and the nature of the error.
1086/// For cases where errors happen with unknown frequency, a \c result is used.
1087template <typename TValue, typename TError = std::exception_ptr>
1089 public result_value_operations<result<TValue, TError>, TValue>,
1090 public result_error_operations<result<TValue, TError>, TError>
1091{
1093
1094 template <typename, typename>
1095 friend class result;
1096
1097 template <typename, typename>
1098 friend class result_value_operations;
1099
1100 template <typename, typename>
1101 friend class result_error_operations;
1102
1103public:
1104 /// The type this result holds when it is \c result_state::ok. It is allowed to be \c void.
1106
1107 /// The type this result holds when it is \c result_state::error. It is allowed to be \c void.
1109
1110public:
1111 /// Construct an empty result.
1112 constexpr result() noexcept :
1113 _storage()
1114 { }
1115
1116 /// Construct a result with \c result_state::ok from the provided \a args.
1117 template <typename... TArgs>
1118 explicit constexpr result(in_place_ok_t, TArgs&&... args)
1119 noexcept(std::is_nothrow_constructible_v<value_type, TArgs...>) :
1120 _storage(std::in_place_index<ok_index>, std::forward<TArgs>(args)...)
1121 { }
1122
1123 /// Construct a result with \c result_state::ok from the contents of \a value. This constructor is only enabled when
1124 /// \c TOkValue is an \ref ok with a \c value_type convertible to this result's \c value_type.
1125 template <typename TOkValue>
1126 constexpr result(TOkValue&& value,
1127 std::enable_if_t< detail::is_template_of_v<ok, std::decay_t<TOkValue>>
1128 && std::is_convertible_v<typename TOkValue::value_type, value_type>
1129 >* = nullptr
1130 ) :
1131 result(in_place_ok, std::forward<TOkValue>(value).get())
1132 { }
1133
1134 /// Construct a result with \c result_state::ok from a `ok<void>`. This constructor is only enabled when \c TOkValue
1135 /// is an `ok<void>` and this result has \c value_type of \c void.
1136 template <typename TOkValue>
1138 std::enable_if_t< std::is_same_v<ok<void>, std::decay_t<TOkValue>>
1139 && std::is_void_v<value_type>
1140 >* = nullptr
1141 )
1142 noexcept:
1143 result(in_place_ok)
1144 { }
1145
1146 /// Construct a result with \c result_state::ok from the provided \a value. This constructor is only enabled when
1147 /// \c UValue is convertible to \c value_type and \c value_type is distinct from \c error_type.
1148 template <typename UValue>
1149 constexpr result(UValue&& value,
1150 std::enable_if_t< std::is_convertible_v<UValue, value_type>
1151 && !std::is_same_v<value_type, error_type>
1152 >* = nullptr
1153 )
1155 result(in_place_ok, std::forward<UValue>(value))
1156 { }
1157
1158 /// Construct a result with \c result_state::error from the provided \a args.
1159 template <typename... TArgs>
1160 explicit constexpr result(in_place_error_t, TArgs&&... args)
1161 noexcept(std::is_nothrow_constructible_v<error_type, TArgs...>) :
1162 _storage(std::in_place_index<error_index>, std::forward<TArgs>(args)...)
1163 { }
1164
1165 /// Construct a result with \c result_state::error from the contents of \a error_value. This constructor is only
1166 /// enabled when \c TErrorValue is an \ref error with a \c error_type convertible to this result's \c error_type.
1167 template <typename TErrorValue>
1169 std::enable_if_t< detail::is_template_of_v<jsonv::error, TErrorValue>
1170 && std::is_convertible_v<typename TErrorValue::error_type, error_type>
1171 >* = nullptr
1172 ) :
1173 result(in_place_error, std::forward<TErrorValue>(error_value).get())
1174 { }
1175
1176 /// Construct a result with \c result_state::error from an `error<void>`. This constructor is only enabled when
1177 /// \c TErrorValue is an `error<void>` and this result has \c error_type of \c void.
1178 template <typename TErrorValue>
1179 constexpr result(TErrorValue&&,
1180 std::enable_if_t< std::is_same_v<jsonv::error<void>, std::decay_t<TErrorValue>>
1181 && std::is_void_v<error_type>
1182 >* = nullptr
1183 )
1184 noexcept:
1185 result(in_place_error)
1186 { }
1187
1188 /// Result instances are copy-constructible iff \c value_type and \c error_type are.
1189 constexpr result(const result&) = default;
1190
1191 /// Result instances are move-constructible iff \c value_type and \c error_type are.
1193
1194 /// Result instances are copy-assignable iff \c value_type and \c error_type are.
1196
1197 /// Result instances are move-assignable iff \c value_type and \c error_type are.
1199
1200 /// Converting constructor from a \c result with a convertible \c value_type and \c error_type.
1205 >
1206 >
1208 {
1209 switch (src.state())
1210 {
1211 case result_state::ok:
1212 if constexpr (std::is_void_v<value_type>)
1213 {
1214 _storage = ok{};
1215 }
1216 else
1217 {
1218 _storage = ok<value_type>(std::get<ok_index>(src._storage).get());
1219 }
1220 break;
1221 case result_state::error:
1222 if constexpr (std::is_void_v<error_type>)
1223 {
1224 _storage = jsonv::error{};
1225 }
1226 else
1227 {
1228 _storage = jsonv::error<error_type>(std::get<error_index>(src._storage).get());
1229 }
1230 break;
1231 case result_state::empty:
1232 // Do nothing -- we default-construct to empty
1233 break;
1234 }
1235 }
1236
1237 /// Converting constructor from a \c result with a convertible \c value_type and \c error_type.
1238 template <typename UValue,
1239 typename UError,
1240 typename = std::enable_if_t< std::is_convertible_v<UValue, value_type>
1241 && std::is_convertible_v<UError, error_type>
1242 >
1243 >
1245 {
1246 auto clear_src = detail::on_scope_exit([&] { src.reset(); });
1247 switch (src.state())
1248 {
1249 case result_state::ok:
1250 if constexpr (std::is_void_v<value_type>)
1251 {
1252 _storage = ok{};
1253 }
1254 else
1255 {
1256 _storage = ok<value_type>(std::get<ok_index>(std::move(src._storage)).get());
1257 }
1258 break;
1259 case result_state::error:
1260 if constexpr (std::is_void_v<error_type>)
1261 {
1262 _storage = jsonv::error{};
1263 }
1264 else
1265 {
1266 _storage = jsonv::error<error_type>(std::get<error_index>(std::move(src._storage)).get());
1267 }
1268 break;
1269 case result_state::empty:
1270 // Do nothing -- we default-construct to empty
1271 break;
1272 }
1273 }
1274
1275 /// Get the state of this object.
1277 {
1278 if (_storage.index() == ok_index)
1279 return result_state::ok;
1280 else if (_storage.index() == error_index)
1281 return result_state::error;
1282 else
1283 return result_state::empty;
1284 }
1285
1286 /// Check that this result has \c state of \c result_state::ok.
1287 constexpr bool is_ok() const noexcept
1288 {
1289 return state() == result_state::ok;
1290 }
1291
1292 /// Check that this result \c is_ok.
1293 constexpr explicit operator bool() const noexcept
1294 {
1295 return is_ok();
1296 }
1297
1298 /// Check that this result has \c state of \c result_state::error.
1299 constexpr bool is_error() const noexcept
1300 {
1301 return state() == result_state::error;
1302 }
1303
1304 /// Reset the contents of this instance to \c result_state::empty.
1305 void reset()
1307 {
1308 _storage = std::monostate{};
1309 }
1310
1311 /// \{
1312 /// Apply \a transform to a \c result_state::ok value, returning the result of the transformation in another result.
1313 /// If the instance is \c result_state::error, the error is copied into the returned value. If the instance is
1314 /// \c result_state::empty, an empty instance will be returned.
1315 ///
1316 /// This function is \c noexcept when calling \a transform is \c noexcept, the result is \c noexcept
1317 /// move-constructible, and the \c error_type is \c noexcept copy-constructible. It is highly encouraged to not
1318 /// throw from your \a transform function.
1319 ///
1320 /// \tparam FUnary `R (*)(const value_type&)` if \c value_type is not \c void or `R (*)()` if it is. The inferred
1321 /// type \c R will be the \c value_type of the returned \c result (which can be \c void).
1322 ///
1323 /// \dot
1324 /// digraph result_map_xfrms {
1325 /// rankdir = "LR";
1326 ///
1327 /// subgraph cluster_init {
1328 /// label = "";
1329 /// style = "invis";
1330 ///
1331 /// result_ok_1 [label="result<TValue, TError>\nstate=ok", shape="rectangle"];
1332 /// result_err_1 [label="result<TValue, TError>\nstate=error", shape="rectangle"];
1333 /// }
1334 ///
1335 /// subgraph cluster_fin {
1336 /// label = "";
1337 /// style = "invis";
1338 ///
1339 /// result_ok_2 [label="result<UValue, TError>\nstate=ok", shape="rectangle"];
1340 /// result_err_2 [label="result<UValue, TError>\nstate=error", shape="rectangle"];
1341 /// }
1342 ///
1343 /// result_ok_1 -> result_ok_2 [label="transform(const TValue&) -> UValue"];
1344 /// result_err_1 -> result_err_2 [label="transform not called"];
1345 /// }
1346 /// \enddot
1347 template <typename FUnary, typename ResultInfo = detail::result_map_result_info<const self_type&, FUnary>>
1348 typename ResultInfo::type map(FUnary&& transform) const& noexcept(ResultInfo::is_noexcept)
1349 {
1350 using return_type = typename ResultInfo::type;
1351
1352 switch (state())
1353 {
1354 case result_state::ok:
1355 if constexpr (std::is_void_v<value_type>)
1356 {
1357 if constexpr (std::is_void_v<typename return_type::value_type>)
1358 {
1359 std::forward<FUnary>(transform)();
1360 return return_type(in_place_ok);
1361 }
1362 else
1363 {
1364 return return_type(in_place_ok, std::forward<FUnary>(transform)());
1365 }
1366 }
1367 else
1368 {
1369 if constexpr (std::is_void_v<typename return_type::value_type>)
1370 {
1371 std::forward<FUnary>(transform)(std::get<ok_index>(_storage).get());
1372 return return_type(in_place_ok);
1373 }
1374 else
1375 {
1376 return return_type(in_place_ok,
1377 std::forward<FUnary>(transform)(std::get<ok_index>(_storage).get())
1378 );
1379 }
1380 }
1381 case result_state::error:
1382 return return_type(in_place_error, std::get<error_index>(_storage));
1383 case result_state::empty:
1384 default:
1385 return return_type();
1386 }
1387 }
1388
1389 /// Apply \a transform to a \c result_state::ok value, returning the result of the transformation in another result.
1390 /// If the instance is \c result_state::error, the error is moved into the returned value. If the instance is
1391 /// \c result_state::empty, an empty instance will be returned.
1392 ///
1393 /// This function is \c noexcept when calling \a transform is \c noexcept, the result is \c noexcept
1394 /// move-constructible, and the \c error_type is \c noexcept move-constructible. It is highly encouraged to not
1395 /// throw from your \a transform function.
1396 ///
1397 /// \tparam FUnary `R (*)(value_type&&)` if \c value_type is not \c void or `R (*)()` if it is. The inferred type
1398 /// \c R will be the \c value_type of the returned \c result (which can be \c void).
1399 ///
1400 /// \dot
1401 /// digraph result_map_xfrms {
1402 /// rankdir = "LR";
1403 ///
1404 /// subgraph cluster_init {
1405 /// label = "";
1406 /// style = "invis";
1407 ///
1408 /// result_ok_1 [label="result<TValue, TError>\nstate=ok", shape="rectangle"];
1409 /// result_err_1 [label="result<TValue, TError>\nstate=error", shape="rectangle"];
1410 /// }
1411 ///
1412 /// subgraph cluster_fin {
1413 /// label = "";
1414 /// style = "invis";
1415 ///
1416 /// result_ok_2 [label="result<UValue, TError>\nstate=ok", shape="rectangle"];
1417 /// result_err_2 [label="result<UValue, TError>\nstate=error", shape="rectangle"];
1418 /// }
1419 ///
1420 /// result_ok_1 -> result_ok_2 [label="transform(TValue&&) -> UValue"];
1421 /// result_err_1 -> result_err_2 [label="transform not called"];
1422 /// }
1423 /// \enddot
1424 template <typename FUnary, typename ResultInfo = detail::result_map_result_info<self_type&&, FUnary>>
1425 typename ResultInfo::type map(FUnary&& transform) && noexcept(ResultInfo::is_noexcept)
1426 {
1427 using return_type = typename ResultInfo::type;
1428
1429 auto clear_me = detail::on_scope_exit([&] { reset(); });
1430
1431 switch (state())
1432 {
1433 case result_state::ok:
1434 if constexpr (std::is_void_v<value_type>)
1435 {
1436 if constexpr (std::is_void_v<typename return_type::value_type>)
1437 {
1438 std::forward<FUnary>(transform)();
1439 return return_type(in_place_ok);
1440 }
1441 else
1442 {
1443 return return_type(in_place_ok, std::forward<FUnary>(transform)());
1444 }
1445 }
1446 else
1447 {
1448 if constexpr (std::is_void_v<typename return_type::value_type>)
1449 {
1450 std::forward<FUnary>(transform)(std::get<ok_index>(std::move(_storage)).get());
1451 return return_type(in_place_ok);
1452 }
1453 else
1454 {
1455 return return_type(in_place_ok,
1456 std::forward<FUnary>(transform)(std::get<ok_index>(std::move(_storage)).get())
1457 );
1458 }
1459 }
1460 case result_state::error:
1461 return return_type(in_place_error, std::get<error_index>(std::move(_storage)));
1462 case result_state::empty:
1463 default:
1464 return return_type();
1465 }
1466 }
1467 /// \}
1468
1469 /// \{
1470 /// Apply \a transform to a \c result_state::ok value, returning the result of the transformation, which itself is
1471 /// a \c result. If the instance is \c result_state::error, the error is copied into the returned value. If the
1472 /// instance is \c result_state::empty, an empty instance will be returned.
1473 ///
1474 /// This function is \c noexcept when calling \a transform is \c noexcept, the result is \c noexcept
1475 /// move-constructible, and the \c error_type of the returned type is \c noexcept constructible from this instance's
1476 /// \c error_type. It is highly encouraged to not throw from your \a transform function.
1477 ///
1478 /// \tparam FUnary `result<R, E> (*)(const value_type&)` if \c value_type is not \c void or `result<R, E> (*)()` if
1479 /// it is. Type \c E in the result must have common type with \c error_type.
1480 ///
1481 /// \dot
1482 /// digraph result_map_flat_xfrms {
1483 /// rankdir = "LR";
1484 ///
1485 /// subgraph cluster_init {
1486 /// label = "";
1487 /// style = "invis";
1488 ///
1489 /// result_ok_1 [label="result<TValue, TError>\nstate=ok", shape="rectangle"];
1490 /// result_err_1 [label="result<TValue, TError>\nstate=error", shape="rectangle"];
1491 /// }
1492 ///
1493 /// subgraph cluster_fin {
1494 /// label = "";
1495 /// style = "invis";
1496 ///
1497 /// result_ok_2 [label="result<UValue, UError>\nstate=ok", shape="rectangle"];
1498 /// result_err_2 [label="result<UValue, UError>\nstate=error", shape="rectangle"];
1499 /// }
1500 ///
1501 /// result_ok_1 -> result_ok_2 [label="transform(const TValue&) returns ok(UValue)"];
1502 /// result_ok_1 -> result_err_2 [label="transform(const TValue&) returns error(UError)"];
1503 /// result_err_1 -> result_err_2 [label="transform not called"];
1504 /// }
1505 /// \enddot
1506 template <typename FUnary, typename ResultInfo = detail::result_map_flat_result_info<const self_type&, FUnary>>
1507 typename ResultInfo::type
1508 map_flat(FUnary&& transform) const& noexcept(ResultInfo::is_noexcept)
1509 {
1510 using return_type = typename ResultInfo::type;
1511
1512 switch (state())
1513 {
1514 case result_state::ok:
1515 if constexpr (std::is_void_v<value_type>)
1516 {
1517 return std::forward<FUnary>(transform)();
1518 }
1519 else
1520 {
1521 return std::forward<FUnary>(transform)(std::get<ok_index>(_storage).get());
1522 }
1523 case result_state::error:
1524 return return_type(in_place_error, std::get<error_index>(_storage));
1525 case result_state::empty:
1526 default:
1527 return return_type();
1528 }
1529 }
1530
1531 /// Apply \a transform to a \c result_state::ok value, returning the result of the transformation, which itself is
1532 /// a \c result. If the instance is \c result_state::error, the error is moved into the returned value. If the
1533 /// instance is \c result_state::empty, an empty instance will be returned.
1534 ///
1535 /// This function is \c noexcept when calling \a transform is \c noexcept, the result is \c noexcept
1536 /// move-constructible, and the \c error_type of the returned type is \c noexcept constructible from this instance's
1537 /// \c error_type. It is highly encouraged to not throw from your \a transform function.
1538 ///
1539 /// \tparam FUnary `result<R, E> (*)(value_type&&)` if \c value_type is not \c void or `result<R, E> (*)()` if it
1540 /// is. Type \c E in the result must have common type with \c error_type.
1541 ///
1542 /// \dot
1543 /// digraph result_map_flat_xfrms {
1544 /// rankdir = "LR";
1545 ///
1546 /// subgraph cluster_init {
1547 /// label = "";
1548 /// style = "invis";
1549 ///
1550 /// result_ok_1 [label="result<TValue, TError>\nstate=ok", shape="rectangle"];
1551 /// result_err_1 [label="result<TValue, TError>\nstate=error", shape="rectangle"];
1552 /// }
1553 ///
1554 /// subgraph cluster_fin {
1555 /// label = "";
1556 /// style = "invis";
1557 ///
1558 /// result_ok_2 [label="result<UValue, UError>\nstate=ok", shape="rectangle"];
1559 /// result_err_2 [label="result<UValue, UError>\nstate=error", shape="rectangle"];
1560 /// }
1561 ///
1562 /// result_ok_1 -> result_ok_2 [label="transform(TValue&&) returns ok(UValue)"];
1563 /// result_ok_1 -> result_err_2 [label="transform(TValue&&) returns error(UError)"];
1564 /// result_err_1 -> result_err_2 [label="transform not called"];
1565 /// }
1566 /// \enddot
1567 template <typename FUnary, typename ResultInfo = detail::result_map_flat_result_info<self_type&&, FUnary>>
1568 typename ResultInfo::type
1569 map_flat(FUnary&& transform) && noexcept(ResultInfo::is_noexcept)
1570 {
1571 using return_type = typename ResultInfo::type;
1572
1573 auto clear_me = detail::on_scope_exit([&] { reset(); });
1574 switch (state())
1575 {
1576 case result_state::ok:
1577 if constexpr (std::is_void_v<value_type>)
1578 {
1579 return std::forward<FUnary>(transform)();
1580 }
1581 else
1582 {
1583 return std::forward<FUnary>(transform)(std::get<ok_index>(std::move(_storage)).get());
1584 }
1585 case result_state::error:
1586 return return_type(in_place_error, std::get<error_index>(std::move(_storage)));
1587 case result_state::empty:
1588 default:
1589 return return_type();
1590 }
1591 }
1592 /// \}
1593
1594 /// \{
1595 /// Apply \a transform to a \c result_state::error value, returning the result of the transformation in another
1596 /// result. If the instance is \c result_state::ok, the value is copied into the returned value. If the instance is
1597 /// \c result_state::empty, an empty instance will be returned.
1598 ///
1599 /// This function is \c noexcept when calling \a transform is \c noexcept, the result is \c noexcept
1600 /// move-constructible, and the \c value_type is \c noexcept copy-constructible. It is highly encouraged to not
1601 /// throw from your \a transform function.
1602 ///
1603 /// \tparam FUnary `E (*)(const error_type&)` if \c error_type is not \c void or `E (*)()` if it is. The inferred
1604 /// type \c E will be the \c error_type of the returned \c result (which can be \c void).
1605 ///
1606 /// \dot
1607 /// digraph result_map_error_xfrms {
1608 /// rankdir = "LR";
1609 ///
1610 /// subgraph cluster_init {
1611 /// label = "";
1612 /// style = "invis";
1613 ///
1614 /// result_ok_1 [label="result<TValue, TError>\nstate=ok", shape="rectangle"];
1615 /// result_err_1 [label="result<TValue, TError>\nstate=error", shape="rectangle"];
1616 /// }
1617 ///
1618 /// subgraph cluster_fin {
1619 /// label = "";
1620 /// style = "invis";
1621 ///
1622 /// result_ok_2 [label="result<TValue, UError>\nstate=ok", shape="rectangle"];
1623 /// result_err_2 [label="result<TValue, UError>\nstate=error", shape="rectangle"];
1624 /// }
1625 ///
1626 /// result_ok_1 -> result_ok_2 [label="transform not called"];
1627 /// result_err_1 -> result_err_2 [label="transform(const TError&) -> UError"];
1628 /// }
1629 /// \enddot
1630 template <typename FUnary, typename ResultInfo = detail::result_map_error_result_info<const self_type&, FUnary>>
1631 typename ResultInfo::type
1632 map_error(FUnary&& transform) const& noexcept(ResultInfo::is_noexcept)
1633 {
1634 using return_type = typename ResultInfo::type;
1635
1636 switch (state())
1637 {
1638 case result_state::ok:
1639 return return_type(in_place_ok, std::get<ok_index>(_storage));
1640 case result_state::error:
1641 if constexpr (std::is_void_v<error_type>)
1642 {
1643 if constexpr (std::is_void_v<typename return_type::error_type>)
1644 {
1645 std::forward<FUnary>(transform)();
1646 return return_type(in_place_error);
1647 }
1648 else
1649 {
1650 return return_type(in_place_error, std::forward<FUnary>(transform)());
1651 }
1652 }
1653 else
1654 {
1655 if constexpr (std::is_void_v<typename return_type::error_type>)
1656 {
1657 std::forward<FUnary>(transform)(std::get<error_index>(_storage).get());
1658 return return_type(in_place_error);
1659 }
1660 else
1661 {
1662 return return_type(in_place_error,
1663 std::forward<FUnary>(transform)(std::get<error_index>(_storage).get())
1664 );
1665 }
1666 }
1667 case result_state::empty:
1668 default:
1669 return return_type();
1670 }
1671 }
1672
1673 /// Apply \a transform to a \c result_state::error value, returning the result of the transformation in another
1674 /// result. If the instance is \c result_state::ok, the value is moved into the returned value. If the instance is
1675 /// \c result_state::empty, an empty instance will be returned.
1676 ///
1677 /// This function is \c noexcept when calling \a transform is \c noexcept, the result is \c noexcept
1678 /// move-constructible, and the \c value_type is \c noexcept move-constructible. It is highly encouraged to not
1679 /// throw from your \a transform function.
1680 ///
1681 /// \tparam FUnary `E (*)(error_type&&)` if \c error_type is not \c void or `E (*)()` if it is. The inferred type
1682 /// \c E will be the \c error_type of the returned \c result (which can be \c void).
1683 ///
1684 /// \dot
1685 /// digraph result_map_error_xfrms {
1686 /// rankdir = "LR";
1687 ///
1688 /// subgraph cluster_init {
1689 /// label = "";
1690 /// style = "invis";
1691 ///
1692 /// result_ok_1 [label="result<TValue, TError>\nstate=ok", shape="rectangle"];
1693 /// result_err_1 [label="result<TValue, TError>\nstate=error", shape="rectangle"];
1694 /// }
1695 ///
1696 /// subgraph cluster_fin {
1697 /// label = "";
1698 /// style = "invis";
1699 ///
1700 /// result_ok_2 [label="result<TValue, UError>\nstate=ok", shape="rectangle"];
1701 /// result_err_2 [label="result<TValue, UError>\nstate=error", shape="rectangle"];
1702 /// }
1703 ///
1704 /// result_ok_1 -> result_ok_2 [label="transform not called"];
1705 /// result_err_1 -> result_err_2 [label="transform(TError&&) -> UError"];
1706 /// }
1707 /// \enddot
1708 template <typename FUnary, typename ResultInfo = detail::result_map_error_result_info<self_type&&, FUnary>>
1709 typename ResultInfo::type
1710 map_error(FUnary&& transform) && noexcept(ResultInfo::is_noexcept)
1711 {
1712 using return_type = typename ResultInfo::type;
1713
1714 auto clear_me = detail::on_scope_exit([&] { reset(); });
1715
1716 switch (state())
1717 {
1718 case result_state::ok:
1719 return return_type(in_place_ok, std::get<ok_index>(std::move(_storage)));
1720 case result_state::error:
1721 if constexpr (std::is_void_v<error_type>)
1722 {
1723 if constexpr (std::is_void_v<typename return_type::error_type>)
1724 {
1725 std::forward<FUnary>(transform)();
1726 return return_type(in_place_error);
1727 }
1728 else
1729 {
1730 return return_type(in_place_error, std::forward<FUnary>(transform)());
1731 }
1732 }
1733 else
1734 {
1735 if constexpr (std::is_void_v<typename return_type::error_type>)
1736 {
1737 std::forward<FUnary>(transform)(std::get<error_index>(std::move(_storage)).get());
1738 return return_type(in_place_error);
1739 }
1740 else
1741 {
1742 return return_type(in_place_error,
1743 std::forward<FUnary>(transform)(std::get<error_index>(std::move(_storage)).get())
1744 );
1745 }
1746 }
1747 case result_state::empty:
1748 default:
1749 return return_type();
1750 }
1751 }
1752 /// \}
1753
1754 /// \{
1755 /// Apply \a recovery to a \c result_state::error value, returning the result of the transformation if it returns an
1756 /// \c optional instance which has a value. If \a recovery returns \c nullopt, this instance's \c error is copied to
1757 /// the returned value. If this instance is \c result_state::ok, \a recovery is not called and the current value is
1758 /// copied to the result. If the instance is \c result_state::empty, an empty instance will be returned.
1759 ///
1760 /// This function is \c noexcept when calling \a transform is \c noexcept, the \c error_type is \c noexcept copy
1761 /// constructible, and the common type of \c value_type and the \a recovery function's \c value_type is \c noexcept
1762 /// copy constructible from \c value_type and \c noexcept move constructible from the \a recovery function's
1763 /// \c value_type. It is highly recommended to not throw from your \a recovery function.
1764 ///
1765 /// \param recovery The function to call in an attempt to recover from an error. This function returns a valued
1766 /// \c optional instance to indicate the error has been recovered from with the value to recover to.
1767 /// Returning a \c nullopt indicates the error was not recovered from.
1768 /// \tparam FUnary `optional<R> (*)(const error_type&)` if \c error_type is not \c void or `optional<R> (*)()` if it
1769 /// is. The type \c R in the result must have common type with \c value_type if \c value_type is not
1770 /// \c void and must be \c ok<void> if it is.
1771 ///
1772 /// \dot
1773 /// digraph result_recover_xfrms {
1774 /// rankdir = "LR";
1775 ///
1776 /// subgraph cluster_init {
1777 /// label = "";
1778 /// style = "invis";
1779 ///
1780 /// result_ok_1 [label="result<TValue, TError>\nstate=ok", shape="rectangle"];
1781 /// result_err_1 [label="result<TValue, TError>\nstate=error", shape="rectangle"];
1782 /// }
1783 ///
1784 /// subgraph cluster_fin {
1785 /// label = "";
1786 /// style = "invis";
1787 ///
1788 /// result_ok_2 [label="result<UValue, TError>\nstate=ok", shape="rectangle"];
1789 /// result_err_2 [label="result<UValue, TError>\nstate=error", shape="rectangle"];
1790 /// }
1791 ///
1792 /// result_ok_1 -> result_ok_2 [label="recovery not called"];
1793 /// result_err_1 -> result_ok_2 [label="recovery(const TError&) -> some(UValue)"];
1794 /// result_err_1 -> result_err_2 [label="recovery(const TError&) -> nullopt"];
1795 /// }
1796 /// \enddot
1797 ///
1798 /// \note
1799 /// There is no \c && qualified overload of \c recover. In \c result_state::error cases, the \c error_type instance
1800 /// would be moved into the \a recovery function, which should be passed back in cases where the error could not be
1801 /// recovered from. This would require the signature for \c FUnary to be something which could return either a
1802 /// value or an error, which is covered by \c recover_flat.
1803 template <typename FUnary, typename ResultInfo = detail::result_recover_result_info<const self_type&, FUnary>>
1804 typename ResultInfo::type
1805 recover(FUnary&& recovery) const& noexcept(ResultInfo::is_noexcept)
1806 {
1807 using return_type = typename ResultInfo::type;
1808
1809 switch (state())
1810 {
1811 case result_state::ok:
1812 if constexpr (std::is_void_v<value_type>)
1813 {
1814 return return_type(in_place_ok);
1815 }
1816 else
1817 {
1818 return return_type(in_place_ok, std::get<ok_index>(_storage).get());
1819 }
1820 case result_state::error:
1821 {
1822 using optional_type = typename ResultInfo::function_result_type;
1823
1824 auto intermediate =
1825 [&]() -> optional_type
1826 {
1827 if constexpr (std::is_void_v<error_type>)
1828 {
1829 return std::forward<FUnary>(recovery)();
1830 }
1831 else
1832 {
1833 return std::forward<FUnary>(recovery)(std::get<error_index>(_storage).get());
1834 }
1835 }();
1836
1837 if (intermediate)
1838 {
1839 return return_type(in_place_ok, std::move(*intermediate));
1840 }
1841 else
1842 {
1843 return return_type(in_place_error, std::get<error_index>(_storage));
1844 }
1845 }
1846 case result_state::empty:
1847 default:
1848 return return_type();
1849 }
1850 }
1851 /// \}
1852
1853 /// \{
1854 /// Apply \a recovery to a \c result_state::error value, returning the result of the transformation, which itself is
1855 /// a \c result. If the instance is \c result_state::ok, the value is copied into the returned value. If the
1856 /// instance is \c result_state::empty, an empty instance will be returned.
1857 ///
1858 /// This function is \c noexcept when calling \a recovery is \c noexcept, the result is \c noexcept
1859 /// move-constructible, and the \c value_type of the returned type is \c noexcept constructible from this instance's
1860 /// \c value_type. It is highly encouraged to not throw from your \a recovery function.
1861 ///
1862 /// \tparam FUnary `result<R, E> (*)(const error_type&)` if \c error_type is not \c void or `result<R, E> (*)()` if
1863 /// it is. Type \c R in the result must have common type with \c value_type.
1864 ///
1865 /// \dot
1866 /// digraph result_recover_flat_xfrms {
1867 /// rankdir = "LR";
1868 ///
1869 /// subgraph cluster_init {
1870 /// label = "";
1871 /// style = "invis";
1872 ///
1873 /// result_ok_1 [label="result<TValue, TError>\nstate=ok", shape="rectangle"];
1874 /// result_err_1 [label="result<TValue, TError>\nstate=error", shape="rectangle"];
1875 /// }
1876 ///
1877 /// subgraph cluster_fin {
1878 /// label = "";
1879 /// style = "invis";
1880 ///
1881 /// result_ok_2 [label="result<UValue, UError>\nstate=ok", shape="rectangle"];
1882 /// result_err_2 [label="result<UValue, UError>\nstate=error", shape="rectangle"];
1883 /// }
1884 ///
1885 /// result_ok_1 -> result_ok_2 [label="recovery not called"];
1886 /// result_err_1 -> result_ok_2 [label="recovery(const TError&) -> UValue"];
1887 /// result_err_1 -> result_err_2 [label="recovery(const TError&) -> UError"];
1888 /// }
1889 /// \enddot
1890 template <typename FUnary, typename ResultInfo = detail::result_recover_flat_result_info<const self_type&, FUnary>>
1891 typename ResultInfo::type
1892 recover_flat(FUnary&& recovery) const& noexcept(ResultInfo::is_noexcept)
1893 {
1894 using return_type = typename ResultInfo::type;
1895
1896 switch (state())
1897 {
1898 case result_state::ok:
1899 if constexpr (std::is_void_v<value_type>)
1900 {
1901 return return_type(in_place_ok);
1902 }
1903 else
1904 {
1905 return return_type(in_place_ok, std::get<ok_index>(_storage).get());
1906 }
1907 case result_state::error:
1908 if constexpr (std::is_void_v<error_type>)
1909 {
1910 return std::forward<FUnary>(recovery)();
1911 }
1912 else
1913 {
1914 return std::forward<FUnary>(recovery)(std::get<error_index>(_storage).get());
1915 }
1916 case result_state::empty:
1917 default:
1918 return return_type();
1919 }
1920 }
1921
1922 /// Apply \a recovery to a \c result_state::error value, returning the result of the transformation, which itself is
1923 /// a \c result. If the instance is \c result_state::ok, the value is copied into the returned value. If the
1924 /// instance is \c result_state::empty, an empty instance will be returned.
1925 ///
1926 /// This function is \c noexcept when calling \a recovery is \c noexcept, the result is \c noexcept
1927 /// move-constructible, and the \c value_type of the returned type is \c noexcept constructible from this instance's
1928 /// \c value_type. It is highly encouraged to not throw from your \a recovery function.
1929 ///
1930 /// \tparam FUnary `result<R, E> (*)(error_type&&)` if \c error_type is not \c void or `result<R, E> (*)()` if it
1931 /// is. Type \c R in the result must have common type with \c value_type.
1932 ///
1933 /// \dot
1934 /// digraph result_recover_flat_xfrms {
1935 /// rankdir = "LR";
1936 ///
1937 /// subgraph cluster_init {
1938 /// label = "";
1939 /// style = "invis";
1940 ///
1941 /// result_ok_1 [label="result<TValue, TError>\nstate=ok", shape="rectangle"];
1942 /// result_err_1 [label="result<TValue, TError>\nstate=error", shape="rectangle"];
1943 /// }
1944 ///
1945 /// subgraph cluster_fin {
1946 /// label = "";
1947 /// style = "invis";
1948 ///
1949 /// result_ok_2 [label="result<UValue, UError>\nstate=ok", shape="rectangle"];
1950 /// result_err_2 [label="result<UValue, UError>\nstate=error", shape="rectangle"];
1951 /// }
1952 ///
1953 /// result_ok_1 -> result_ok_2 [label="recovery not called"];
1954 /// result_err_1 -> result_ok_2 [label="recovery(TError&&) -> UValue"];
1955 /// result_err_1 -> result_err_2 [label="recovery(TError&&) -> UError"];
1956 /// }
1957 /// \enddot
1958 template <typename FUnary, typename ResultInfo = detail::result_recover_flat_result_info<self_type&&, FUnary>>
1959 typename ResultInfo::type
1960 recover_flat(FUnary&& recovery) && noexcept(ResultInfo::is_noexcept)
1961 {
1962 using return_type = typename ResultInfo::type;
1963
1964 auto clear_me = detail::on_scope_exit([&] { reset(); });
1965 switch (state())
1966 {
1967 case result_state::ok:
1968 if constexpr (std::is_void_v<value_type>)
1969 {
1970 return return_type(in_place_ok);
1971 }
1972 else
1973 {
1974 return return_type(in_place_ok, std::get<ok_index>(std::move(_storage)).get());
1975 }
1976 case result_state::error:
1977 if constexpr (std::is_void_v<error_type>)
1978 {
1979 return std::forward<FUnary>(recovery)();
1980 }
1981 else
1982 {
1983 return std::forward<FUnary>(recovery)(std::get<error_index>(std::move(_storage)).get());
1984 }
1985 case result_state::empty:
1986 default:
1987 return return_type();
1988 }
1989 }
1990 /// \}
1991
1992private:
1993 using storage_type = std::variant<std::monostate, ok<value_type>, jsonv::error<error_type>>;
1994
1995 static constexpr std::size_t empty_index = 0U;
1996 static constexpr std::size_t ok_index = 1U;
1997 static constexpr std::size_t error_index = 2U;
1998
1999 constexpr void ensure_state(const char* op_name, result_state expected) const
2000 {
2001 if (state() != expected)
2002 throw bad_result_access::must_be(op_name, expected, state());
2003 }
2004
2005private:
2006 storage_type _storage;
2007};
2008
2009template <typename T>
2010result(ok<T>) -> result<T>;
2011
2012}
Thrown when an attempt to access the contents of a result was illegal.
Definition result.hpp:672
static bad_result_access must_be(string_view op_name, result_state expected, result_state actual)
Create an exception with a description noting the op_name must be performed in the expected state,...
static bad_result_access must_not_be(string_view op_name, result_state unexpected)
Create an exception with a description noting the op_name must not be performed in the unexpected sta...
bad_result_access(const std::string &description) noexcept
Create an exception with the given description.
An adapter for enumeration types.
A wrapper type for creating a result with result_state::error.
Definition result.hpp:626
TError error_type
Definition result.hpp:629
A wrapper type for creating a result with result_state::ok.
Definition result.hpp:569
TValue value_type
Definition result.hpp:572
constexpr ok(TArgs &&... args) noexcept(std::is_nothrow_constructible_v< value_type, TArgs... >)
Create an instance from the given args.
Definition result.hpp:577
const value_type & get() const &noexcept
Definition result.hpp:583
The set of result operations which depend on the type of TError.
Definition result.hpp:784
const error_type & error() const &
Definition result.hpp:796
The set of result operations which depend on the type of TValue.
Definition result.hpp:693
const value_type & value() const &
Definition result.hpp:704
const value_type & operator*() const &
Definition result.hpp:736
constexpr value_type value_or(UValue &&recovery_value) const &noexcept(std::is_nothrow_copy_constructible_v< value_type > &&std::is_nothrow_constructible_v< value_type, UValue >)
Definition result.hpp:712
const value_type * operator->() const
Definition result.hpp:745
A type that represents a value which might be a success (is_ok) or a failure (is_error).
Definition result.hpp:1091
ResultInfo::type map(FUnary &&transform) &&noexcept(ResultInfo::is_noexcept)
Apply transform to a result_state::ok value, returning the result of the transformation in another re...
Definition result.hpp:1425
ResultInfo::type map_error(FUnary &&transform) &&noexcept(ResultInfo::is_noexcept)
Apply transform to a result_state::error value, returning the result of the transformation in another...
Definition result.hpp:1710
constexpr result(UValue &&value, std::enable_if_t< std::is_convertible_v< UValue, value_type > &&!std::is_same_v< value_type, error_type > > *=nullptr) noexcept(std::is_nothrow_constructible_v< value_type, UValue >)
Construct a result with result_state::ok from the provided value.
Definition result.hpp:1149
constexpr bool is_error() const noexcept
Check that this result has state of result_state::error.
Definition result.hpp:1299
constexpr result(TOkValue &&value, std::enable_if_t< detail::is_template_of_v< ok, std::decay_t< TOkValue > > &&std::is_convertible_v< typename TOkValue::value_type, value_type > > *=nullptr)
Construct a result with result_state::ok from the contents of value.
Definition result.hpp:1126
constexpr result_state state() const noexcept
Get the state of this object.
Definition result.hpp:1276
constexpr result(result &&) noexcept=default
Result instances are move-constructible iff value_type and error_type are.
constexpr bool is_ok() const noexcept
Check that this result has state of result_state::ok.
Definition result.hpp:1287
constexpr result(in_place_error_t, TArgs &&... args) noexcept(std::is_nothrow_constructible_v< error_type, TArgs... >)
Construct a result with result_state::error from the provided args.
Definition result.hpp:1160
constexpr result(TOkValue &&value, std::enable_if_t< std::is_same_v< ok< void >, std::decay_t< TOkValue > > &&std::is_void_v< value_type > > *=nullptr) noexcept
Construct a result with result_state::ok from a ok<void>.
Definition result.hpp:1137
void reset() noexcept(std::is_nothrow_destructible_v< value_type > &&std::is_nothrow_destructible_v< error_type >)
Reset the contents of this instance to result_state::empty.
Definition result.hpp:1305
ResultInfo::type recover_flat(FUnary &&recovery) const &noexcept(ResultInfo::is_noexcept)
Definition result.hpp:1892
ResultInfo::type recover(FUnary &&recovery) const &noexcept(ResultInfo::is_noexcept)
Definition result.hpp:1805
ResultInfo::type map_flat(FUnary &&transform) const &noexcept(ResultInfo::is_noexcept)
Definition result.hpp:1508
ResultInfo::type map_error(FUnary &&transform) const &noexcept(ResultInfo::is_noexcept)
Definition result.hpp:1632
constexpr result() noexcept
Construct an empty result.
Definition result.hpp:1112
ResultInfo::type map_flat(FUnary &&transform) &&noexcept(ResultInfo::is_noexcept)
Apply transform to a result_state::ok value, returning the result of the transformation,...
Definition result.hpp:1569
constexpr result(TErrorValue &&, std::enable_if_t< std::is_same_v< jsonv::error< void >, std::decay_t< TErrorValue > > &&std::is_void_v< error_type > > *=nullptr) noexcept
Construct a result with result_state::error from an error<void>.
Definition result.hpp:1179
ResultInfo::type recover_flat(FUnary &&recovery) &&noexcept(ResultInfo::is_noexcept)
Apply recovery to a result_state::error value, returning the result of the transformation,...
Definition result.hpp:1960
constexpr result(TErrorValue &&error_value, std::enable_if_t< detail::is_template_of_v< jsonv::error, TErrorValue > &&std::is_convertible_v< typename TErrorValue::error_type, error_type > > *=nullptr)
Construct a result with result_state::error from the contents of error_value.
Definition result.hpp:1168
constexpr result(const result &)=default
Result instances are copy-constructible iff value_type and error_type are.
constexpr result(in_place_ok_t, TArgs &&... args) noexcept(std::is_nothrow_constructible_v< value_type, TArgs... >)
Construct a result with result_state::ok from the provided args.
Definition result.hpp:1118
ResultInfo::type map(FUnary &&transform) const &noexcept(ResultInfo::is_noexcept)
Definition result.hpp:1348
result(result< UValue, UError > &&src) noexcept
Converting constructor from a result with a convertible value_type and error_type.
Definition result.hpp:1244
Represents a single JSON value, which can be any one of a potential kind, each behaving slightly diff...
Definition value.hpp:107
Copyright (c) 2014-2020 by Travis Gockel.
#define JSONV_PUBLIC
This function or class is part of the public API for JSON Voorhees.
Definition config.hpp:102
STL namespace.
Pulls in an implementation of optional.
result_state
Describes the state of a result instance.
Definition result.hpp:548
Definition of the on_scope_exit utility.
Pulls in an implementation of string_view.
std::string_view string_view
A non-owning reference to a string.