1 //     Copyright Toru Niina 2017.
2 // Distributed under the MIT License.
3 #ifndef TOML11_RESULT_HPP
4 #define TOML11_RESULT_HPP
5 #include "traits.hpp"
6 #include <type_traits>
7 #include <stdexcept>
8 #include <utility>
9 #include <new>
10 #include <string>
11 #include <sstream>
12 #include <cassert>
13 
14 namespace toml
15 {
16 
17 template<typename T>
18 struct success
19 {
20     using value_type = T;
21     value_type value;
22 
successtoml::success23     explicit success(const value_type& v)
24         noexcept(std::is_nothrow_copy_constructible<value_type>::value)
25         : value(v)
26     {}
successtoml::success27     explicit success(value_type&& v)
28         noexcept(std::is_nothrow_move_constructible<value_type>::value)
29         : value(std::move(v))
30     {}
31 
32     template<typename U>
successtoml::success33     explicit success(U&& v): value(std::forward<U>(v)) {}
34 
35     template<typename U>
successtoml::success36     explicit success(const success<U>& v): value(v.value) {}
37     template<typename U>
successtoml::success38     explicit success(success<U>&& v): value(std::move(v.value)) {}
39 
40     ~success() = default;
41     success(const success&) = default;
42     success(success&&)      = default;
43     success& operator=(const success&) = default;
44     success& operator=(success&&)      = default;
45 };
46 
47 template<typename T>
48 struct failure
49 {
50     using value_type = T;
51     value_type value;
52 
failuretoml::failure53     explicit failure(const value_type& v)
54         noexcept(std::is_nothrow_copy_constructible<value_type>::value)
55         : value(v)
56     {}
failuretoml::failure57     explicit failure(value_type&& v)
58         noexcept(std::is_nothrow_move_constructible<value_type>::value)
59         : value(std::move(v))
60     {}
61 
62     template<typename U>
failuretoml::failure63     explicit failure(U&& v): value(std::forward<U>(v)) {}
64 
65     template<typename U>
failuretoml::failure66     explicit failure(const failure<U>& v): value(v.value) {}
67     template<typename U>
failuretoml::failure68     explicit failure(failure<U>&& v): value(std::move(v.value)) {}
69 
70     ~failure() = default;
71     failure(const failure&) = default;
72     failure(failure&&)      = default;
73     failure& operator=(const failure&) = default;
74     failure& operator=(failure&&)      = default;
75 };
76 
77 template<typename T>
78 success<typename std::remove_cv<typename std::remove_reference<T>::type>::type>
ok(T && v)79 ok(T&& v)
80 {
81     return success<
82         typename std::remove_cv<typename std::remove_reference<T>::type>::type
83         >(std::forward<T>(v));
84 }
85 template<typename T>
86 failure<typename std::remove_cv<typename std::remove_reference<T>::type>::type>
err(T && v)87 err(T&& v)
88 {
89     return failure<
90         typename std::remove_cv<typename std::remove_reference<T>::type>::type
91         >(std::forward<T>(v));
92 }
93 
ok(const char * literal)94 inline success<std::string> ok(const char* literal)
95 {
96     return success<std::string>(std::string(literal));
97 }
err(const char * literal)98 inline failure<std::string> err(const char* literal)
99 {
100     return failure<std::string>(std::string(literal));
101 }
102 
103 
104 template<typename T, typename E>
105 struct result
106 {
107     using value_type = T;
108     using error_type = E;
109     using success_type = success<value_type>;
110     using failure_type = failure<error_type>;
111 
resulttoml::result112     result(const success_type& s): is_ok_(true)
113     {
114         auto tmp = ::new(std::addressof(this->succ)) success_type(s);
115         assert(tmp == std::addressof(this->succ));
116         (void)tmp;
117     }
resulttoml::result118     result(const failure_type& f): is_ok_(false)
119     {
120         auto tmp = ::new(std::addressof(this->fail)) failure_type(f);
121         assert(tmp == std::addressof(this->fail));
122         (void)tmp;
123     }
resulttoml::result124     result(success_type&& s): is_ok_(true)
125     {
126         auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s));
127         assert(tmp == std::addressof(this->succ));
128         (void)tmp;
129     }
resulttoml::result130     result(failure_type&& f): is_ok_(false)
131     {
132         auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f));
133         assert(tmp == std::addressof(this->fail));
134         (void)tmp;
135     }
136 
137     template<typename U>
resulttoml::result138     result(const success<U>& s): is_ok_(true)
139     {
140         auto tmp = ::new(std::addressof(this->succ)) success_type(s.value);
141         assert(tmp == std::addressof(this->succ));
142         (void)tmp;
143     }
144     template<typename U>
resulttoml::result145     result(const failure<U>& f): is_ok_(false)
146     {
147         auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value);
148         assert(tmp == std::addressof(this->fail));
149         (void)tmp;
150     }
151     template<typename U>
resulttoml::result152     result(success<U>&& s): is_ok_(true)
153     {
154         auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value));
155         assert(tmp == std::addressof(this->succ));
156         (void)tmp;
157     }
158     template<typename U>
resulttoml::result159     result(failure<U>&& f): is_ok_(false)
160     {
161         auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value));
162         assert(tmp == std::addressof(this->fail));
163         (void)tmp;
164     }
165 
operator =toml::result166     result& operator=(const success_type& s)
167     {
168         this->cleanup();
169         this->is_ok_ = true;
170         auto tmp = ::new(std::addressof(this->succ)) success_type(s);
171         assert(tmp == std::addressof(this->succ));
172         (void)tmp;
173         return *this;
174     }
operator =toml::result175     result& operator=(const failure_type& f)
176     {
177         this->cleanup();
178         this->is_ok_ = false;
179         auto tmp = ::new(std::addressof(this->fail)) failure_type(f);
180         assert(tmp == std::addressof(this->fail));
181         (void)tmp;
182         return *this;
183     }
operator =toml::result184     result& operator=(success_type&& s)
185     {
186         this->cleanup();
187         this->is_ok_ = true;
188         auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s));
189         assert(tmp == std::addressof(this->succ));
190         (void)tmp;
191         return *this;
192     }
operator =toml::result193     result& operator=(failure_type&& f)
194     {
195         this->cleanup();
196         this->is_ok_ = false;
197         auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f));
198         assert(tmp == std::addressof(this->fail));
199         (void)tmp;
200         return *this;
201     }
202 
203     template<typename U>
operator =toml::result204     result& operator=(const success<U>& s)
205     {
206         this->cleanup();
207         this->is_ok_ = true;
208         auto tmp = ::new(std::addressof(this->succ)) success_type(s.value);
209         assert(tmp == std::addressof(this->succ));
210         (void)tmp;
211         return *this;
212     }
213     template<typename U>
operator =toml::result214     result& operator=(const failure<U>& f)
215     {
216         this->cleanup();
217         this->is_ok_ = false;
218         auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value);
219         assert(tmp == std::addressof(this->fail));
220         (void)tmp;
221         return *this;
222     }
223     template<typename U>
operator =toml::result224     result& operator=(success<U>&& s)
225     {
226         this->cleanup();
227         this->is_ok_ = true;
228         auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value));
229         assert(tmp == std::addressof(this->succ));
230         (void)tmp;
231         return *this;
232     }
233     template<typename U>
operator =toml::result234     result& operator=(failure<U>&& f)
235     {
236         this->cleanup();
237         this->is_ok_ = false;
238         auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value));
239         assert(tmp == std::addressof(this->fail));
240         (void)tmp;
241         return *this;
242     }
243 
~resulttoml::result244     ~result() noexcept {this->cleanup();}
245 
resulttoml::result246     result(const result& other): is_ok_(other.is_ok())
247     {
248         if(other.is_ok())
249         {
250             auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
251             assert(tmp == std::addressof(this->succ));
252             (void)tmp;
253         }
254         else
255         {
256             auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
257             assert(tmp == std::addressof(this->fail));
258             (void)tmp;
259         }
260     }
resulttoml::result261     result(result&& other): is_ok_(other.is_ok())
262     {
263         if(other.is_ok())
264         {
265             auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
266             assert(tmp == std::addressof(this->succ));
267             (void)tmp;
268         }
269         else
270         {
271             auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
272             assert(tmp == std::addressof(this->fail));
273             (void)tmp;
274         }
275     }
276 
277     template<typename U, typename F>
resulttoml::result278     result(const result<U, F>& other): is_ok_(other.is_ok())
279     {
280         if(other.is_ok())
281         {
282             auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
283             assert(tmp == std::addressof(this->succ));
284             (void)tmp;
285         }
286         else
287         {
288             auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
289             assert(tmp == std::addressof(this->fail));
290             (void)tmp;
291         }
292     }
293     template<typename U, typename F>
resulttoml::result294     result(result<U, F>&& other): is_ok_(other.is_ok())
295     {
296         if(other.is_ok())
297         {
298             auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
299             assert(tmp == std::addressof(this->succ));
300             (void)tmp;
301         }
302         else
303         {
304             auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
305             assert(tmp == std::addressof(this->fail));
306             (void)tmp;
307         }
308     }
309 
operator =toml::result310     result& operator=(const result& other)
311     {
312         this->cleanup();
313         if(other.is_ok())
314         {
315             auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
316             assert(tmp == std::addressof(this->succ));
317             (void)tmp;
318         }
319         else
320         {
321             auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
322             assert(tmp == std::addressof(this->fail));
323             (void)tmp;
324         }
325         is_ok_ = other.is_ok();
326         return *this;
327     }
operator =toml::result328     result& operator=(result&& other)
329     {
330         this->cleanup();
331         if(other.is_ok())
332         {
333             auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
334             assert(tmp == std::addressof(this->succ));
335             (void)tmp;
336         }
337         else
338         {
339             auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
340             assert(tmp == std::addressof(this->fail));
341             (void)tmp;
342         }
343         is_ok_ = other.is_ok();
344         return *this;
345     }
346 
347     template<typename U, typename F>
operator =toml::result348     result& operator=(const result<U, F>& other)
349     {
350         this->cleanup();
351         if(other.is_ok())
352         {
353             auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
354             assert(tmp == std::addressof(this->succ));
355             (void)tmp;
356         }
357         else
358         {
359             auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
360             assert(tmp == std::addressof(this->fail));
361             (void)tmp;
362         }
363         is_ok_ = other.is_ok();
364         return *this;
365     }
366     template<typename U, typename F>
operator =toml::result367     result& operator=(result<U, F>&& other)
368     {
369         this->cleanup();
370         if(other.is_ok())
371         {
372             auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
373             assert(tmp == std::addressof(this->succ));
374             (void)tmp;
375         }
376         else
377         {
378             auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
379             assert(tmp == std::addressof(this->fail));
380             (void)tmp;
381         }
382         is_ok_ = other.is_ok();
383         return *this;
384     }
385 
is_oktoml::result386     bool is_ok()  const noexcept {return is_ok_;}
is_errtoml::result387     bool is_err() const noexcept {return !is_ok_;}
388 
operator booltoml::result389     operator bool() const noexcept {return is_ok_;}
390 
unwraptoml::result391     value_type&       unwrap() &
392     {
393         if(is_err())
394         {
395             throw std::runtime_error("toml::result: bad unwrap: " +
396                                      format_error(this->as_err()));
397         }
398         return this->succ.value;
399     }
unwraptoml::result400     value_type const& unwrap() const&
401     {
402         if(is_err())
403         {
404             throw std::runtime_error("toml::result: bad unwrap: " +
405                                      format_error(this->as_err()));
406         }
407         return this->succ.value;
408     }
unwraptoml::result409     value_type&&      unwrap() &&
410     {
411         if(is_err())
412         {
413             throw std::runtime_error("toml::result: bad unwrap: " +
414                                      format_error(this->as_err()));
415         }
416         return std::move(this->succ.value);
417     }
418 
unwrap_ortoml::result419     value_type&       unwrap_or(value_type& opt) &
420     {
421         if(is_err()) {return opt;}
422         return this->succ.value;
423     }
unwrap_ortoml::result424     value_type const& unwrap_or(value_type const& opt) const&
425     {
426         if(is_err()) {return opt;}
427         return this->succ.value;
428     }
unwrap_ortoml::result429     value_type        unwrap_or(value_type opt) &&
430     {
431         if(is_err()) {return opt;}
432         return this->succ.value;
433     }
434 
unwrap_errtoml::result435     error_type&       unwrap_err() &
436     {
437         if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");}
438         return this->fail.value;
439     }
unwrap_errtoml::result440     error_type const& unwrap_err() const&
441     {
442         if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");}
443         return this->fail.value;
444     }
unwrap_errtoml::result445     error_type&&      unwrap_err() &&
446     {
447         if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");}
448         return std::move(this->fail.value);
449     }
450 
as_oktoml::result451     value_type&       as_ok() &      noexcept {return this->succ.value;}
as_oktoml::result452     value_type const& as_ok() const& noexcept {return this->succ.value;}
as_oktoml::result453     value_type&&      as_ok() &&     noexcept {return std::move(this->succ.value);}
454 
as_errtoml::result455     error_type&       as_err() &      noexcept {return this->fail.value;}
as_errtoml::result456     error_type const& as_err() const& noexcept {return this->fail.value;}
as_errtoml::result457     error_type&&      as_err() &&     noexcept {return std::move(this->fail.value);}
458 
459 
460     // prerequisities
461     // F: T -> U
462     // retval: result<U, E>
463     template<typename F>
464     result<detail::return_type_of_t<F, value_type&>, error_type>
maptoml::result465     map(F&& f) &
466     {
467         if(this->is_ok()){return ok(f(this->as_ok()));}
468         return err(this->as_err());
469     }
470     template<typename F>
471     result<detail::return_type_of_t<F, value_type const&>, error_type>
maptoml::result472     map(F&& f) const&
473     {
474         if(this->is_ok()){return ok(f(this->as_ok()));}
475         return err(this->as_err());
476     }
477     template<typename F>
478     result<detail::return_type_of_t<F, value_type &&>, error_type>
maptoml::result479     map(F&& f) &&
480     {
481         if(this->is_ok()){return ok(f(std::move(this->as_ok())));}
482         return err(std::move(this->as_err()));
483     }
484 
485     // prerequisities
486     // F: E -> F
487     // retval: result<T, F>
488     template<typename F>
489     result<value_type, detail::return_type_of_t<F, error_type&>>
map_errtoml::result490     map_err(F&& f) &
491     {
492         if(this->is_err()){return err(f(this->as_err()));}
493         return ok(this->as_ok());
494     }
495     template<typename F>
496     result<value_type, detail::return_type_of_t<F, error_type const&>>
map_errtoml::result497     map_err(F&& f) const&
498     {
499         if(this->is_err()){return err(f(this->as_err()));}
500         return ok(this->as_ok());
501     }
502     template<typename F>
503     result<value_type, detail::return_type_of_t<F, error_type&&>>
map_errtoml::result504     map_err(F&& f) &&
505     {
506         if(this->is_err()){return err(f(std::move(this->as_err())));}
507         return ok(std::move(this->as_ok()));
508     }
509 
510     // prerequisities
511     // F: T -> U
512     // retval: U
513     template<typename F, typename U>
514     detail::return_type_of_t<F, value_type&>
map_or_elsetoml::result515     map_or_else(F&& f, U&& opt) &
516     {
517         if(this->is_err()){return std::forward<U>(opt);}
518         return f(this->as_ok());
519     }
520     template<typename F, typename U>
521     detail::return_type_of_t<F, value_type const&>
map_or_elsetoml::result522     map_or_else(F&& f, U&& opt) const&
523     {
524         if(this->is_err()){return std::forward<U>(opt);}
525         return f(this->as_ok());
526     }
527     template<typename F, typename U>
528     detail::return_type_of_t<F, value_type&&>
map_or_elsetoml::result529     map_or_else(F&& f, U&& opt) &&
530     {
531         if(this->is_err()){return std::forward<U>(opt);}
532         return f(std::move(this->as_ok()));
533     }
534 
535     // prerequisities
536     // F: E -> U
537     // retval: U
538     template<typename F, typename U>
539     detail::return_type_of_t<F, error_type&>
map_err_or_elsetoml::result540     map_err_or_else(F&& f, U&& opt) &
541     {
542         if(this->is_ok()){return std::forward<U>(opt);}
543         return f(this->as_err());
544     }
545     template<typename F, typename U>
546     detail::return_type_of_t<F, error_type const&>
map_err_or_elsetoml::result547     map_err_or_else(F&& f, U&& opt) const&
548     {
549         if(this->is_ok()){return std::forward<U>(opt);}
550         return f(this->as_err());
551     }
552     template<typename F, typename U>
553     detail::return_type_of_t<F, error_type&&>
map_err_or_elsetoml::result554     map_err_or_else(F&& f, U&& opt) &&
555     {
556         if(this->is_ok()){return std::forward<U>(opt);}
557         return f(std::move(this->as_err()));
558     }
559 
560     // prerequisities:
561     // F: func T -> U
562     // toml::err(error_type) should be convertible to U.
563     // normally, type U is another result<S, F> and E is convertible to F
564     template<typename F>
565     detail::return_type_of_t<F, value_type&>
and_thentoml::result566     and_then(F&& f) &
567     {
568         if(this->is_ok()){return f(this->as_ok());}
569         return err(this->as_err());
570     }
571     template<typename F>
572     detail::return_type_of_t<F, value_type const&>
and_thentoml::result573     and_then(F&& f) const&
574     {
575         if(this->is_ok()){return f(this->as_ok());}
576         return err(this->as_err());
577     }
578     template<typename F>
579     detail::return_type_of_t<F, value_type&&>
and_thentoml::result580     and_then(F&& f) &&
581     {
582         if(this->is_ok()){return f(std::move(this->as_ok()));}
583         return err(std::move(this->as_err()));
584     }
585 
586     // prerequisities:
587     // F: func E -> U
588     // toml::ok(value_type) should be convertible to U.
589     // normally, type U is another result<S, F> and T is convertible to S
590     template<typename F>
591     detail::return_type_of_t<F, error_type&>
or_elsetoml::result592     or_else(F&& f) &
593     {
594         if(this->is_err()){return f(this->as_err());}
595         return ok(this->as_ok());
596     }
597     template<typename F>
598     detail::return_type_of_t<F, error_type const&>
or_elsetoml::result599     or_else(F&& f) const&
600     {
601         if(this->is_err()){return f(this->as_err());}
602         return ok(this->as_ok());
603     }
604     template<typename F>
605     detail::return_type_of_t<F, error_type&&>
or_elsetoml::result606     or_else(F&& f) &&
607     {
608         if(this->is_err()){return f(std::move(this->as_err()));}
609         return ok(std::move(this->as_ok()));
610     }
611 
612     // if *this is error, returns *this. otherwise, returns other.
and_othertoml::result613     result and_other(const result& other) const&
614     {
615         return this->is_err() ? *this : other;
616     }
and_othertoml::result617     result and_other(result&& other) &&
618     {
619         return this->is_err() ? std::move(*this) : std::move(other);
620     }
621 
622     // if *this is okay, returns *this. otherwise, returns other.
or_othertoml::result623     result or_other(const result& other) const&
624     {
625         return this->is_ok() ? *this : other;
626     }
or_othertoml::result627     result or_other(result&& other) &&
628     {
629         return this->is_ok() ? std::move(*this) : std::move(other);
630     }
631 
swaptoml::result632     void swap(result<T, E>& other)
633     {
634         result<T, E> tmp(std::move(*this));
635         *this = std::move(other);
636         other = std::move(tmp);
637         return ;
638     }
639 
640   private:
641 
format_errortoml::result642     static std::string format_error(std::exception const& excpt)
643     {
644         return std::string(excpt.what());
645     }
646     template<typename U, typename std::enable_if<!std::is_base_of<
647         std::exception, U>::value, std::nullptr_t>::type = nullptr>
format_errortoml::result648     static std::string format_error(U const& others)
649     {
650         std::ostringstream oss; oss << others;
651         return oss.str();
652     }
653 
cleanuptoml::result654     void cleanup() noexcept
655     {
656         if(this->is_ok_) {this->succ.~success_type();}
657         else             {this->fail.~failure_type();}
658         return;
659     }
660 
661   private:
662 
663     bool      is_ok_;
664     union
665     {
666         success_type succ;
667         failure_type fail;
668     };
669 };
670 
671 template<typename T, typename E>
swap(result<T,E> & lhs,result<T,E> & rhs)672 void swap(result<T, E>& lhs, result<T, E>& rhs)
673 {
674     lhs.swap(rhs);
675     return;
676 }
677 
678 // this might be confusing because it eagerly evaluated, while in the other
679 // cases operator && and || are short-circuited.
680 //
681 // template<typename T, typename E>
682 // inline result<T, E>
683 // operator&&(const result<T, E>& lhs, const result<T, E>& rhs) noexcept
684 // {
685 //     return lhs.is_ok() ? rhs : lhs;
686 // }
687 //
688 // template<typename T, typename E>
689 // inline result<T, E>
690 // operator||(const result<T, E>& lhs, const result<T, E>& rhs) noexcept
691 // {
692 //     return lhs.is_ok() ? lhs : rhs;
693 // }
694 
695 // ----------------------------------------------------------------------------
696 // re-use result<T, E> as a optional<T> with none_t
697 
698 namespace detail
699 {
700 struct none_t {};
operator ==(const none_t &,const none_t &)701 inline bool operator==(const none_t&, const none_t&) noexcept {return true;}
operator !=(const none_t &,const none_t &)702 inline bool operator!=(const none_t&, const none_t&) noexcept {return false;}
operator <(const none_t &,const none_t &)703 inline bool operator< (const none_t&, const none_t&) noexcept {return false;}
operator <=(const none_t &,const none_t &)704 inline bool operator<=(const none_t&, const none_t&) noexcept {return true;}
operator >(const none_t &,const none_t &)705 inline bool operator> (const none_t&, const none_t&) noexcept {return false;}
operator >=(const none_t &,const none_t &)706 inline bool operator>=(const none_t&, const none_t&) noexcept {return true;}
707 template<typename charT, typename traitsT>
708 std::basic_ostream<charT, traitsT>&
operator <<(std::basic_ostream<charT,traitsT> & os,const none_t &)709 operator<<(std::basic_ostream<charT, traitsT>& os, const none_t&)
710 {
711     os << "none";
712     return os;
713 }
none()714 inline failure<none_t> none() noexcept {return failure<none_t>{none_t{}};}
715 } // detail
716 } // toml11
717 #endif// TOML11_RESULT_H
718