Merge branch 'result' into combinator

- fix some of the constructors of result
- add some utility member functions to boost
This commit is contained in:
ToruNiina 2018-12-06 12:57:58 +09:00
commit f8aa604959
2 changed files with 416 additions and 4 deletions

View File

@ -109,3 +109,260 @@ BOOST_AUTO_TEST_CASE(test_assignment)
BOOST_CHECK_EQUAL(result.unwrap_err(), "hoge");
}
}
BOOST_AUTO_TEST_CASE(test_map)
{
{
const toml::result<int, std::string> result(toml::ok(42));
const auto mapped = result.map(
[](const int i) -> int {
return i * 2;
});
BOOST_CHECK(!!mapped);
BOOST_CHECK(mapped.is_ok());
BOOST_CHECK(!mapped.is_err());
BOOST_CHECK_EQUAL(mapped.unwrap(), 42 * 2);
}
{
toml::result<std::unique_ptr<int>, std::string>
result(toml::ok(std::unique_ptr<int>(new int(42))));
const auto mapped = std::move(result).map(
[](std::unique_ptr<int> i) -> int {
return *i;
});
BOOST_CHECK(!!mapped);
BOOST_CHECK(mapped.is_ok());
BOOST_CHECK(!mapped.is_err());
BOOST_CHECK_EQUAL(mapped.unwrap(), 42);
}
{
const toml::result<int, std::string> result(toml::err<std::string>("hoge"));
const auto mapped = result.map(
[](const int i) -> int {
return i * 2;
});
BOOST_CHECK(!mapped);
BOOST_CHECK(!mapped.is_ok());
BOOST_CHECK(mapped.is_err());
BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hoge");
}
{
toml::result<std::unique_ptr<int>, std::string>
result(toml::err<std::string>("hoge"));
const auto mapped = std::move(result).map(
[](std::unique_ptr<int> i) -> int {
return *i;
});
BOOST_CHECK(!mapped);
BOOST_CHECK(!mapped.is_ok());
BOOST_CHECK(mapped.is_err());
BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hoge");
}
}
BOOST_AUTO_TEST_CASE(test_map_err)
{
{
const toml::result<int, std::string> result(toml::ok(42));
const auto mapped = result.map_err(
[](const std::string s) -> std::string {
return s + s;
});
BOOST_CHECK(!!mapped);
BOOST_CHECK(mapped.is_ok());
BOOST_CHECK(!mapped.is_err());
BOOST_CHECK_EQUAL(mapped.unwrap(), 42);
}
{
toml::result<std::unique_ptr<int>, std::string>
result(toml::ok(std::unique_ptr<int>(new int(42))));
const auto mapped = std::move(result).map_err(
[](const std::string s) -> std::string {
return s + s;
});
BOOST_CHECK(!!mapped);
BOOST_CHECK(mapped.is_ok());
BOOST_CHECK(!mapped.is_err());
BOOST_CHECK_EQUAL(*(mapped.unwrap()), 42);
}
{
const toml::result<int, std::string> result(toml::err<std::string>("hoge"));
const auto mapped = result.map_err(
[](const std::string s) -> std::string {
return s + s;
});
BOOST_CHECK(!mapped);
BOOST_CHECK(!mapped.is_ok());
BOOST_CHECK(mapped.is_err());
BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hogehoge");
}
{
toml::result<int, std::unique_ptr<std::string>>
result(toml::err(std::unique_ptr<std::string>(new std::string("hoge"))));
const auto mapped = std::move(result).map_err(
[](std::unique_ptr<std::string> p) -> std::string {
return *p;
});
BOOST_CHECK(!mapped);
BOOST_CHECK(!mapped.is_ok());
BOOST_CHECK(mapped.is_err());
BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hoge");
}
}
BOOST_AUTO_TEST_CASE(test_map_or_else)
{
{
const toml::result<int, std::string> result(toml::ok(42));
const auto mapped = result.map_or_else(
[](const int i) -> int {
return i * 2;
}, 54);
BOOST_CHECK_EQUAL(mapped, 42 * 2);
}
{
toml::result<std::unique_ptr<int>, std::string>
result(toml::ok(std::unique_ptr<int>(new int(42))));
const auto mapped = std::move(result).map_or_else(
[](std::unique_ptr<int> i) -> int {
return *i;
}, 54);
BOOST_CHECK_EQUAL(mapped, 42);
}
{
const toml::result<int, std::string> result(toml::err<std::string>("hoge"));
const auto mapped = result.map_or_else(
[](const int i) -> int {
return i * 2;
}, 54);
BOOST_CHECK_EQUAL(mapped, 54);
}
{
toml::result<std::unique_ptr<int>, std::string>
result(toml::err<std::string>("hoge"));
const auto mapped = std::move(result).map_or_else(
[](std::unique_ptr<int> i) -> int {
return *i;
}, 54);
BOOST_CHECK_EQUAL(mapped, 54);
}
}
BOOST_AUTO_TEST_CASE(test_and_then)
{
{
const toml::result<int, std::string> result(toml::ok(42));
const auto mapped = result.and_then(
[](const int i) -> toml::result<int, std::string> {
return toml::ok(i * 2);
});
BOOST_CHECK(!!mapped);
BOOST_CHECK(mapped.is_ok());
BOOST_CHECK(!mapped.is_err());
BOOST_CHECK_EQUAL(mapped.unwrap(), 42 * 2);
}
{
toml::result<std::unique_ptr<int>, std::string>
result(toml::ok(std::unique_ptr<int>(new int(42))));
const auto mapped = std::move(result).and_then(
[](std::unique_ptr<int> i) -> toml::result<int, std::string> {
return toml::ok(*i);
});
BOOST_CHECK(!!mapped);
BOOST_CHECK(mapped.is_ok());
BOOST_CHECK(!mapped.is_err());
BOOST_CHECK_EQUAL(mapped.unwrap(), 42);
}
{
const toml::result<int, std::string> result(toml::err<std::string>("hoge"));
const auto mapped = result.and_then(
[](const int i) -> toml::result<int, std::string> {
return toml::ok(i * 2);
});
BOOST_CHECK(!mapped);
BOOST_CHECK(!mapped.is_ok());
BOOST_CHECK(mapped.is_err());
BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hoge");
}
{
toml::result<std::unique_ptr<int>, std::string>
result(toml::err<std::string>("hoge"));
const auto mapped = std::move(result).and_then(
[](std::unique_ptr<int> i) -> toml::result<int, std::string> {
return toml::ok(*i);
});
BOOST_CHECK(!mapped);
BOOST_CHECK(!mapped.is_ok());
BOOST_CHECK(mapped.is_err());
BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hoge");
}
}
BOOST_AUTO_TEST_CASE(test_or_else)
{
{
const toml::result<int, std::string> result(toml::ok(42));
const auto mapped = result.or_else(
[](const std::string& s) -> toml::result<int, std::string> {
return toml::err(s + s);
});
BOOST_CHECK(!!mapped);
BOOST_CHECK(mapped.is_ok());
BOOST_CHECK(!mapped.is_err());
BOOST_CHECK_EQUAL(mapped.unwrap(), 42);
}
{
toml::result<std::unique_ptr<int>, std::string>
result(toml::ok(std::unique_ptr<int>(new int(42))));
const auto mapped = std::move(result).or_else(
[](const std::string& s) -> toml::result<std::unique_ptr<int>, std::string> {
return toml::err(s + s);
});
BOOST_CHECK(!!mapped);
BOOST_CHECK(mapped.is_ok());
BOOST_CHECK(!mapped.is_err());
BOOST_CHECK_EQUAL(*mapped.unwrap(), 42);
}
{
const toml::result<int, std::string> result(toml::err<std::string>("hoge"));
const auto mapped = result.or_else(
[](const std::string& s) -> toml::result<int, std::string> {
return toml::err(s + s);
});
BOOST_CHECK(!mapped);
BOOST_CHECK(!mapped.is_ok());
BOOST_CHECK(mapped.is_err());
BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hogehoge");
}
{
toml::result<std::unique_ptr<int>, std::string>
result(toml::err<std::string>("hoge"));
const auto mapped = std::move(result).or_else(
[](const std::string& s) -> toml::result<std::unique_ptr<int>, std::string> {
return toml::err(s + s);
});
BOOST_CHECK(!mapped);
BOOST_CHECK(!mapped.is_ok());
BOOST_CHECK(mapped.is_err());
BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hogehoge");
}
}

