227 lines
6.5 KiB
C++
227 lines
6.5 KiB
C++
|
|
#pragma once
|
||
|
|
|
||
|
|
#include <string>
|
||
|
|
#include <utility>
|
||
|
|
#include <variant>
|
||
|
|
|
||
|
|
namespace core {
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Контейнер результата выполнения операции.
|
||
|
|
*
|
||
|
|
* BasicResult хранит одно из двух состояний:
|
||
|
|
* - успешный результат типа T;
|
||
|
|
* - ошибку типа E.
|
||
|
|
*
|
||
|
|
* Создание объекта возможно только через фабричные методы:
|
||
|
|
* Success() и Failure(), что делает намерение вызова явным.
|
||
|
|
*
|
||
|
|
* @tparam T Тип успешного результата.
|
||
|
|
* @tparam E Тип ошибки.
|
||
|
|
*
|
||
|
|
* @note Копирование запрещено.
|
||
|
|
* Объект поддерживает только перемещение.
|
||
|
|
*/
|
||
|
|
template <typename T, typename E> class [[nodiscard]] BasicResult {
|
||
|
|
public:
|
||
|
|
/**
|
||
|
|
* @brief Создает успешный результат.
|
||
|
|
*
|
||
|
|
* @param value Значение успешного результата.
|
||
|
|
*
|
||
|
|
* @return Объект BasicResult в состоянии успеха.
|
||
|
|
*/
|
||
|
|
static BasicResult Success(T value) {
|
||
|
|
return BasicResult(SuccessTag{}, std::move(value));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Создает результат с ошибкой.
|
||
|
|
*
|
||
|
|
* @param error Значение ошибки.
|
||
|
|
*
|
||
|
|
* @return Объект BasicResult в состоянии ошибки.
|
||
|
|
*/
|
||
|
|
static BasicResult Failure(E error) {
|
||
|
|
return BasicResult(FailureTag{}, std::move(error));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Запрещает копирование.
|
||
|
|
*/
|
||
|
|
BasicResult(const BasicResult &) = delete;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Запрещает копирующее присваивание.
|
||
|
|
*/
|
||
|
|
BasicResult &operator=(const BasicResult &) = delete;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Разрешает перемещение.
|
||
|
|
*/
|
||
|
|
BasicResult(BasicResult &&) noexcept = default;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Разрешает перемещающее присваивание.
|
||
|
|
*/
|
||
|
|
BasicResult &operator=(BasicResult &&) noexcept = default;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Проверяет успешность результата.
|
||
|
|
*
|
||
|
|
* @return true, если результат содержит значение T.
|
||
|
|
* @return false, если результат содержит ошибку E.
|
||
|
|
*/
|
||
|
|
[[nodiscard]]
|
||
|
|
bool isSuccess() const {
|
||
|
|
return data_.index() == 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Проверяет наличие ошибки в результате.
|
||
|
|
*
|
||
|
|
* @return true, если результат содержит ошибку E.
|
||
|
|
* @return false, если результат содержит значение T.
|
||
|
|
*/
|
||
|
|
[[nodiscard]]
|
||
|
|
bool isFailure() const {
|
||
|
|
return data_.index() == 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Возвращает значение успешного результата.
|
||
|
|
*
|
||
|
|
* @return Константная ссылка на значение типа T.
|
||
|
|
*
|
||
|
|
* @warning Метод должен вызываться только после проверки
|
||
|
|
* isSuccess().
|
||
|
|
*
|
||
|
|
* @throws std::bad_variant_access
|
||
|
|
* если объект содержит ошибку.
|
||
|
|
*/
|
||
|
|
[[nodiscard]]
|
||
|
|
const T &getValue() const {
|
||
|
|
return std::get<0>(data_);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Возвращает изменяемое значение успешного результата.
|
||
|
|
*
|
||
|
|
* @return Ссылка на значение типа T.
|
||
|
|
*
|
||
|
|
* @warning Метод должен вызываться только после проверки
|
||
|
|
* isSuccess().
|
||
|
|
*/
|
||
|
|
[[nodiscard]]
|
||
|
|
T &getValue() {
|
||
|
|
return std::get<0>(data_);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Возвращает ошибку результата.
|
||
|
|
*
|
||
|
|
* @return Константная ссылка на ошибку типа E.
|
||
|
|
*
|
||
|
|
* @warning Метод должен вызываться только если isSuccess()
|
||
|
|
* возвращает false.
|
||
|
|
*
|
||
|
|
* @throws std::bad_variant_access
|
||
|
|
* если объект содержит успешный результат.
|
||
|
|
*/
|
||
|
|
[[nodiscard]]
|
||
|
|
const E &getError() const {
|
||
|
|
return std::get<1>(data_);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Возвращает изменяемую ошибку результата.
|
||
|
|
*
|
||
|
|
* @return Ссылка на ошибку типа E.
|
||
|
|
*
|
||
|
|
* @warning Метод должен вызываться только если isSuccess()
|
||
|
|
* возвращает false.
|
||
|
|
*/
|
||
|
|
[[nodiscard]]
|
||
|
|
E &getError() {
|
||
|
|
return std::get<1>(data_);
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
/**
|
||
|
|
* @brief Вспомогательный тип для выбора конструктора успеха.
|
||
|
|
*/
|
||
|
|
struct SuccessTag {};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Вспомогательный тип для выбора конструктора ошибки.
|
||
|
|
*/
|
||
|
|
struct FailureTag {};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Внутренний конструктор успешного результата.
|
||
|
|
*
|
||
|
|
* Доступен только через Success().
|
||
|
|
*/
|
||
|
|
explicit BasicResult(SuccessTag, T value)
|
||
|
|
: data_(std::in_place_index<0>, std::move(value)) {}
|
||
|
|
/**
|
||
|
|
* @brief Внутренний конструктор результата с ошибкой.
|
||
|
|
*
|
||
|
|
* Доступен только через Failure().
|
||
|
|
*/
|
||
|
|
explicit BasicResult(FailureTag, E error)
|
||
|
|
: data_(std::in_place_index<1>, std::move(error)) {}
|
||
|
|
|
||
|
|
private:
|
||
|
|
/**
|
||
|
|
* @brief Хранилище результата.
|
||
|
|
*
|
||
|
|
* Первый вариант — успешное значение T.
|
||
|
|
* Второй вариант — ошибка E.
|
||
|
|
*/
|
||
|
|
std::variant<T, E> data_;
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Результат операции с текстовой ошибкой.
|
||
|
|
*
|
||
|
|
* Упрощенный вариант BasicResult, где ошибка всегда представлена
|
||
|
|
* строкой std::string.
|
||
|
|
*
|
||
|
|
* Пример:
|
||
|
|
*
|
||
|
|
* @code
|
||
|
|
* core::Result<int> load()
|
||
|
|
* {
|
||
|
|
* return core::Result<int>::Success(100);
|
||
|
|
* }
|
||
|
|
*
|
||
|
|
* core::Result<int> parse()
|
||
|
|
* {
|
||
|
|
* return core::Result<int>::Failure("Invalid format");
|
||
|
|
* }
|
||
|
|
* @endcode
|
||
|
|
*
|
||
|
|
* @tparam T Тип успешного результата.
|
||
|
|
*/
|
||
|
|
template <typename T> using Result = BasicResult<T, std::string>;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Статус выполнения операции.
|
||
|
|
*
|
||
|
|
* Используется для операций, где важно только состояние:
|
||
|
|
* успех или сообщение об ошибке.
|
||
|
|
*
|
||
|
|
* Пример:
|
||
|
|
*
|
||
|
|
* @code
|
||
|
|
* core::Status save()
|
||
|
|
* {
|
||
|
|
* return core::Status::Success(true);
|
||
|
|
* }
|
||
|
|
* @endcode
|
||
|
|
*/
|
||
|
|
using Status = Result<bool>;
|
||
|
|
|
||
|
|
} // namespace core
|