mirror of
https://github.com/ToruNiina/toml11.git
synced 2025-09-17 09:08:08 +08:00
487 lines
14 KiB
C++
487 lines
14 KiB
C++
#ifndef TOML11_RESULT_HPP
|
|
#define TOML11_RESULT_HPP
|
|
|
|
#include "compat.hpp"
|
|
#include "exception.hpp"
|
|
|
|
#include <ostream>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#include <cassert>
|
|
|
|
namespace toml
|
|
{
|
|
|
|
struct bad_result_access final : public ::toml::exception
|
|
{
|
|
public:
|
|
explicit bad_result_access(std::string what_arg)
|
|
: what_(std::move(what_arg))
|
|
{}
|
|
~bad_result_access() noexcept override = default;
|
|
const char* what() const noexcept override {return what_.c_str();}
|
|
|
|
private:
|
|
std::string what_;
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
template<typename T>
|
|
struct success
|
|
{
|
|
static_assert( ! std::is_void<T>::value, "");
|
|
|
|
using value_type = T;
|
|
|
|
explicit success(value_type v)
|
|
noexcept(std::is_nothrow_move_constructible<value_type>::value)
|
|
: value(std::move(v))
|
|
{}
|
|
|
|
template<typename U, cxx::enable_if_t<
|
|
std::is_convertible<cxx::remove_cvref_t<U>, T>::value,
|
|
std::nullptr_t> = nullptr>
|
|
explicit success(U&& v): value(std::forward<U>(v)) {}
|
|
|
|
template<typename U>
|
|
explicit success(success<U> v): value(std::move(v.value)) {}
|
|
|
|
~success() = default;
|
|
success(const success&) = default;
|
|
success(success&&) = default;
|
|
success& operator=(const success&) = default;
|
|
success& operator=(success&&) = default;
|
|
|
|
value_type& get() noexcept {return value;}
|
|
value_type const& get() const noexcept {return value;}
|
|
|
|
private:
|
|
|
|
value_type value;
|
|
};
|
|
|
|
template<typename T>
|
|
struct success<std::reference_wrapper<T>>
|
|
{
|
|
static_assert( ! std::is_void<T>::value, "");
|
|
|
|
using value_type = T;
|
|
|
|
explicit success(std::reference_wrapper<value_type> v) noexcept
|
|
: value(std::move(v))
|
|
{}
|
|
|
|
~success() = default;
|
|
success(const success&) = default;
|
|
success(success&&) = default;
|
|
success& operator=(const success&) = default;
|
|
success& operator=(success&&) = default;
|
|
|
|
value_type& get() noexcept {return value.get();}
|
|
value_type const& get() const noexcept {return value.get();}
|
|
|
|
private:
|
|
|
|
std::reference_wrapper<value_type> value;
|
|
};
|
|
|
|
template<typename T>
|
|
success<typename std::decay<T>::type> ok(T&& v)
|
|
{
|
|
return success<typename std::decay<T>::type>(std::forward<T>(v));
|
|
}
|
|
template<std::size_t N>
|
|
success<std::string> ok(const char (&literal)[N])
|
|
{
|
|
return success<std::string>(std::string(literal));
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
template<typename T>
|
|
struct failure
|
|
{
|
|
using value_type = T;
|
|
|
|
explicit failure(value_type v)
|
|
noexcept(std::is_nothrow_move_constructible<value_type>::value)
|
|
: value(std::move(v))
|
|
{}
|
|
|
|
template<typename U, cxx::enable_if_t<
|
|
std::is_convertible<cxx::remove_cvref_t<U>, T>::value,
|
|
std::nullptr_t> = nullptr>
|
|
explicit failure(U&& v): value(std::forward<U>(v)) {}
|
|
|
|
template<typename U>
|
|
explicit failure(failure<U> v): value(std::move(v.value)) {}
|
|
|
|
~failure() = default;
|
|
failure(const failure&) = default;
|
|
failure(failure&&) = default;
|
|
failure& operator=(const failure&) = default;
|
|
failure& operator=(failure&&) = default;
|
|
|
|
value_type& get() noexcept {return value;}
|
|
value_type const& get() const noexcept {return value;}
|
|
|
|
private:
|
|
|
|
value_type value;
|
|
};
|
|
|
|
template<typename T>
|
|
struct failure<std::reference_wrapper<T>>
|
|
{
|
|
using value_type = T;
|
|
|
|
explicit failure(std::reference_wrapper<value_type> v) noexcept
|
|
: value(std::move(v))
|
|
{}
|
|
|
|
~failure() = default;
|
|
failure(const failure&) = default;
|
|
failure(failure&&) = default;
|
|
failure& operator=(const failure&) = default;
|
|
failure& operator=(failure&&) = default;
|
|
|
|
value_type& get() noexcept {return value.get();}
|
|
value_type const& get() const noexcept {return value.get();}
|
|
|
|
private:
|
|
|
|
std::reference_wrapper<value_type> value;
|
|
};
|
|
|
|
template<typename T>
|
|
failure<typename std::decay<T>::type> err(T&& v)
|
|
{
|
|
return failure<typename std::decay<T>::type>(std::forward<T>(v));
|
|
}
|
|
|
|
template<std::size_t N>
|
|
failure<std::string> err(const char (&literal)[N])
|
|
{
|
|
return failure<std::string>(std::string(literal));
|
|
}
|
|
|
|
/* ============================================================================
|
|
* _ _
|
|
* _ _ ___ ____ _| | |_
|
|
* | '_/ -_|_-< || | | _|
|
|
* |_| \___/__/\_,_|_|\__|
|
|
*/
|
|
|
|
template<typename T, typename E>
|
|
struct result
|
|
{
|
|
using success_type = success<T>;
|
|
using failure_type = failure<E>;
|
|
using value_type = typename success_type::value_type;
|
|
using error_type = typename failure_type::value_type;
|
|
|
|
result(success_type s): is_ok_(true), succ_(std::move(s)) {}
|
|
result(failure_type f): is_ok_(false), fail_(std::move(f)) {}
|
|
|
|
template<typename U, cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<U>, value_type>>,
|
|
std::is_convertible<cxx::remove_cvref_t<U>, value_type>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
result(success<U> s): is_ok_(true), succ_(std::move(s.value)) {}
|
|
|
|
template<typename U, cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<U>, error_type>>,
|
|
std::is_convertible<cxx::remove_cvref_t<U>, error_type>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
result(failure<U> f): is_ok_(false), fail_(std::move(f.value)) {}
|
|
|
|
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<typename U>
|
|
result& operator=(success<U> 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<typename U>
|
|
result& operator=(failure<U> 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.succ_);
|
|
assert(tmp == std::addressof(this->succ_));
|
|
(void)tmp;
|
|
}
|
|
else
|
|
{
|
|
auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_);
|
|
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.succ_));
|
|
assert(tmp == std::addressof(this->succ_));
|
|
(void)tmp;
|
|
}
|
|
else
|
|
{
|
|
auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_));
|
|
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.succ_);
|
|
assert(tmp == std::addressof(this->succ_));
|
|
(void)tmp;
|
|
}
|
|
else
|
|
{
|
|
auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_);
|
|
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.succ_));
|
|
assert(tmp == std::addressof(this->succ_));
|
|
(void)tmp;
|
|
}
|
|
else
|
|
{
|
|
auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_));
|
|
assert(tmp == std::addressof(this->fail_));
|
|
(void)tmp;
|
|
}
|
|
is_ok_ = other.is_ok();
|
|
return *this;
|
|
}
|
|
|
|
template<typename U, typename F, cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<U>, value_type>>,
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<F>, error_type>>,
|
|
std::is_convertible<cxx::remove_cvref_t<U>, value_type>,
|
|
std::is_convertible<cxx::remove_cvref_t<F>, error_type>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
result(result<U, F> 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<typename U, typename F, cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<U>, value_type>>,
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<F>, error_type>>,
|
|
std::is_convertible<cxx::remove_cvref_t<U>, value_type>,
|
|
std::is_convertible<cxx::remove_cvref_t<F>, error_type>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
result& operator=(result<U, F> 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_;}
|
|
|
|
explicit operator bool() const noexcept {return is_ok_;}
|
|
|
|
value_type& unwrap(cxx::source_location loc = cxx::source_location::current())
|
|
{
|
|
if(this->is_err())
|
|
{
|
|
throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc));
|
|
}
|
|
return this->succ_.get();
|
|
}
|
|
value_type const& unwrap(cxx::source_location loc = cxx::source_location::current()) const
|
|
{
|
|
if(this->is_err())
|
|
{
|
|
throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc));
|
|
}
|
|
return this->succ_.get();
|
|
}
|
|
|
|
value_type& unwrap_or(value_type& opt) noexcept
|
|
{
|
|
if(this->is_err()) {return opt;}
|
|
return this->succ_.get();
|
|
}
|
|
value_type const& unwrap_or(value_type const& opt) const noexcept
|
|
{
|
|
if(this->is_err()) {return opt;}
|
|
return this->succ_.get();
|
|
}
|
|
|
|
error_type& unwrap_err(cxx::source_location loc = cxx::source_location::current())
|
|
{
|
|
if(this->is_ok())
|
|
{
|
|
throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc));
|
|
}
|
|
return this->fail_.get();
|
|
}
|
|
error_type const& unwrap_err(cxx::source_location loc = cxx::source_location::current()) const
|
|
{
|
|
if(this->is_ok())
|
|
{
|
|
throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc));
|
|
}
|
|
return this->fail_.get();
|
|
}
|
|
|
|
value_type& as_ok() noexcept
|
|
{
|
|
assert(this->is_ok());
|
|
return this->succ_.get();
|
|
}
|
|
value_type const& as_ok() const noexcept
|
|
{
|
|
assert(this->is_ok());
|
|
return this->succ_.get();
|
|
}
|
|
|
|
error_type& as_err() noexcept
|
|
{
|
|
assert(this->is_err());
|
|
return this->fail_.get();
|
|
}
|
|
error_type const& as_err() const noexcept
|
|
{
|
|
assert(this->is_err());
|
|
return this->fail_.get();
|
|
}
|
|
|
|
private:
|
|
|
|
void cleanup() noexcept
|
|
{
|
|
#if defined(__GNUC__) && ! defined(__clang__)
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wduplicated-branches"
|
|
#endif
|
|
|
|
if(this->is_ok_) {this->succ_.~success_type();}
|
|
else {this->fail_.~failure_type();}
|
|
|
|
#if defined(__GNUC__) && ! defined(__clang__)
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
private:
|
|
|
|
bool is_ok_;
|
|
union
|
|
{
|
|
success_type succ_;
|
|
failure_type fail_;
|
|
};
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
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;}
|
|
inline std::ostream& operator<<(std::ostream& os, const none_t&)
|
|
{
|
|
os << "none";
|
|
return os;
|
|
}
|
|
} // detail
|
|
|
|
inline success<detail::none_t> ok() noexcept
|
|
{
|
|
return success<detail::none_t>(detail::none_t{});
|
|
}
|
|
inline failure<detail::none_t> err() noexcept
|
|
{
|
|
return failure<detail::none_t>(detail::none_t{});
|
|
}
|
|
|
|
} // toml
|
|
#endif // TOML11_RESULT_HPP
|