Simple header-only variant-driven C++17 Result<T,E>
implementation.
- c++17 or c++1z with support of variant
- Small, header-only.
- Pure C++.
- Human and machine readable (with custom return errors support) error representation.
- Compile-time check of dereference.
#include <cassert>
#include <iostream>
#include "result.hpp"
using namespace std::string_literals;
enum class ErrorCode { Error };
using IError = Result::Error<ErrorCode>;
template <class T> using IResult = Result::Result<T, ErrorCode>;
class Test
{
public:
Test() = default;
~Test() = default;
friend std::ostream &operator<<(std::ostream &os, const Test &)
{
os << "I'm test class";
return os;
};
};
template <class T> Result::Content print_example(IResult<T> r)
{
switch (r.type()) {
case Result::Content::Value:
std::cout << "Result has value " << r.get() << std::endl;
break;
case Result::Content::Error:
std::cout << "Result has error " << r.error().message() << std::endl;
break;
case Result::Content::Empty:
std::cout << "Result does not contain anything" << std::endl;
break;
}
return r.type();
}
int main()
{
assert(print_example<int>(42) == Result::Content::Value);
assert(print_example<int>(IError("int error"s)) == Result::Content::Error);
assert(print_example<std::string>("a string"s) == Result::Content::Value);
assert(print_example<std::string>(IError("std::string error"s))
== Result::Content::Error);
assert(print_example<Test>(Test()) == Result::Content::Value);
assert(print_example<Test>(IError("Test error"s))
== Result::Content::Error);
return 0;
}
Alternatively you can use tempalte function to do result match, e.g.:
template <class T> Result::Content print_example(IResult<T> r)
{
Result::match(r, [](const T &value) {
std::cout << "Result has value " << value << std::endl;
}, [](const IError &error) {
std::cout << "Result has error " << error.message() << std::endl;
});
return r.type();
}
Construction like r.match(...)
is also allowed.
Result::recover
can be called in case if you want to set default value in case
of error or do another action. Result::onSuccess
can be used to convert your
value to another one (including doing action if success):
auto convert = [](const int value) { return "str("s + std::to_string(value) + ")"s; };
std::string get_string(IResult<int> r)
{
return r
.onSuccess<std::string, decltype(convert)>(convert)
.recover([](IError) { return ""s; });
}
This code example is copied from queued and uses QtDBus classes.
template <typename T, typename ErrorCode>
inline QDBusArgument &operator<<(QDBusArgument &_argument,
const Result::Result<T, ErrorCode> &_arg)
{
// HACK we are using explicit cast to QString here to make sure of valid
// marshalling
_argument.beginStructure();
switch (_arg.type()) {
case Result::Content::Value:
_argument << QString("v");
_argument << _arg.get();
_argument << Result::Error<ErrorCode>();
break;
case Result::Content::Error:
_argument << QString("e");
_argument << T();
_argument << _arg.error();
break;
case Result::Content::Empty:
_argument << QString("n");
_argument << T();
_argument << Result::Error<ErrorCode>();
break;
}
_argument.endStructure();
return _argument;
};
template <typename T, typename ErrorCode>
inline const QDBusArgument &operator>>(const QDBusArgument &_argument,
Result::Result<T, ErrorCode> &_arg)
{
QString type;
T value;
Result::Error<ErrorCode> error;
_argument.beginStructure();
_argument >> type;
_argument >> value;
_argument >> error;
_argument.endStructure();
if (type == "v")
_arg = value;
else if (type == "e")
_arg = error;
else if (type == "n")
_arg = Result::Result<T, ErrorCode><T>();
return _argument;
};