desktop/src/common/result.h

169 lines
3.5 KiB
C++

/*
* Copyright (C) by Olivier Goffart <ogoffart@woboq.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#pragma once
#include "asserts.h"
namespace OCC {
/**
* A Result of type T, or an Error
*/
template <typename T, typename Error>
class Result
{
union {
T _result;
Error _error;
};
bool _isError;
public:
Result(T value)
: _result(std::move(value))
, _isError(false)
{
}
// TODO: This doesn't work if T and Error are too similar
Result(Error error)
: _error(std::move(error))
, _isError(true)
{
}
Result(Result &&other)
: _isError(other._isError)
{
if (_isError) {
new (&_error) Error(std::move(other._error));
} else {
new (&_result) T(std::move(other._result));
}
}
Result(const Result &other)
: _isError(other._isError)
{
if (_isError) {
new (&_error) Error(other._error);
} else {
new (&_result) T(other._result);
}
}
Result &operator=(Result &&other)
{
if (&other != this) {
_isError = other._isError;
if (_isError) {
new (&_error) Error(std::move(other._error));
} else {
new (&_result) T(std::move(other._result));
}
}
return *this;
}
Result &operator=(const Result &other)
{
if (&other != this) {
_isError = other._isError;
if (_isError) {
new (&_error) Error(other._error);
} else {
new (&_result) T(other._result);
}
}
return *this;
}
~Result()
{
if (_isError)
_error.~Error();
else
_result.~T();
}
explicit operator bool() const { return !_isError; }
const T &operator*() const &
{
ASSERT(!_isError);
return _result;
}
T operator*() &&
{
ASSERT(!_isError);
return std::move(_result);
}
const T *operator->() const
{
ASSERT(!_isError);
return &_result;
}
[[nodiscard]] const T &get() const
{
ASSERT(!_isError)
return _result;
}
[[nodiscard]] const Error &error() const &
{
ASSERT(_isError);
return _error;
}
Error error() &&
{
ASSERT(_isError);
return std::move(_error);
}
[[nodiscard]] bool isValid() const { return !_isError; }
};
namespace detail {
struct NoResultData{};
}
template <typename Error>
class Result<void, Error> : public Result<detail::NoResultData, Error>
{
public:
using Result<detail::NoResultData, Error>::Result;
Result() : Result(detail::NoResultData{}) {}
};
namespace detail {
struct OptionalNoErrorData{};
}
template <typename T>
class Optional : public Result<T, detail::OptionalNoErrorData>
{
public:
using Result<T, detail::OptionalNoErrorData>::Result;
Optional()
: Optional(detail::OptionalNoErrorData{})
{
}
};
} // namespace OCC