View File

@ -8,6 +8,19 @@
namespace toml
{
#if __cplusplus >= 201703L
template<typename F, typename ... Args>
using return_type_of_t = std::invoke_result_t<F, Args...>;
#else
// result_of is deprecated after C++17
template<typename F, typename ... Args>
using return_type_of_t = typename std::result_of<F(Args...)>::type;
#endif
template<typename T>
struct success
{
@ -238,12 +251,12 @@ struct result
{
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
assert(tmp == std::addressof(this->succ));
}
else
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
assert(tmp == std::addressof(this->fail));
}
}
@ -267,12 +280,12 @@ struct result
{
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
assert(tmp == std::addressof(this->succ));
}
else
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
assert(tmp == std::addressof(this->fail));
}
}
@ -390,6 +403,142 @@ struct result
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<U, E>
template<typename F>
result<return_type_of_t<F, value_type&>, error_type>
map(F&& f) &
{
if(this->is_ok()){return ok(f(this->as_ok()));}
return err(this->as_err());
}
template<typename F>
result<return_type_of_t<F, value_type const&>, error_type>
map(F&& f) const&
{
if(this->is_ok()){return ok(f(this->as_ok()));}
return err(this->as_err());
}
template<typename F>
result<return_type_of_t<F, value_type &&>, 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<T, F>
template<typename F>
result<value_type, return_type_of_t<F, error_type&>>
map_err(F&& f) &
{
if(this->is_err()){return err(f(this->as_err()));}
return ok(this->as_ok());
}
template<typename F>
result<value_type, return_type_of_t<F, error_type const&>>
map_err(F&& f) const&
{
if(this->is_err()){return err(f(this->as_err()));}
return ok(this->as_ok());
}
template<typename F>
result<value_type, return_type_of_t<F, error_type&&>>
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<typename F, typename U>
return_type_of_t<F, value_type&>
map_or_else(F&& f, U&& opt) &
{
if(this->is_err()){return std::forward<U>(opt);}
return f(this->as_ok());
}
template<typename F, typename U>
return_type_of_t<F, value_type const&>
map_or_else(F&& f, U&& opt) const&
{
if(this->is_err()){return std::forward<U>(opt);}
return f(this->as_ok());
}
template<typename F, typename U>
return_type_of_t<F, value_type&&>
map_or_else(F&& f, U&& opt) &&
{
if(this->is_err()){return std::forward<U>(opt);}
return f(std::move(this->as_ok()));
}
// prerequisities:
// F: func T -> U
// toml::err(error_type) should be convertible to U.
// normally, type U is another result<S, F> and E is convertible to F
template<typename F>
return_type_of_t<F, value_type&>
and_then(F&& f) &
{
if(this->is_ok()){return f(this->as_ok());}
return err(this->as_err());
}
template<typename F>
return_type_of_t<F, value_type const&>
and_then(F&& f) const&
{
if(this->is_ok()){return f(this->as_ok());}
return err(this->as_err());
}
template<typename F>
return_type_of_t<F, value_type&&>
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<S, F> and T is convertible to S
template<typename F>
return_type_of_t<F, error_type&>
or_else(F&& f) &
{
if(this->is_err()){return f(this->as_err());}
return ok(this->as_ok());
}
template<typename F>
return_type_of_t<F, error_type const&>
or_else(F&& f) const&
{
if(this->is_err()){return f(this->as_err());}
return ok(this->as_ok());
}
template<typename F>
return_type_of_t<F, error_type&&>
or_else(F&& f) &&
{
if(this->is_err()){return f(std::move(this->as_err()));}
return ok(std::move(this->as_ok()));
}
void swap(result<T, E>& other)
{
result<T, E> tmp(std::move(*this));
*this = std::move(other);
other = std::move(tmp);
return ;
}
private:
void cleanup() noexcept
@ -409,6 +558,12 @@ struct result
};
};
template<typename T, typename E>
void swap(result<T, E>& lhs, result<T, E>& rhs)
{
lhs.swap(rhs);
return;
}
} // toml11
#endif// TOML11_RESULT_H