Skip to content

Instantly share code, notes, and snippets.

@alexeiz
Created September 9, 2013 15:41
Show Gist options
  • Save alexeiz/6497398 to your computer and use it in GitHub Desktop.
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.
#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
// 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