Skip to content

Commit

Permalink
Expected impl, try 0
Browse files Browse the repository at this point in the history
  • Loading branch information
ksergey committed Jul 8, 2024
1 parent 45c3bc7 commit 8f6c6c8
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 15 deletions.
19 changes: 4 additions & 15 deletions code/turboq/Result.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,9 @@
#include <exception>
#include <system_error>

#include <turboq/detail/Expected.h>
#include <turboq/platform.h>

#if __cpp_concepts >= 202002L
#include <expected>
#elif __clang_major__ >= 17
#define turboq_save__cpp_concepts
#pragma clang diagnostic ignored "-Wbuiltin-macro-redefined"
#define __cpp_concepts 202002L
#include <expected>
#pragma clang diagnostic ignored "-Wmacro-redefined"
#define __cpp_concepts turboq_save__cpp_concepts
#undef turboq_save__cpp_concepts
#endif

namespace turboq {

/// Error category for posix errors
Expand All @@ -47,11 +36,11 @@ TURBOQ_FORCE_INLINE std::error_category const& getPosixErrorCategory() noexcept

/// Optional with failure reason.
template <class T = void, class E = std::error_code>
using Result = std::expected<T, E>;
using Result = detail::Expected<T, E>;

/// Return ErrorCode with posix error
TURBOQ_FORCE_INLINE std::unexpected<std::error_code> makePosixErrorCode(int ec) noexcept {
return std::unexpected<std::error_code>(std::error_code(ec, getPosixErrorCategory()));
TURBOQ_FORCE_INLINE detail::Unexpected<std::error_code> makePosixErrorCode(int ec) noexcept {
return detail::Unexpected<std::error_code>(std::error_code(ec, getPosixErrorCategory()));
}

} // namespace turboq
128 changes: 128 additions & 0 deletions code/turboq/detail/Expected.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright (c) Sergey Kovalevich <inndie@gmail.com>
// SPDX-License-Identifier: AGPL-3.0

#include <stdexcept>
#include <type_traits>
#include <variant>

namespace turboq::detail {

struct BadExpectedAccess : public std::runtime_error {
template <typename... Args>
BadExpectedAccess(Args&&... args) : std::runtime_error(std::forward<Args>(args)...) {}
};

/// @brief Required tagging type for cases where expected and unexpected type are identical.
template <typename E>
class Unexpected {
public:
constexpr Unexpected(Unexpected const&) = default;
constexpr Unexpected(Unexpected&&) = default;

constexpr explicit Unexpected(E const& rhs) : storage_(rhs) {}
constexpr explicit Unexpected(E&& rhs) : storage_(std::move(rhs)) {}

constexpr E const& error() const& noexcept {
return storage_;
}
constexpr E&& error() && noexcept {
return std::move(storage_);
}

private:
E storage_;
};

template <typename E>
Unexpected(E) -> Unexpected<E>;

/// @brief A stripped-down version of std::expected from C++23.
template <typename T, typename E>
class Expected {
private:
std::variant<T, Unexpected<E>> storage_;

public:
constexpr Expected() : storage_(std::in_place_index<0>) {}

template <typename... Args>
constexpr Expected(Args&&... args) : storage_(std::forward<Args>(args)...) {}

constexpr Expected(Expected const&) = default;
constexpr Expected(Expected&&) = default;

constexpr bool has_value() const noexcept {
return storage_.index() == 0;
}

constexpr explicit operator bool() const noexcept {
return has_value();
}

template <class X>
constexpr T value_or(X&& v) const& noexcept {
return has_value() ? std::get<T>(storage_) : static_cast<T>(std::forward<X>(v));
}

constexpr T const& value() const& {
if (!has_value())
throw BadExpectedAccess("Attempt to access value() when unexpected");
return std::get<T>(storage_);
}

constexpr T value() && {
if (!has_value())
throw BadExpectedAccess("Attempt to access value() when unexpected");
return std::move(std::get<T>(storage_));
}

constexpr E const& error() const& noexcept {
return std::get_if<Unexpected<E>>(&storage_)->error();
}

constexpr E error() && noexcept {
return std::move(*std::get_if<Unexpected<E>>(&storage_)).error();
}

constexpr T const& operator*() const noexcept {
return *std::get_if<T>(&storage_);
}

constexpr T const* operator->() const noexcept {
return std::get_if<T>(&storage_);
}
};

/// \overload
template <typename E>
class Expected<void, E> {
private:
std::variant<std::monostate, Unexpected<E>> storage_;

public:
constexpr Expected() noexcept : storage_(std::in_place_index<0>) {}

template <typename... Args>
constexpr Expected(Args&&... args) : storage_(std::forward<Args>(args)...) {}

constexpr Expected(Expected const&) = default;
constexpr Expected(Expected&&) = default;

constexpr bool has_value() const noexcept {
return storage_.index() == 0;
}

constexpr explicit operator bool() const noexcept {
return has_value();
}

constexpr E const& error() const& noexcept {
return std::get_if<Unexpected<E>>(&storage_)->error();
}

constexpr E error() && noexcept {
return std::move(*std::get_if<Unexpected<E>>(&storage_)).error();
}
};

} // namespace turboq::detail

0 comments on commit 8f6c6c8

Please sign in to comment.