blob: 59d5841ffa108a0a982cc6e79ab2566ee0151df3 [file] [log] [blame]
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef LIBHWSEC_FOUNDATION_STATUS_STATUS_CHAIN_OR_H_
#define LIBHWSEC_FOUNDATION_STATUS_STATUS_CHAIN_OR_H_
#include <memory>
#include <utility>
#include <variant>
#include "libhwsec-foundation/status/status_chain.h"
// Convenience class to represent value or non-ok status.
namespace hwsec_foundation {
namespace status {
template <typename _Vt, typename _Et>
class [[nodiscard]] StatusChainOr {
public:
using value_type = _Vt;
using status_type = StatusChain<_Et>;
using container_type = std::variant<value_type, status_type>;
// We don't want default constructor.
StatusChainOr() = delete;
// We don't want copy constructor.
StatusChainOr(const StatusChainOr&) = delete;
// Accept the move constructor.
StatusChainOr(StatusChainOr&& status_or)
: value_(std::move(status_or.value_)) {}
// Implicit conversion to StatusChainOr to allow transparent "return"s.
template <
typename _Ut,
typename = std::enable_if_t<std::is_constructible_v<value_type, _Ut>>>
StatusChainOr(_Ut&& v) // NOLINT(runtime/explicit)
: value_(container_type{std::in_place_type<value_type>,
std::forward<_Ut>(v)}) {}
// Constructs the inner value in-place using the provided args, using the
// `value_type(args...)` constructor.
template <
typename... Args,
typename = std::enable_if_t<std::is_constructible_v<value_type, Args...>>>
StatusChainOr(std::in_place_t, Args&&... args) // NOLINT(runtime/explicit)
: value_(container_type{std::in_place_type<value_type>,
std::forward<Args>(args)...}) {}
// Constructs the inner value in-place using the provided args, using the
// `value_type(args...)` constructor.
template <typename _Ut,
typename... Args,
typename = std::enable_if_t<
std::is_constructible_v<value_type,
std::initializer_list<_Ut>,
Args...>>>
StatusChainOr(std::in_place_t,
std::initializer_list<_Ut> ilist,
Args&&... args) // NOLINT(runtime/explicit)
: value_(container_type{std::in_place_type<value_type>, ilist,
std::forward<Args>(args)...}) {}
// Converting move constructor from a compatible stackable error type. It is
// fine, since our internal stack representation is of a base |Base| type
// anyway. SFINAE checks that the supplied pointer type is compatible with
// this object's head type. Since manually specializing the operator could
// lead to breaking invariant of the head object being castable to class
// template type |_Et|, we use |ExplicitArgumentBarrier| idiom to make |_Ut|
// auto-deducible only.
template <int&... ExplicitArgumentBarrier,
typename _Ut,
typename = std::enable_if_t<
std::is_convertible_v<_Ut*,
typename StatusChain<_Et>::pointer>>>
StatusChainOr(StatusChain<_Ut>&& other) // NOLINT(runtime/explicit)
: value_(
container_type{std::in_place_type<status_type>, std::move(other)}) {
CHECK(!std::get<status_type>(value_).ok())
<< " StatusChainOr cannot hold an OK status";
}
// We don't want copy operator.
StatusChainOr& operator=(const StatusChainOr&) = delete;
StatusChainOr& operator=(StatusChainOr&& status_or) noexcept {
value_ = std::move(status_or.value_);
return *this;
}
value_type* operator->() noexcept {
CHECK(ok()) << " Arrow operator on a non-OK StatusChainOr is not allowed";
return std::get_if<value_type>(&value_);
}
constexpr const value_type& operator*() const& {
CHECK(ok()) << " Dereferencing a non-OK StatusChainOr is not allowed";
return *std::get_if<value_type>(&value_);
}
value_type& operator*() & {
CHECK(ok()) << " Dereferencing a non-OK StatusChainOr is not allowed";
return *std::get_if<value_type>(&value_);
}
value_type&& operator*() && {
CHECK(ok()) << " Dereferencing a non-OK StatusChainOr is not allowed";
return std::move(*std::get_if<value_type>(&value_));
}
constexpr const value_type& value() const& noexcept {
CHECK(ok()) << " Get the value of a non-OK StatusChainOr is not allowed";
return *std::get_if<value_type>(&value_);
}
value_type& value() & noexcept {
CHECK(ok()) << " Get the value of a non-OK StatusChainOr is not allowed";
return *std::get_if<value_type>(&value_);
}
value_type&& value() && noexcept {
CHECK(ok()) << " Get the value of a non-OK StatusChainOr is not allowed";
return std::move(*std::get_if<value_type>(&value_));
}
bool ok() const noexcept {
return std::holds_alternative<value_type>(value_);
}
constexpr const status_type& status() const& noexcept {
if (ok()) {
return ConstRefOkStatus<_Et>();
}
return *std::get_if<status_type>(&value_);
}
status_type status() && noexcept {
if (ok()) {
return OkStatus<_Et>();
}
return std::move(*std::get_if<status_type>(&value_));
}
private:
container_type value_;
};
} // namespace status
} // namespace hwsec_foundation
#endif // LIBHWSEC_FOUNDATION_STATUS_STATUS_CHAIN_OR_H_