/* * Copyright (C) by Olivier Goffart * * 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 class Result { union { T _result; Error _error; }; bool _isError; public: Result(T value) : _result(std::move(value)) , _isError(false) { static_assert(!std::is_same::value, "Bool is not supported as this class overrides the bool operator"); } // 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 & { OC_ASSERT(!_isError); return _result; } T operator*() && { OC_ASSERT(!_isError); return std::move(_result); } const T *operator->() const { OC_ASSERT(!_isError); return &_result; } const T &get() const { OC_ASSERT(!_isError); return _result; } const Error &error() const & { OC_ASSERT(_isError); return _error; } Error error() && { OC_ASSERT(_isError); return std::move(_error); } }; namespace detail { struct NoResultData{}; } template class Result : public Result { public: using Result::Result; Result() : Result(detail::NoResultData{}) {} }; namespace detail { struct OptionalNoErrorData{}; } template class Optional : public Result { public: using Result::Result; Optional() : Optional(detail::OptionalNoErrorData{}) { } }; } // namespace OCC