Skip to content

Commit

Permalink
Merge pull request #124 from debruce/feature/expected_h
Browse files Browse the repository at this point in the history
Header implementation to mock C++23 std::expected.
  • Loading branch information
gregmedd authored Jun 12, 2024
2 parents 2e74a93 + e9c3fb6 commit 0e98904
Show file tree
Hide file tree
Showing 3 changed files with 358 additions and 67 deletions.
151 changes: 91 additions & 60 deletions include/up-cpp/utils/Expected.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
#ifndef UP_CPP_UTILS_EXPECTED_H
#define UP_CPP_UTILS_EXPECTED_H

#include <exception>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <variant>

namespace uprotocol::utils {

Expand All @@ -27,72 +29,100 @@ static_assert(!__has_cpp_attribute(__cpp_lib_expected),
/// https://en.cppreference.com/w/cpp/utility/expected
/// No further documentation is provided in this file.
/// @{
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>
struct BadExpectedAccess;
class Unexpected {
public:
constexpr Unexpected(const Unexpected&) = default;
constexpr Unexpected(Unexpected&&) = default;

template <>
struct BadExpectedAccess<void> : public std::exception {};
constexpr explicit Unexpected(const E& rhs) : storage_(rhs) {}
constexpr explicit Unexpected(E&& rhs) : storage_(std::move(rhs)) {}

template <typename E>
struct BadExpectedAccess : public BadExpectedAccess<void> {};
constexpr const E& error() const& noexcept { return storage_; }
constexpr E&& error() && noexcept { return std::move(storage_); }

private:
E storage_;
};

/// @brief A stripped-down version of std::expected from C++23.
template <typename T, typename E>
struct Expected {
using value_type = T;
using error_type = E;

constexpr Expected();
constexpr Expected(const Expected& other);
constexpr Expected(Expected&& other) noexcept;

constexpr explicit Expected(T&& v);
constexpr explicit Expected(E&& e);

Expected& operator=(const Expected& other);
Expected& operator=(Expected&& other);

Expected& operator=(const T& other);
Expected& operator=(T&& other);

Expected& operator=(const E& other);
Expected& operator=(E&& other);

constexpr explicit operator bool() const noexcept;
constexpr bool has_value() const noexcept;

constexpr T& value() &;
constexpr const T& value() const&;
constexpr T&& value() &&;
constexpr const T&& value() const&&;

constexpr const E& error() const& noexcept;
constexpr E& error() & noexcept;
constexpr const E&& error() const&& noexcept;
constexpr E&& error() && noexcept;

constexpr T value_or(T&& default_value) const&;
constexpr T value_or(T&& default_value) &&;

template <typename F>
constexpr auto and_then(F&& f) &;
template <typename F>
constexpr auto and_then(F&& f) const&;
template <typename F>
constexpr auto and_then(F&& f) &&;
template <typename F>
constexpr auto and_then(F&& f) const&&;

template <typename F>
constexpr auto or_else(F&& f) &;
template <typename F>
constexpr auto or_else(F&& f) const&;
template <typename F>
constexpr auto or_else(F&& f) &&;
template <typename F>
constexpr auto or_else(F&& f) const&&;
class Expected {
public:
template <typename... Args>
constexpr Expected(Args&&... args)
: storage_(std::forward<Args>(args)...) {}

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

constexpr bool has_value() const noexcept {
return std::holds_alternative<T>(storage_);
}

constexpr explicit operator bool() const noexcept {
return std::holds_alternative<T>(storage_);
}

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 const T& 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 const E& error() const& {
if (has_value())
throw BadExpectedAccess(
"Attempt to access error() when not unexpected.");
return std::get<Unexpected<E>>(storage_).error();
}

constexpr E error() && {
if (has_value())
throw BadExpectedAccess(
"Attempt to access error() when not unexpected.");
return std::move(std::get<Unexpected<E>>(storage_)).error();
}

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

constexpr const T* operator->() const {
if (!has_value())
throw BadExpectedAccess(
"Attempt to dereference expected pointer when unexpected.");
return &std::get<T>(storage_);
}

private:
std::variant<T, Unexpected<E>> storage_;

static_assert(!std::is_void_v<T>,
"We don't allow T==void (unlike std::expected)");

Expand All @@ -101,8 +131,9 @@ struct Expected {
!std::is_reference_v<T>,
"Expected requires T to meet the C++ 'destructable' requirement");
};

/// @}

} // namespace uprotocol::utils

#endif // UP_CPP_UTILS_EXPECTED_H
#endif // UP_CPP_UTILS_EXPECTED_H
5 changes: 3 additions & 2 deletions src/transport/UTransport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include "up-cpp/datamodel/validator/UMessage.h"
#include "up-cpp/datamodel/validator/UUri.h"
#include "up-cpp/utils/Expected.h"

namespace uprotocol::transport {

Expand Down Expand Up @@ -67,9 +68,9 @@ UTransport::registerListener(const v1::UUri& sink_filter,
std::move(source_filter));

if (status.code() == v1::UCode::OK) {
return utils::Expected<ListenHandle, v1::UStatus>(std::move(handle));
return std::move(handle);
} else {
return utils::Expected<ListenHandle, v1::UStatus>(std::move(status));
return uprotocol::utils::Unexpected(std::move(status));
}
}

Expand Down
Loading

0 comments on commit 0e98904

Please sign in to comment.