Created
September 9, 2013 15:41
-
-
Save alexeiz/6497398 to your computer and use it in GitHub Desktop.
`result` class is a discriminated union that combines a result of a function call with an exception that a function throws on error that allows to pass the full result of a function execution to code that doesn't allow exceptions.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef RESULT_H__ | |
#define RESULT_H__ | |
#include <stdexcept> | |
#include <algorithm> | |
#include <boost/variant.hpp> | |
namespace lib | |
{ | |
template <typename T = void> | |
class result | |
{ | |
public: | |
result(T val = T()) | |
: val_(std::move(val)) | |
{} | |
result(std::exception_ptr exc_val) | |
: val_(exc_val) | |
{} | |
explicit | |
operator bool() const | |
{ | |
return boost::get<T>(&val_) != nullptr; | |
} | |
template <typename U> | |
operator result<U> () const | |
{ | |
if (*this) | |
return get(); | |
else | |
return error(); | |
} | |
T & operator*() | |
{ | |
return get(); | |
} | |
T const & operator*() const | |
{ | |
return get(); | |
} | |
T * operator->() | |
{ | |
return &get(); | |
} | |
T const * operator->() const | |
{ | |
return &get(); | |
} | |
T & get() | |
{ | |
if (T * obj = boost::get<T>(&val_)) | |
return *obj; | |
else | |
fail("dereference result<T> object without a valid value"); | |
} | |
T const & get() const | |
{ | |
return const_cast<result *>(this)->get(); | |
} | |
void raise() const | |
{ | |
std::rethrow_exception(error()); | |
} | |
std::exception_ptr error() const | |
{ | |
if (std::exception_ptr const * exc = boost::get<std::exception_ptr>(&val_)) | |
return *exc; | |
else | |
fail("dereference result<T> object without a valid exception"); | |
} | |
private: | |
void fail(char const * msg) const | |
{ | |
throw std::logic_error(msg); | |
} | |
private: | |
boost::variant<std::exception_ptr, T> val_; | |
}; | |
template <> | |
class result<void> | |
{ | |
public: | |
result(std::exception_ptr exc = std::exception_ptr()) | |
: exc_(exc) | |
{} | |
explicit | |
operator bool() const | |
{ | |
return exc_ == std::exception_ptr(); | |
} | |
template <typename U> | |
operator result<U> () const | |
{ | |
if (*this) | |
fail("converting result<void> to result<T> without a valid exception"); | |
else | |
return error(); | |
} | |
void operator*() const | |
{ | |
get(); | |
} | |
void get() const | |
{ | |
if (exc_ != std::exception_ptr()) | |
fail("dereference result<T> object without a valid value"); | |
} | |
void raise() const | |
{ | |
std::rethrow_exception(error()); | |
} | |
std::exception_ptr error() const | |
{ | |
if (exc_ != std::exception_ptr()) | |
return exc_; | |
else | |
fail("dereference result<T> object without a valid exception"); | |
} | |
private: | |
void fail(char const * msg) const | |
{ | |
throw std::logic_error(msg); | |
} | |
private: | |
std::exception_ptr exc_; | |
}; | |
template <typename T> | |
result<T> make_result(T val = T()) | |
{ | |
return result<T>(std::move(val)); | |
} | |
template <typename T = void, typename E> | |
result<T> make_result_exc(E exc) | |
{ | |
return std::make_exception_ptr(std::move(exc)); | |
} | |
} | |
#endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// compile with: | |
// g++ -std=gnu++1y result.cpp -lboost_unit_test_framework-mt -L/opt/local/lib -g -O0 -D_DEBUG | |
#include "result.h" | |
#define BOOST_TEST_MODULE lib_result | |
#define BOOST_TEST_MAIN | |
#define BOOST_TEST_DYN_LINK | |
#include <boost/test/unit_test.hpp> | |
#include <algorithm> | |
#include <stdexcept> | |
#include <string> | |
BOOST_AUTO_TEST_CASE(default_construction) | |
{ | |
lib::result<int> res; | |
} | |
BOOST_AUTO_TEST_CASE(construction) | |
{ | |
lib::result<int> int_res(5); | |
lib::result<int> exc_res(std::make_exception_ptr(std::exception())); | |
} | |
BOOST_AUTO_TEST_CASE(construction_methods) | |
{ | |
lib::result<int> int_res(lib::make_result(5)); | |
lib::result<int> exc_res(lib::make_result_exc(std::exception())); | |
} | |
BOOST_AUTO_TEST_CASE(forward_construction) | |
{ | |
// TODO | |
} | |
BOOST_AUTO_TEST_CASE(copy_construction) | |
{ | |
lib::result<int> res(5); | |
lib::result<int> res2(res); | |
lib::result<int> exc_res(lib::make_result_exc(std::exception())); | |
lib::result<int> exc_res2(exc_res); | |
} | |
BOOST_AUTO_TEST_CASE(move_construction) | |
{ | |
lib::result<int> res(5); | |
lib::result<int> res2(std::move(res)); | |
lib::result<int> exc_res(lib::make_result_exc(std::exception())); | |
lib::result<int> exc_res2(std::move(exc_res)); | |
} | |
BOOST_AUTO_TEST_CASE(construct_from_moveable) | |
{ | |
// TODO | |
} | |
BOOST_AUTO_TEST_CASE(copy_assignment) | |
{ | |
lib::result<int> res(5); | |
lib::result<int> res2; | |
res2 = res; | |
lib::result<int> exc_res(lib::make_result_exc(std::exception())); | |
lib::result<int> exc_res2; | |
exc_res2 = exc_res; | |
} | |
BOOST_AUTO_TEST_CASE(move_assignment) | |
{ | |
lib::result<int> res(5); | |
lib::result<int> res2; | |
res2 = std::move(res); | |
lib::result<int> exc_res(lib::make_result_exc(std::exception())); | |
lib::result<int> exc_res2; | |
exc_res2 = std::move(exc_res); | |
} | |
BOOST_AUTO_TEST_CASE(bool_convertion) | |
{ | |
lib::result<int> int_res(5); | |
BOOST_CHECK(int_res); | |
lib::result<int> int_res_copy(int_res); | |
BOOST_CHECK(int_res_copy); | |
lib::result<int> int_res_move(std::move(int_res)); | |
BOOST_CHECK(int_res_move); | |
lib::result<int> exc_res(lib::make_result_exc(std::exception())); | |
BOOST_CHECK(!exc_res); | |
lib::result<int> exc_res_copy(exc_res); | |
BOOST_CHECK(!exc_res_copy); | |
lib::result<int> exc_res_move(std::move(exc_res)); | |
BOOST_CHECK(!exc_res_move); | |
} | |
BOOST_AUTO_TEST_CASE(get_value) | |
{ | |
lib::result<int> int_res(5); | |
BOOST_CHECK(int_res.get() == 5); | |
lib::result<int> const & int_res_c = int_res; | |
BOOST_CHECK(int_res_c.get() == 5); | |
lib::result<int> exc_res(lib::make_result_exc(std::exception())); | |
BOOST_CHECK_THROW(exc_res.get(), std::exception); | |
BOOST_CHECK_THROW(exc_res.get(), std::logic_error); | |
} | |
BOOST_AUTO_TEST_CASE(dereference) | |
{ | |
lib::result<int> int_res(5); | |
BOOST_CHECK(*int_res == 5); | |
lib::result<int> const & int_res_c = int_res; | |
BOOST_CHECK(*int_res_c == 5); | |
lib::result<int> exc_res(lib::make_result_exc(std::exception())); | |
BOOST_CHECK_THROW(*exc_res, std::exception); | |
BOOST_CHECK_THROW(*exc_res, std::logic_error); | |
} | |
BOOST_AUTO_TEST_CASE(field_access) | |
{ | |
struct aggr { int a; bool b; }; | |
lib::result<aggr> aggr_res(aggr{10, false}); | |
BOOST_CHECK(aggr_res->a == 10); | |
BOOST_CHECK(!aggr_res->b); | |
lib::result<aggr> const & aggr_res_c = aggr_res; | |
BOOST_CHECK(aggr_res_c->a == 10); | |
BOOST_CHECK(!aggr_res_c->b); | |
lib::result<aggr> exc_res(lib::make_result_exc(std::exception())); | |
BOOST_CHECK_THROW(exc_res->a, std::exception); | |
BOOST_CHECK_THROW(exc_res->a, std::logic_error); | |
} | |
BOOST_AUTO_TEST_CASE(raise_exception) | |
{ | |
lib::result<int> exc_res(lib::make_result_exc(std::runtime_error("error"))); | |
BOOST_CHECK_THROW(exc_res.raise(), std::runtime_error); | |
BOOST_CHECK_THROW(*exc_res, std::logic_error); | |
} | |
BOOST_AUTO_TEST_CASE(get_error_exception) | |
{ | |
lib::result<int> exc_res(lib::make_result_exc(std::runtime_error("error"))); | |
std::exception_ptr exc = exc_res.error(); | |
BOOST_CHECK_THROW(std::rethrow_exception(exc), std::runtime_error); | |
} | |
// Usage example | |
// | |
struct Resource | |
{ | |
Resource(int size) : size_(size) {} | |
int size() const { return size_; } | |
private: | |
int size_; | |
}; | |
lib::result<Resource> acquire_resource(char const * desc) | |
{ | |
try | |
{ | |
return Resource(std::stoi(desc)); | |
} | |
catch (...) | |
{ | |
return std::current_exception(); | |
} | |
} | |
lib::result<int> resource_size(char const * desc) | |
{ | |
auto res = acquire_resource(desc); | |
if (res) | |
return res->size(); | |
else | |
{ | |
// propagate error | |
return res.error(); | |
} | |
} | |
BOOST_AUTO_TEST_CASE(usage_example) | |
{ | |
BOOST_CHECK_EQUAL(*resource_size("1024"), 1024); | |
BOOST_CHECK_THROW(*resource_size("oiuweriu"), std::logic_error); | |
auto size = resource_size("woieurwo"); | |
BOOST_CHECK_THROW(size.raise(), std::invalid_argument); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment