ROSE 0.11.145.147
Result.h
1// WARNING: Changes to this file must be contributed back to Sawyer or else they will
2// be clobbered by the next update from Sawyer. The Sawyer repository is at
3// https://gitlab.com/charger7534/sawyer.git.
4
5
6
7
8#ifndef Sawyer_Result_H
9#define Sawyer_Result_H
10
11#include <Sawyer/Optional.h>
12
13#include <boost/lexical_cast.hpp>
14#include <boost/variant.hpp>
15#include <exception>
16#include <string>
17#include <type_traits>
18
19namespace Sawyer {
20
21template <class F, class... Args>
23{
24 template <class U>
25 static auto test(U* p) -> decltype((*p)(std::declval<Args>()...), void(), std::true_type());
26 template <class U>
27 static auto test(...) -> decltype(std::false_type());
28
29 static constexpr bool value = decltype(test<F>(0))::value;
30};
31
32
34template<class T>
35class Ok {
36public:
37 using Value = T;
38
39private:
40 Value ok_;
41
42#ifdef SAWYER_HAVE_BOOST_SERIALIZATION
43private:
44 friend class boost::serialization::access;
45
46 template<class S>
47 void serialize(S &s, const unsigned /*version*/) {
48 s & BOOST_SERIALIZATION_NVP(ok_);
49 }
50#endif
51
52#ifdef SAWYER_HAVE_CEREAL
53private:
54 friend class cereal::access;
55
56 template<class Archive>
57 void CEREAL_SERIALIZE_FUNCTION_NAME(Archive &archive) {
58 archive(cereal::make_nvp("ok", ok_));
59 }
60#endif
61
62public:
63 Ok() = delete;
64
66 Ok(const Ok &other)
67 : ok_(other.ok_) {}
68
70 explicit Ok(const Value &ok)
71 : ok_(ok) {}
72
76 Ok& operator=(const Ok &other) {
77 ok_ = other.ok_;
78 return *this;
79 }
80 Ok& operator=(const Value &ok) {
81 ok_ = ok;
82 return *this;
83 }
87 const Value& operator*() const {
88 return ok_;
89 }
90
92 const Value* operator->() const {
93 return &ok_;
94 }
95};
96
97// Specialization for Ok that stores string literals, as in Ok("foo"). These get treated as std::string instead.
98template<size_t N>
99class Ok<char[N]> {
100public:
101 using Value = std::string;
102
103private:
104 std::string ok_;
105
106#ifdef SAWYER_HAVE_BOOST_SERIALIZATION
107private:
108 friend class boost::serialization::access;
109
110 template<class S>
111 void serialize(S &s, const unsigned /*version*/) {
112 s & BOOST_SERIALIZATION_NVP(ok_);
113 }
114#endif
115
116#ifdef SAWYER_HAVE_CEREAL
117private:
118 friend class cereal::access;
119
120 template<class Archive>
121 void CEREAL_SERIALIZE_FUNCTION_NAME(Archive &archive) {
122 archive(cereal::make_nvp("ok", ok_));
123 }
124#endif
125
126public:
127 Ok() = delete;
128
129 explicit Ok(const Value &s)
130 : ok_(s) {}
131
132 Ok& operator=(const Ok &other) {
133 ok_ = other.ok_;
134 return *this;
135 }
136
137 Ok& operator=(const Value &ok) {
138 ok_ = ok;
139 return *this;
140 }
141
142 const Value& operator*() const {
143 return ok_;
144 }
145
146 const Value* operator->() const {
147 return &ok_;
148 }
149};
150
155template<class T>
156Ok<T> makeOk(const T &value) {
157 return Ok<T>(value);
158}
159
160// Specialization for string literals, as in makeOk("foo") so they get treated as std::string instead.
161inline Ok<std::string> makeOk(const char *s) {
162 return Ok<std::string>(std::string(s));
163}
164inline Ok<std::string> makeOk(char *s) {
165 return Ok<std::string>(std::string(s));
166}
167
169template<class E>
170class Error {
171public:
172 using Value = E;
173
174private:
175 Value error_;
176
177#ifdef SAWYER_HAVE_BOOST_SERIALIZATION
178private:
179 friend class boost::serialization::access;
180
181 template<class S>
182 void serialize(S &s, const unsigned /*version*/) {
183 s & BOOST_SERIALIZATION_NVP(error_);
184 }
185#endif
186
187#ifdef SAWYER_HAVE_CEREAL
188private:
189 friend class cereal::access;
190
191 template<class Archive>
192 void CEREAL_SERIALIZE_FUNCTION_NAME(Archive &archive) {
193 archive(cereal::make_nvp("error", error_));
194 }
195#endif
196
197public:
198 Error() = delete;
199
201 Error(const Error &other)
202 : error_(other.error_) {}
203
205 explicit Error(const E &error)
206 : error_(error) {}
207
211 Error& operator=(const Error &other) {
212 error_ = other.error_;
213 return *this;
214 }
215 Error& operator=(const Value &error) {
216 error_ = error;
217 return *this;
218 }
222 const Value& operator*() const {
223 return error_;
224 }
225
227 const Value* operator->() const {
228 return &error_;
229 }
230};
231
232// Specialization for Error that stores string literals as in Error("syntax error"). It stores them as std::string instead.
233template<size_t N>
234class Error<char[N]> {
235public:
236 using Value = std::string;
237
238private:
239 std::string error_;
240
241#ifdef SAWYER_HAVE_BOOST_SERIALIZATION
242private:
243 friend class boost::serialization::access;
244
245 template<class S>
246 void serialize(S &s, const unsigned /*version*/) {
247 s & BOOST_SERIALIZATION_NVP(error_);
248 }
249#endif
250
251#ifdef SAWYER_HAVE_CEREAL
252private:
253 friend class cereal::access;
254
255 template<class Archive>
256 void CEREAL_SERIALIZE_FUNCTION_NAME(Archive &archive) {
257 archive(cereal::make_nvp("error", error_));
258 }
259#endif
260
261public:
262 Error() = delete;
263
265 explicit Error(const Error &other)
266 : error_(other.error_) {}
267
269 explicit Error(const Value &error)
270 : error_(error) {}
271
275 Error& operator=(const Error &other) {
276 error_ = other.error_;
277 return *this;
278 }
279 Error& operator=(const Value &error) {
280 error_ = error;
281 return *this;
282 }
286 const Value& operator*() const {
287 return error_;
288 }
289
291 const Value* operator->() const {
292 return &error_;
293 }
294};
295
300template<class T>
301Error<T> makeError(const T &value) {
302 return Error<T>(value);
303}
304
305// Specialization for string literals, as in makeError("foo") so they get treated as std::string instead.
306inline Error<std::string> makeError(const char *s) {
307 return Error<std::string>(std::string(s));
308}
309inline Error<std::string> makeError(char *s) {
310 return Error<std::string>(std::string(s));
311}
312
314template<class T, class E>
315class Result {
316public:
317 using OkValue = T;
318 using ErrorValue = E;
319 using OkType = Ok<T>;
320 using ErrorType = Error<E>;
321
322private:
323 boost::variant<Ok<T>, Error<E>> result_;
324
325#ifdef SAWYER_HAVE_BOOST_SERIALIZATION
326private:
327 friend class boost::serialization::access;
328
329 template<class S>
330 void save(S &s, const unsigned /*version*/) const {
331 s <<boost::serialization::make_nvp("isOk", isOk());
332 if (isOk()) {
333 s <<boost::serialization::make_nvp("ok", unwrap());
334 } else {
335 s <<boost::serialization::make_nvp("error", unwrapError());
336 }
337 }
338
339 template<class S>
340 void load(S &s, const unsigned /*version*/) {
341 bool isOk;
342 s >>boost::serialization::make_nvp("isOk", isOk);
343 if (isOk) {
344 T ok;
345 s >>boost::serialization::make_nvp("ok", ok);
346 result_ = OkType(ok);
347 } else {
348 E error;
349 s >>boost::serialization::make_nvp("error", error);
350 result_ = ErrorType(error);
351 }
352 }
353
354 BOOST_SERIALIZATION_SPLIT_MEMBER();
355#endif
356
357#ifdef SAWYER_HAVE_CEREAL
358private:
359 friend class cereal::access;
360
361 template<class Archive>
362 void CEREAL_SAVE_FUNCTION_NAME(Archive &archive) const {
363 archive(cereal::make_nvp("isOk", isOk()));
364 if (isOk()) {
365 archive(cereal::make_nvp("ok", unwrap()));
366 } else {
367 archive(cereal::make_nvp("error", unwrapError()));
368 }
369 }
370
371 template<class Archive>
372 void CEREAL_LOAD_FUNCTION_NAME(Archive &archive) {
373 bool isOk;
374 archive(cereal::make_nvp("isOk", isOk));
375 if (isOk) {
376 T ok;
377 archive(cereal::make_nvp("ok", ok));
378 result_ = OkType(ok);
379 } else {
380 E error;
381 archive(cereal::make_nvp("error", error));
382 result_ = ErrorType(error);
383 }
384 }
385#endif
386
387public:
388 template<class U = T>
389 /*implicit*/ Result(const Ok<U> &ok)
390 : result_(OkType(*ok)) {}
391
392 template<class F = E>
393 /*implicit*/ Result(const Error<F> &error)
394 : result_(ErrorType(*error)) {}
395
397 template<class U = T>
399 result_ = OkType(*ok);
400 return *this;
401 }
402
404 template<class F = E>
406 result_ = ErrorType(*error);
407 return *this;
408 }
409
411 template<class U = T>
412 bool operator==(const Ok<U> &ok) const {
413 return isOk() && *this->ok() == *ok;
414 }
415
417 template<class U = T>
418 bool operator!=(const Ok<U> &ok) const {
419 return !(*this == ok);
420 }
421
423 template<class F = E>
424 bool operator==(const Error<F> &error) const {
425 return isError() && *this->error() == *error;
426 }
427
429 template<class F = E>
430 bool operator!=(const Error<F> &error) const {
431 return !(*this == error);
432 }
433
435 template<class U, class F>
436 bool operator==(const Result<U, F> &other) const {
437 return ((isOk() && other.isOk() && *ok() == *other.ok()) ||
438 (isError() && other.isError() && *error() == *other.error()));
439 }
440
442 template<class U, class F>
443 bool operator!=(const Result<U, F> &other) const {
444 return !(*this == other);
445 }
446
450 bool isOk() const {
451 return result_.which() == 0;
452 }
453 explicit operator bool() const {
454 return isOk();
455 }
459 bool isError() const {
460 return !isOk();
461 }
462
466 const Sawyer::Optional<T> ok() const {
467 if (isOk()) {
468 return *boost::get<OkType>(result_);
469 } else {
470 return Sawyer::Nothing();
471 }
472 }
473
478 if (isOk()) {
479 return Sawyer::Nothing();
480 } else {
481 return *boost::get<ErrorType>(result_);
482 }
483 }
484
488 const T& expect(const std::string &mesg) const {
489 if (isOk()) {
490 return *boost::get<OkType>(result_);
491 } else {
492 throw std::runtime_error(mesg);
493 }
494 }
495
501 const T& unwrap() const {
502 return expect("result is not okay");
503 }
504 const T& operator*() const {
505 return unwrap();
506 }
512 const T* operator->() const {
513 return &unwrap();
514 }
515
517 const T orElse(const T &dflt) const {
518 return isOk() ? unwrap() : dflt;
519 }
520
525 template<class Fn>
526 typename std::enable_if<is_invocable<Fn, ErrorValue>::value, const Result>::type
527 orElse(Fn fn) const {
528 if (isOk()) {
529 return *this;
530 } else {
531 return fn(*error());
532 }
533 }
534
538 template<class F>
539 const Result<T, F> orElse(const Result<T, F> &other) const {
540 if (isOk()) {
541 return boost::get<OkType>(result_);
542 } else {
543 return other;
544 }
545 }
546
548 const T& orDefault() const {
549 static T dflt = T();
550 return isOk() ? unwrap() : dflt;
551 }
552
557 template<class Exception = E>
558 const T& orThrow() const {
559 if (isOk()) {
560 return unwrap();
561 } else {
562 throw Exception(*error());
563 }
564 }
565
567 template<class Exception = E>
568 const T& orThrow(const Exception &e) const {
569 if (isOk()) {
570 return unwrap();
571 } else {
572 throw e;
573 }
574 }
575
580 template<class Fn>
581 typename std::enable_if<is_invocable<Fn, OkValue>::value, const Result>::type
582 andThen(Fn fn) const {
583 if (isOk()) {
584 return fn(*ok());
585 } else {
586 return *this;
587 }
588 }
589
593 template<class U>
594 const Result<U, E> andThen(const Result<U, E> &other) const {
595 if (isOk()) {
596 return other;
597 } else {
598 return boost::get<ErrorType>(result_);
599 }
600 }
601
605 const E& expectError(const std::string &mesg) const {
606 if (isOk()) {
607 throw std::runtime_error(mesg);
608 } else {
609 return *boost::get<ErrorType>(result_);
610 }
611 }
612
616 const E& unwrapError() const {
617 return expectError("result is not an error");
618 }
619
621 template<class U>
622 bool contains(const U &value) const {
623 return isOk() ? unwrap() == value : false;
624 }
625
627 template<class F>
628 bool containsError(const F &error) const {
629 return isOk() ? false : unwrapError() == error;
630 }
631
632#if 0 // [Robb Matzke 2022-08-17]
638 Optional<Result<typename T::Value, E>>
639 transpose() const {
640 if (isOk()) {
641 if (unwrap().isEmpty()) {
642 return Sawyer::Nothing();
643 } else {
644 return OkType(*unwrap());
645 }
646 } else {
647 return ErrorType(unwrapError());
648 }
649 }
650
652 Result<typename OkValue::OkValue, E> flatten() const {
653 if (isOk()) {
654 if (unwrap().isOk()) {
655 return OkValue::OkType(unwrap().unwrap());
656 } else {
657 return ErrorType(unwrap().unwrapError());
658 }
659 } else {
660 return ErrorType(unwrapError());
661 }
662 }
663#endif
664
671 template<class U>
672 const Result& assignTo(U &out) const {
673 if (isOk())
674 out = unwrap();
675 return *this;
676 }
677
678 template<class U>
679 Result& assignTo(U &out) const {
680 if (isOk())
681 out = unwrap();
682 return *this;
683 }
691 template <typename F>
692 auto
693 fmap(F&& f) -> Result<decltype(f(unwrap())), E> {
694 if (isOk())
695 return makeOk(f(unwrap()));
696 return makeError(unwrapError());
697 }
698
699};
700
701} // namespace
702#endif
Error(const Error &other)
Copy constructor.
Definition Result.h:265
Error & operator=(const Value &error)
Assignment.
Definition Result.h:279
const Value & operator*() const
Dereference to obtain error.
Definition Result.h:286
Error & operator=(const Error &other)
Assignment.
Definition Result.h:275
const Value * operator->() const
Dereference to obtain pointer to error.
Definition Result.h:291
Error(const Value &error)
Construct from a value.
Definition Result.h:269
Error value.
Definition Result.h:170
const Value & operator*() const
Dereference to obtain error.
Definition Result.h:222
const Value * operator->() const
Dereference to obtain pointer to error.
Definition Result.h:227
Error(const E &error)
Construct from a value.
Definition Result.h:205
Error & operator=(const Value &error)
Assignment.
Definition Result.h:215
Error(const Error &other)
Copy constructor.
Definition Result.h:201
Error & operator=(const Error &other)
Assignment.
Definition Result.h:211
Represents no value.
Definition Optional.h:36
Success value.
Definition Result.h:35
Ok & operator=(const Ok &other)
Assignment.
Definition Result.h:76
const Value * operator->() const
Dereference to obtain pointer.
Definition Result.h:92
Ok & operator=(const Value &ok)
Assignment.
Definition Result.h:80
Ok(const Ok &other)
Copy constructor.
Definition Result.h:66
const Value & operator*() const
Dereference to obtain value.
Definition Result.h:87
Ok(const Value &ok)
Construct from an value.
Definition Result.h:70
Holds a value or nothing.
Definition Optional.h:56
Result containing a value or an error.
Definition Result.h:315
const T orElse(const T &dflt) const
Returns the contained Ok value or a provided default.
Definition Result.h:517
bool operator==(const Result< U, F > &other) const
Test whether this result is equal to the other result.
Definition Result.h:436
bool operator!=(const Ok< U > &ok) const
Test whether this result does not have the specified Ok value.
Definition Result.h:418
const T & expect(const std::string &mesg) const
Returns the success value or throws an exception.
Definition Result.h:488
const Sawyer::Optional< T > ok() const
Convert to Optional<T>.
Definition Result.h:466
bool contains(const U &value) const
Returns true if this result contains the specified okay value.
Definition Result.h:622
std::enable_if< is_invocable< Fn, ErrorValue >::value, constResult >::type orElse(Fn fn) const
Returns the contained Ok value, or calls a function.
Definition Result.h:527
const E & expectError(const std::string &mesg) const
Returns the error value or throws an exception.
Definition Result.h:605
bool operator==(const Ok< U > &ok) const
Test whether this result has the specified Ok value.
Definition Result.h:412
Result & operator=(const Error< F > &error)
Assign an Error value to this result.
Definition Result.h:405
const E & unwrapError() const
Returns the error value or throws an exception.
Definition Result.h:616
const T & orDefault() const
Returns the okay value or a default constructed value.
Definition Result.h:548
const T & operator*() const
Returns the success value or throws an exception.
Definition Result.h:504
const Result< U, E > andThen(const Result< U, E > &other) const
Returns this error or the other result.
Definition Result.h:594
const T & unwrap() const
Returns the success value or throws an exception.
Definition Result.h:501
const Result & assignTo(U &out) const
Conditionally save a value.
Definition Result.h:672
bool operator==(const Error< F > &error) const
Test whether this result has the specified Error value.
Definition Result.h:424
const Sawyer::Optional< E > error() const
Convert to Optional<E>.
Definition Result.h:477
const Result< T, F > orElse(const Result< T, F > &other) const
Returns this value or the other result.
Definition Result.h:539
bool isError() const
Returns true if the result is an error.
Definition Result.h:459
bool operator!=(const Result< U, F > &other) const
Test whether this result is unequal to the other result.
Definition Result.h:443
Result & assignTo(U &out) const
Conditionally save a value.
Definition Result.h:679
const T * operator->() const
Returns a pointer to the success value, or thows an exception.
Definition Result.h:512
bool operator!=(const Error< F > &error) const
Test whether this result does not have the specified Error value.
Definition Result.h:430
const T & orThrow(const Exception &e) const
Returns the value or throws an exception constructed from the specified value.
Definition Result.h:568
Result & operator=(const Ok< U > &ok)
Assign an Ok value to this result.
Definition Result.h:398
bool containsError(const F &error) const
Returns true if this result contains the specified error value.
Definition Result.h:628
std::enable_if< is_invocable< Fn, OkValue >::value, constResult >::type andThen(Fn fn) const
Returns the contained Error value, or calls a function.
Definition Result.h:582
auto fmap(F &&f) -> Result< decltype(f(unwrap())), E >
Conditionally apply a functor f to the contents of a Result.
Definition Result.h:693
const T & orThrow() const
Returns the value or throws an exception.
Definition Result.h:558
bool isOk() const
Returns true if the result is okay.
Definition Result.h:450
Sawyer support library.
Error< T > makeError(const T &value)
Conventient way to constructo an Error value before C++17.
Definition Result.h:301
Ok< T > makeOk(const T &value)
Conventient way to constructo an Ok value before C++17.
Definition Result.h:156