// Copyright Toru Niina 2017. // Distributed under the MIT License. #ifndef TOML11_RESULT_HPP #define TOML11_RESULT_HPP #include "traits.hpp" #include #include #include #include #include #include #include namespace toml { template struct success { using value_type = T; value_type value; explicit success(const value_type& v) noexcept(std::is_nothrow_copy_constructible::value) : value(v) {} explicit success(value_type&& v) noexcept(std::is_nothrow_move_constructible::value) : value(std::move(v)) {} template explicit success(U&& v): value(std::forward(v)) {} template explicit success(const success& v): value(v.value) {} template explicit success(success&& v): value(std::move(v.value)) {} ~success() = default; success(const success&) = default; success(success&&) = default; success& operator=(const success&) = default; success& operator=(success&&) = default; }; template struct failure { using value_type = T; value_type value; explicit failure(const value_type& v) noexcept(std::is_nothrow_copy_constructible::value) : value(v) {} explicit failure(value_type&& v) noexcept(std::is_nothrow_move_constructible::value) : value(std::move(v)) {} template explicit failure(U&& v): value(std::forward(v)) {} template explicit failure(const failure& v): value(v.value) {} template explicit failure(failure&& v): value(std::move(v.value)) {} ~failure() = default; failure(const failure&) = default; failure(failure&&) = default; failure& operator=(const failure&) = default; failure& operator=(failure&&) = default; }; template success::type>::type> ok(T&& v) { return success< typename std::remove_cv::type>::type >(std::forward(v)); } template failure::type>::type> err(T&& v) { return failure< typename std::remove_cv::type>::type >(std::forward(v)); } inline success ok(const char* literal) { return success(std::string(literal)); } inline failure err(const char* literal) { return failure(std::string(literal)); } template struct result { using value_type = T; using error_type = E; using success_type = success; using failure_type = failure; result(const success_type& s): is_ok_(true) { auto tmp = ::new(std::addressof(this->succ)) success_type(s); assert(tmp == std::addressof(this->succ)); (void)tmp; } result(const failure_type& f): is_ok_(false) { auto tmp = ::new(std::addressof(this->fail)) failure_type(f); assert(tmp == std::addressof(this->fail)); (void)tmp; } result(success_type&& s): is_ok_(true) { auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); assert(tmp == std::addressof(this->succ)); (void)tmp; } result(failure_type&& f): is_ok_(false) { auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); assert(tmp == std::addressof(this->fail)); (void)tmp; } template result(const success& s): is_ok_(true) { auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); assert(tmp == std::addressof(this->succ)); (void)tmp; } template result(const failure& f): is_ok_(false) { auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); assert(tmp == std::addressof(this->fail)); (void)tmp; } template result(success&& s): is_ok_(true) { auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); assert(tmp == std::addressof(this->succ)); (void)tmp; } template result(failure&& f): is_ok_(false) { auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); assert(tmp == std::addressof(this->fail)); (void)tmp; } result& operator=(const success_type& s) { this->cleanup(); this->is_ok_ = true; auto tmp = ::new(std::addressof(this->succ)) success_type(s); assert(tmp == std::addressof(this->succ)); (void)tmp; return *this; } result& operator=(const failure_type& f) { this->cleanup(); this->is_ok_ = false; auto tmp = ::new(std::addressof(this->fail)) failure_type(f); assert(tmp == std::addressof(this->fail)); (void)tmp; return *this; } result& operator=(success_type&& s) { this->cleanup(); this->is_ok_ = true; auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); assert(tmp == std::addressof(this->succ)); (void)tmp; return *this; } result& operator=(failure_type&& f) { this->cleanup(); this->is_ok_ = false; auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); assert(tmp == std::addressof(this->fail)); (void)tmp; return *this; } template result& operator=(const success& s) { this->cleanup(); this->is_ok_ = true; auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); assert(tmp == std::addressof(this->succ)); (void)tmp; return *this; } template result& operator=(const failure& f) { this->cleanup(); this->is_ok_ = false; auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); assert(tmp == std::addressof(this->fail)); (void)tmp; return *this; } template result& operator=(success&& s) { this->cleanup(); this->is_ok_ = true; auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); assert(tmp == std::addressof(this->succ)); (void)tmp; return *this; } template result& operator=(failure&& f) { this->cleanup(); this->is_ok_ = false; auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); assert(tmp == std::addressof(this->fail)); (void)tmp; return *this; } ~result() noexcept {this->cleanup();} result(const result& other): is_ok_(other.is_ok()) { if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); assert(tmp == std::addressof(this->succ)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); assert(tmp == std::addressof(this->fail)); (void)tmp; } } result(result&& other): is_ok_(other.is_ok()) { if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); assert(tmp == std::addressof(this->succ)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); assert(tmp == std::addressof(this->fail)); (void)tmp; } } template result(const result& other): is_ok_(other.is_ok()) { if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); assert(tmp == std::addressof(this->succ)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); assert(tmp == std::addressof(this->fail)); (void)tmp; } } template result(result&& other): is_ok_(other.is_ok()) { if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); assert(tmp == std::addressof(this->succ)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); assert(tmp == std::addressof(this->fail)); (void)tmp; } } result& operator=(const result& other) { this->cleanup(); if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); assert(tmp == std::addressof(this->succ)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); assert(tmp == std::addressof(this->fail)); (void)tmp; } is_ok_ = other.is_ok(); return *this; } result& operator=(result&& other) { this->cleanup(); if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); assert(tmp == std::addressof(this->succ)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); assert(tmp == std::addressof(this->fail)); (void)tmp; } is_ok_ = other.is_ok(); return *this; } template result& operator=(const result& other) { this->cleanup(); if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); assert(tmp == std::addressof(this->succ)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); assert(tmp == std::addressof(this->fail)); (void)tmp; } is_ok_ = other.is_ok(); return *this; } template result& operator=(result&& other) { this->cleanup(); if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); assert(tmp == std::addressof(this->succ)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); assert(tmp == std::addressof(this->fail)); (void)tmp; } is_ok_ = other.is_ok(); return *this; } bool is_ok() const noexcept {return is_ok_;} bool is_err() const noexcept {return !is_ok_;} operator bool() const noexcept {return is_ok_;} value_type& unwrap() & { if(is_err()) { throw std::runtime_error("toml::result: bad unwrap: " + format_error(this->as_err())); } return this->succ.value; } value_type const& unwrap() const& { if(is_err()) { throw std::runtime_error("toml::result: bad unwrap: " + format_error(this->as_err())); } return this->succ.value; } value_type&& unwrap() && { if(is_err()) { throw std::runtime_error("toml::result: bad unwrap: " + format_error(this->as_err())); } return std::move(this->succ.value); } value_type& unwrap_or(value_type& opt) & { if(is_err()) {return opt;} return this->succ.value; } value_type const& unwrap_or(value_type const& opt) const& { if(is_err()) {return opt;} return this->succ.value; } value_type unwrap_or(value_type opt) && { if(is_err()) {return opt;} return this->succ.value; } error_type& unwrap_err() & { if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} return this->fail.value; } error_type const& unwrap_err() const& { if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} return this->fail.value; } error_type&& unwrap_err() && { if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} return std::move(this->fail.value); } value_type& as_ok() & noexcept {return this->succ.value;} value_type const& as_ok() const& noexcept {return this->succ.value;} value_type&& as_ok() && noexcept {return std::move(this->succ.value);} error_type& as_err() & noexcept {return this->fail.value;} error_type const& as_err() const& noexcept {return this->fail.value;} error_type&& as_err() && noexcept {return std::move(this->fail.value);} // prerequisities // F: T -> U // retval: result template result, error_type> map(F&& f) & { if(this->is_ok()){return ok(f(this->as_ok()));} return err(this->as_err()); } template result, error_type> map(F&& f) const& { if(this->is_ok()){return ok(f(this->as_ok()));} return err(this->as_err()); } template result, error_type> map(F&& f) && { if(this->is_ok()){return ok(f(std::move(this->as_ok())));} return err(std::move(this->as_err())); } // prerequisities // F: E -> F // retval: result template result> map_err(F&& f) & { if(this->is_err()){return err(f(this->as_err()));} return ok(this->as_ok()); } template result> map_err(F&& f) const& { if(this->is_err()){return err(f(this->as_err()));} return ok(this->as_ok()); } template result> map_err(F&& f) && { if(this->is_err()){return err(f(std::move(this->as_err())));} return ok(std::move(this->as_ok())); } // prerequisities // F: T -> U // retval: U template detail::return_type_of_t map_or_else(F&& f, U&& opt) & { if(this->is_err()){return std::forward(opt);} return f(this->as_ok()); } template detail::return_type_of_t map_or_else(F&& f, U&& opt) const& { if(this->is_err()){return std::forward(opt);} return f(this->as_ok()); } template detail::return_type_of_t map_or_else(F&& f, U&& opt) && { if(this->is_err()){return std::forward(opt);} return f(std::move(this->as_ok())); } // prerequisities // F: E -> U // retval: U template detail::return_type_of_t map_err_or_else(F&& f, U&& opt) & { if(this->is_ok()){return std::forward(opt);} return f(this->as_err()); } template detail::return_type_of_t map_err_or_else(F&& f, U&& opt) const& { if(this->is_ok()){return std::forward(opt);} return f(this->as_err()); } template detail::return_type_of_t map_err_or_else(F&& f, U&& opt) && { if(this->is_ok()){return std::forward(opt);} return f(std::move(this->as_err())); } // prerequisities: // F: func T -> U // toml::err(error_type) should be convertible to U. // normally, type U is another result and E is convertible to F template detail::return_type_of_t and_then(F&& f) & { if(this->is_ok()){return f(this->as_ok());} return err(this->as_err()); } template detail::return_type_of_t and_then(F&& f) const& { if(this->is_ok()){return f(this->as_ok());} return err(this->as_err()); } template detail::return_type_of_t and_then(F&& f) && { if(this->is_ok()){return f(std::move(this->as_ok()));} return err(std::move(this->as_err())); } // prerequisities: // F: func E -> U // toml::ok(value_type) should be convertible to U. // normally, type U is another result and T is convertible to S template detail::return_type_of_t or_else(F&& f) & { if(this->is_err()){return f(this->as_err());} return ok(this->as_ok()); } template detail::return_type_of_t or_else(F&& f) const& { if(this->is_err()){return f(this->as_err());} return ok(this->as_ok()); } template detail::return_type_of_t or_else(F&& f) && { if(this->is_err()){return f(std::move(this->as_err()));} return ok(std::move(this->as_ok())); } // if *this is error, returns *this. otherwise, returns other. result and_other(const result& other) const& { return this->is_err() ? *this : other; } result and_other(result&& other) && { return this->is_err() ? std::move(*this) : std::move(other); } // if *this is okay, returns *this. otherwise, returns other. result or_other(const result& other) const& { return this->is_ok() ? *this : other; } result or_other(result&& other) && { return this->is_ok() ? std::move(*this) : std::move(other); } void swap(result& other) { result tmp(std::move(*this)); *this = std::move(other); other = std::move(tmp); return ; } private: static std::string format_error(std::exception const& excpt) { return std::string(excpt.what()); } template::value, std::nullptr_t>::type = nullptr> static std::string format_error(U const& others) { std::ostringstream oss; oss << others; return oss.str(); } void cleanup() noexcept { if(this->is_ok_) {this->succ.~success_type();} else {this->fail.~failure_type();} return; } private: bool is_ok_; union { success_type succ; failure_type fail; }; }; template void swap(result& lhs, result& rhs) { lhs.swap(rhs); return; } // this might be confusing because it eagerly evaluated, while in the other // cases operator && and || are short-circuited. // // template // inline result // operator&&(const result& lhs, const result& rhs) noexcept // { // return lhs.is_ok() ? rhs : lhs; // } // // template // inline result // operator||(const result& lhs, const result& rhs) noexcept // { // return lhs.is_ok() ? lhs : rhs; // } // ---------------------------------------------------------------------------- // re-use result as a optional with none_t namespace detail { struct none_t {}; inline bool operator==(const none_t&, const none_t&) noexcept {return true;} inline bool operator!=(const none_t&, const none_t&) noexcept {return false;} inline bool operator< (const none_t&, const none_t&) noexcept {return false;} inline bool operator<=(const none_t&, const none_t&) noexcept {return true;} inline bool operator> (const none_t&, const none_t&) noexcept {return false;} inline bool operator>=(const none_t&, const none_t&) noexcept {return true;} template std::basic_ostream& operator<<(std::basic_ostream& os, const none_t&) { os << "none"; return os; } inline failure none() noexcept {return failure{none_t{}};} } // detail } // toml11 #endif// TOML11_RESULT_H