feat add status and status_or (#4)
All checks were successful
rpcrypto-build / build (Debug, hisiv510.toolchain.cmake) (push) Successful in 1m9s
rpcrypto-build / build (Debug, himix200.toolchain.cmake) (push) Successful in 1m11s
rpcrypto-build / build (Release, himix200.toolchain.cmake) (push) Successful in 1m9s
rpcrypto-build / build (Release, hisiv510.toolchain.cmake) (push) Successful in 1m19s
linux-hisiv500-gcc / linux-gcc-hisiv500 (push) Successful in 1m35s
linux-mips64-gcc / linux-gcc-mips64el (push) Successful in 1m41s
linux-x64-gcc / linux-gcc (push) Successful in 1m56s
All checks were successful
rpcrypto-build / build (Debug, hisiv510.toolchain.cmake) (push) Successful in 1m9s
rpcrypto-build / build (Debug, himix200.toolchain.cmake) (push) Successful in 1m11s
rpcrypto-build / build (Release, himix200.toolchain.cmake) (push) Successful in 1m9s
rpcrypto-build / build (Release, hisiv510.toolchain.cmake) (push) Successful in 1m19s
linux-hisiv500-gcc / linux-gcc-hisiv500 (push) Successful in 1m35s
linux-mips64-gcc / linux-gcc-mips64el (push) Successful in 1m41s
linux-x64-gcc / linux-gcc (push) Successful in 1m56s
Co-authored-by: tqcq <99722391+tqcq@users.noreply.github.com> Reviewed-on: #4
This commit is contained in:
parent
afe3853878
commit
1610953cd8
108
CMakeLists.txt
108
CMakeLists.txt
@ -1,6 +1,9 @@
|
|||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
project(ulib LANGUAGES CXX C VERSION 0.1.0)
|
project(
|
||||||
|
ulib
|
||||||
|
LANGUAGES CXX C
|
||||||
|
VERSION 0.1.0)
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_C_EXTENSIONS OFF)
|
set(CMAKE_C_EXTENSIONS OFF)
|
||||||
@ -23,40 +26,41 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
|||||||
if(ULIB_SHARED_LIB)
|
if(ULIB_SHARED_LIB)
|
||||||
add_library(${PROJECT_NAME} SHARED "")
|
add_library(${PROJECT_NAME} SHARED "")
|
||||||
else()
|
else()
|
||||||
add_library(${PROJECT_NAME} STATIC ""
|
add_library(${PROJECT_NAME} STATIC "" src/ulib/system/timer.cpp
|
||||||
src/ulib/system/timer.cpp
|
src/ulib/system/timer.h)
|
||||||
src/ulib/system/timer.h)
|
|
||||||
endif()
|
endif()
|
||||||
target_sources(${PROJECT_NAME} PRIVATE
|
target_sources(
|
||||||
3party/mongoose/mongoose.c
|
${PROJECT_NAME}
|
||||||
src/ulib/base/location.h
|
PRIVATE 3party/mongoose/mongoose.c
|
||||||
src/ulib/base/location.cpp
|
src/ulib/base/location.h
|
||||||
src/ulib/concorrency/barrier.cpp
|
src/ulib/base/location.cpp
|
||||||
src/ulib/concorrency/barrier.h
|
src/ulib/status.h
|
||||||
src/ulib/concorrency/mutex.cpp
|
src/ulib/status.cpp
|
||||||
src/ulib/concorrency/mutex.h
|
src/ulib/concorrency/barrier.cpp
|
||||||
src/ulib/concorrency/condition_variable.cpp
|
src/ulib/concorrency/barrier.h
|
||||||
src/ulib/concorrency/condition_variable.h
|
src/ulib/concorrency/mutex.cpp
|
||||||
src/ulib/concorrency/countdown_latch.cpp
|
src/ulib/concorrency/mutex.h
|
||||||
src/ulib/concorrency/countdown_latch.h
|
src/ulib/concorrency/condition_variable.cpp
|
||||||
src/ulib/concorrency/internal/mutex_impl.cpp
|
src/ulib/concorrency/condition_variable.h
|
||||||
src/ulib/concorrency/internal/mutex_impl.h
|
src/ulib/concorrency/countdown_latch.cpp
|
||||||
src/ulib/concorrency/internal/condition_variable_impl.cpp
|
src/ulib/concorrency/countdown_latch.h
|
||||||
src/ulib/concorrency/internal/condition_variable_impl.h
|
src/ulib/concorrency/internal/mutex_impl.cpp
|
||||||
src/ulib/concorrency/event.cpp
|
src/ulib/concorrency/internal/mutex_impl.h
|
||||||
src/ulib/concorrency/event.h
|
src/ulib/concorrency/internal/condition_variable_impl.cpp
|
||||||
src/ulib/system/thread.h
|
src/ulib/concorrency/internal/condition_variable_impl.h
|
||||||
src/ulib/system/thread.cpp
|
src/ulib/concorrency/event.cpp
|
||||||
src/ulib/system/thread_pool.h
|
src/ulib/concorrency/event.h
|
||||||
src/ulib/system/thread_pool.cpp
|
src/ulib/system/thread.h
|
||||||
src/ulib/system/timer.h
|
src/ulib/system/thread.cpp
|
||||||
src/ulib/system/timer.cpp
|
src/ulib/system/thread_pool.h
|
||||||
)
|
src/ulib/system/thread_pool.cpp
|
||||||
|
src/ulib/system/timer.h
|
||||||
|
src/ulib/system/timer.cpp)
|
||||||
|
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||||
|
|
||||||
## CMP0063
|
# CMP0063
|
||||||
if(POLICY CMP0063)
|
if(POLICY CMP0063)
|
||||||
cmake_policy(SET CMP0063 NEW)
|
cmake_policy(SET CMP0063 NEW)
|
||||||
endif()
|
endif()
|
||||||
@ -64,9 +68,15 @@ if(POLICY CMP0048)
|
|||||||
cmake_policy(SET CMP0048 NEW)
|
cmake_policy(SET CMP0048 NEW)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(FMT_USE_CPP11 OFF CACHE BOOL "Use C++11" FORCE)
|
set(FMT_USE_CPP11
|
||||||
set(FMT_TEST OFF CACHE BOOL "Build tests" FORCE)
|
OFF
|
||||||
set(FMT_USE_CPP11 OFF CACHE BOOL "Use C++11" FORCE)
|
CACHE BOOL "Use C++11" FORCE)
|
||||||
|
set(FMT_TEST
|
||||||
|
OFF
|
||||||
|
CACHE BOOL "Build tests" FORCE)
|
||||||
|
set(FMT_USE_CPP11
|
||||||
|
OFF
|
||||||
|
CACHE BOOL "Use C++11" FORCE)
|
||||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/3party/fmt)
|
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/3party/fmt)
|
||||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/3party/sqlpp11)
|
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/3party/sqlpp11)
|
||||||
|
|
||||||
@ -77,24 +87,22 @@ set(BUILD_SHARED_LIBS OFF)
|
|||||||
set(BUILD_OBJECT_LIBS OFF)
|
set(BUILD_OBJECT_LIBS OFF)
|
||||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/3party/jsoncpp)
|
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/3party/jsoncpp)
|
||||||
|
|
||||||
target_sources(${PROJECT_NAME} PRIVATE
|
target_sources(
|
||||||
src/ulib/empty.cpp
|
${PROJECT_NAME} PRIVATE src/ulib/empty.cpp src/ulib/log/logger.cpp
|
||||||
src/ulib/log/logger.cpp
|
src/ulib/log/log.cpp src/ulib/log/level.cpp)
|
||||||
src/ulib/log/log.cpp
|
target_link_libraries(${PROJECT_NAME} PUBLIC fmt::fmt jsoncpp_static
|
||||||
src/ulib/log/level.cpp
|
sqlpp11::sqlpp11)
|
||||||
)
|
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC fmt::fmt jsoncpp_static sqlpp11::sqlpp11)
|
|
||||||
target_compile_definitions(${PROJECT_NAME} PRIVATE ULIB_LIBRARY_IMPL)
|
target_compile_definitions(${PROJECT_NAME} PRIVATE ULIB_LIBRARY_IMPL)
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
target_include_directories(
|
||||||
3party/inja
|
${PROJECT_NAME}
|
||||||
3party/mongoose
|
PUBLIC 3party/inja
|
||||||
3party/nlohmann
|
3party/mongoose
|
||||||
3party/nonstd
|
3party/nlohmann
|
||||||
3party/sigslot
|
3party/nonstd
|
||||||
3party/rxcpp/Rx/v2/src
|
3party/sigslot
|
||||||
3party/rxcpp/Ix/CPP/src
|
3party/rxcpp/Rx/v2/src
|
||||||
src
|
3party/rxcpp/Ix/CPP/src
|
||||||
)
|
src)
|
||||||
|
|
||||||
install(TARGETS ${PROJECT_NAME} DESTINATION lib)
|
install(TARGETS ${PROJECT_NAME} DESTINATION lib)
|
||||||
|
|
||||||
|
253
src/ulib/status.cpp
Normal file
253
src/ulib/status.cpp
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
// Copyright 2018 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "status.h"
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace ulib {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::string
|
||||||
|
StatusWhat(Status const &status)
|
||||||
|
{
|
||||||
|
std::ostringstream os;
|
||||||
|
os << status;
|
||||||
|
return std::move(os).str();
|
||||||
|
}
|
||||||
|
}// namespace
|
||||||
|
|
||||||
|
std::string
|
||||||
|
StatusCodeToString(StatusCode code)
|
||||||
|
{
|
||||||
|
switch (code) {
|
||||||
|
case StatusCode::kOk:
|
||||||
|
return "OK";
|
||||||
|
case StatusCode::kCancelled:
|
||||||
|
return "CANCELLED";
|
||||||
|
case StatusCode::kUnknown:
|
||||||
|
return "UNKNOWN";
|
||||||
|
case StatusCode::kInvalidArgument:
|
||||||
|
return "INVALID_ARGUMENT";
|
||||||
|
case StatusCode::kDeadlineExceeded:
|
||||||
|
return "DEADLINE_EXCEEDED";
|
||||||
|
case StatusCode::kNotFound:
|
||||||
|
return "NOT_FOUND";
|
||||||
|
case StatusCode::kAlreadyExists:
|
||||||
|
return "ALREADY_EXISTS";
|
||||||
|
case StatusCode::kPermissionDenied:
|
||||||
|
return "PERMISSION_DENIED";
|
||||||
|
case StatusCode::kUnauthenticated:
|
||||||
|
return "UNAUTHENTICATED";
|
||||||
|
case StatusCode::kResourceExhausted:
|
||||||
|
return "RESOURCE_EXHAUSTED";
|
||||||
|
case StatusCode::kFailedPrecondition:
|
||||||
|
return "FAILED_PRECONDITION";
|
||||||
|
case StatusCode::kAborted:
|
||||||
|
return "ABORTED";
|
||||||
|
case StatusCode::kOutOfRange:
|
||||||
|
return "OUT_OF_RANGE";
|
||||||
|
case StatusCode::kUnimplemented:
|
||||||
|
return "UNIMPLEMENTED";
|
||||||
|
case StatusCode::kInternal:
|
||||||
|
return "INTERNAL";
|
||||||
|
case StatusCode::kUnavailable:
|
||||||
|
return "UNAVAILABLE";
|
||||||
|
case StatusCode::kDataLoss:
|
||||||
|
return "DATA_LOSS";
|
||||||
|
default: {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "UNEXPECTED_STATUS_CODE=" << static_cast<int>(code);
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &
|
||||||
|
operator<<(std::ostream &os, StatusCode code)
|
||||||
|
{
|
||||||
|
return os << StatusCodeToString(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator==(ErrorInfo const &a, ErrorInfo const &b)
|
||||||
|
{
|
||||||
|
return a.reason_ == b.reason_ && a.domain_ == b.domain_
|
||||||
|
&& a.metadata_ == b.metadata_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator!=(ErrorInfo const &a, ErrorInfo const &b)
|
||||||
|
{
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encapsulates the implementation of a non-OK status. OK Statuses are
|
||||||
|
// represented by a nullptr Status::impl_, as an optimization for the common
|
||||||
|
// case of OK Statuses. This class holds all the data associated with a non-OK
|
||||||
|
// Status so we don't have to worry about bloating the common OK case.
|
||||||
|
class Status::Impl {
|
||||||
|
public:
|
||||||
|
using PayloadType = std::unordered_map<std::string, std::string>;
|
||||||
|
|
||||||
|
explicit Impl(StatusCode code,
|
||||||
|
std::string message,
|
||||||
|
ErrorInfo info,
|
||||||
|
PayloadType payload)
|
||||||
|
: code_(code),
|
||||||
|
message_(std::move(message)),
|
||||||
|
error_info_(std::move(info)),
|
||||||
|
payload_(std::move(payload))
|
||||||
|
{}
|
||||||
|
|
||||||
|
StatusCode code() const { return code_; }
|
||||||
|
|
||||||
|
std::string const &message() const { return message_; }
|
||||||
|
|
||||||
|
ErrorInfo const &error_info() const { return error_info_; }
|
||||||
|
|
||||||
|
PayloadType const &payload() const { return payload_; }
|
||||||
|
|
||||||
|
// Allows mutable access to payload, which is needed in the
|
||||||
|
// `internal::SetPayload()` function.
|
||||||
|
PayloadType &payload() { return payload_; }
|
||||||
|
|
||||||
|
friend inline bool operator==(Impl const &a, Impl const &b)
|
||||||
|
{
|
||||||
|
return a.code_ == b.code_ && a.message_ == b.message_
|
||||||
|
&& a.error_info_ == b.error_info_ && a.payload_ == b.payload_;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend inline bool operator!=(Impl const &a, Impl const &b)
|
||||||
|
{
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
StatusCode code_;
|
||||||
|
std::string message_;
|
||||||
|
ErrorInfo error_info_;
|
||||||
|
PayloadType payload_;
|
||||||
|
};
|
||||||
|
|
||||||
|
Status::Status() = default;
|
||||||
|
Status::~Status() = default;
|
||||||
|
Status::Status(Status &&) noexcept = default;
|
||||||
|
Status &Status::operator=(Status &&) noexcept = default;
|
||||||
|
|
||||||
|
// Deep copy
|
||||||
|
Status::Status(Status const &other)
|
||||||
|
: impl_(other.ok() ? nullptr : new auto(*other.impl_))
|
||||||
|
{}
|
||||||
|
|
||||||
|
// Deep copy
|
||||||
|
Status &
|
||||||
|
Status::operator=(Status const &other)
|
||||||
|
{
|
||||||
|
impl_.reset(other.ok() ? nullptr : new auto(*other.impl_));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// OK statuses have an impl_ == nullptr. Non-OK Statuses get an Impl.
|
||||||
|
Status::Status(StatusCode code, std::string message, ErrorInfo info)
|
||||||
|
: impl_(code == StatusCode::kOk
|
||||||
|
? nullptr
|
||||||
|
: new Status::Impl{code, std::move(message), std::move(info),
|
||||||
|
Status::Impl::PayloadType()})
|
||||||
|
{}
|
||||||
|
|
||||||
|
StatusCode
|
||||||
|
Status::code() const
|
||||||
|
{
|
||||||
|
return impl_ ? impl_->code() : StatusCode::kOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string const &
|
||||||
|
Status::message() const
|
||||||
|
{
|
||||||
|
static auto const *const kEmpty = new std::string{};
|
||||||
|
return impl_ ? impl_->message() : *kEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorInfo const &
|
||||||
|
Status::error_info() const
|
||||||
|
{
|
||||||
|
static auto const *const kEmpty = new ErrorInfo{};
|
||||||
|
return impl_ ? impl_->error_info() : *kEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Status::Equals(Status const &a, Status const &b)
|
||||||
|
{
|
||||||
|
return (a.ok() && b.ok()) || (a.impl_ && b.impl_ && *a.impl_ == *b.impl_);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &
|
||||||
|
operator<<(std::ostream &os, Status const &s)
|
||||||
|
{
|
||||||
|
if (s.ok()) return os << StatusCode::kOk;
|
||||||
|
os << s.code() << ": " << s.message();
|
||||||
|
auto const &e = s.error_info();
|
||||||
|
if (e.reason().empty() && e.domain().empty() && e.metadata().empty()) {
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
os << " error_info={reason=" << e.reason();
|
||||||
|
os << ", domain=" << e.domain();
|
||||||
|
os << ", metadata={";
|
||||||
|
char const *sep = "";
|
||||||
|
for (auto const &item : e.metadata()) {
|
||||||
|
os << sep << item.first << "=" << item.second;
|
||||||
|
sep = ", ";
|
||||||
|
}
|
||||||
|
return os << "}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
void
|
||||||
|
AddMetadata(ErrorInfo &ei, std::string const &key, std::string value)
|
||||||
|
{
|
||||||
|
ei.metadata_[key] = std::move(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the given `payload`, indexed by the given `key`, on the given `Status`,
|
||||||
|
// IFF the status is not OK. Payloads are considered in equality comparisons.
|
||||||
|
// The keyspace used here is separate from other keyspaces (e.g.,
|
||||||
|
// `absl::Status`), so we only need to coordinate keys with ourselves.
|
||||||
|
void
|
||||||
|
SetPayload(Status &s, std::string key, std::string payload)
|
||||||
|
{
|
||||||
|
if (s.impl_) s.impl_->payload()[std::move(key)] = std::move(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the payload associated with the given `key`, if available.
|
||||||
|
ulib::optional<std::string>
|
||||||
|
GetPayload(Status const &s, std::string const &key)
|
||||||
|
{
|
||||||
|
if (!s.impl_) return ulib::nullopt;
|
||||||
|
auto const &payload = s.impl_->payload();
|
||||||
|
auto it = payload.find(key);
|
||||||
|
if (it == payload.end()) return ulib::nullopt;
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace internal
|
||||||
|
|
||||||
|
RuntimeStatusError::RuntimeStatusError(Status status)
|
||||||
|
: std::runtime_error(StatusWhat(status)),
|
||||||
|
status_(std::move(status))
|
||||||
|
{}
|
||||||
|
|
||||||
|
}// namespace ulib
|
401
src/ulib/status.h
Normal file
401
src/ulib/status.h
Normal file
@ -0,0 +1,401 @@
|
|||||||
|
// Copyright 2018 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STATUS_H
|
||||||
|
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STATUS_H
|
||||||
|
|
||||||
|
#include "optional.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace ulib {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Well-known status codes with `grpc::StatusCode`-compatible values.
|
||||||
|
*
|
||||||
|
* The semantics of these values are documented in:
|
||||||
|
* https://grpc.io/grpc/cpp/classgrpc_1_1_status.html
|
||||||
|
*/
|
||||||
|
enum class StatusCode {
|
||||||
|
/// Not an error; returned on success.
|
||||||
|
kOk = 0,
|
||||||
|
|
||||||
|
/// `kCancelled` (gRPC code `CANCELLED`) indicates the operation was
|
||||||
|
/// cancelled, typically by the caller.
|
||||||
|
kCancelled = 1,
|
||||||
|
|
||||||
|
/// `kUnknown` (gRPC code `UNKNOWN`) indicates an unknown error occurred.
|
||||||
|
///
|
||||||
|
/// In general, more specific errors should be raised, if possible. Errors
|
||||||
|
/// raised by APIs that do not return enough error information may be
|
||||||
|
/// converted to this error.
|
||||||
|
kUnknown = 2,
|
||||||
|
|
||||||
|
/// `kInvalidArgument` (gRPC code `INVALID_ARGUMENT`) indicates the caller
|
||||||
|
/// specified an invalid argument, such as a malformed filename.
|
||||||
|
///
|
||||||
|
/// Note that use of such errors should be narrowly limited to indicate the
|
||||||
|
/// invalid nature of the arguments themselves. Errors with validly formed
|
||||||
|
/// arguments that may cause errors with the state of the receiving system
|
||||||
|
/// should be denoted with `kFailedPrecondition` instead.
|
||||||
|
kInvalidArgument = 3,
|
||||||
|
|
||||||
|
/// `kDeadlineExceeded` (gRPC code `DEADLINE_EXCEEDED`) indicates a deadline
|
||||||
|
/// expired before the operation could complete.
|
||||||
|
///
|
||||||
|
/// For operations that may change state within a system, this error may be
|
||||||
|
/// returned even if the operation has completed successfully. For example, a
|
||||||
|
/// successful response from a server could have been delayed long enough for
|
||||||
|
/// the deadline to expire.
|
||||||
|
kDeadlineExceeded = 4,
|
||||||
|
|
||||||
|
/// `kNotFound` (gRPC code `NOT_FOUND`) indicates some requested entity (such
|
||||||
|
/// as a file or directory) was not found.
|
||||||
|
///
|
||||||
|
/// `kNotFound` is useful if a request should be denied for an entire class of
|
||||||
|
/// users, such as during a gradual feature rollout or undocumented allow
|
||||||
|
/// list.
|
||||||
|
/// If a request should be denied for specific sets of users, such as through
|
||||||
|
/// user-based access control, use `kPermissionDenied` instead.
|
||||||
|
kNotFound = 5,
|
||||||
|
|
||||||
|
/// `kAlreadyExists` (gRPC code `ALREADY_EXISTS`) indicates that the entity a
|
||||||
|
/// caller attempted to create (such as a file or directory) is already
|
||||||
|
/// present.
|
||||||
|
kAlreadyExists = 6,
|
||||||
|
|
||||||
|
/// `kPermissionDenied` (gRPC code `PERMISSION_DENIED`) indicates that the
|
||||||
|
/// caller does not have permission to execute the specified operation.
|
||||||
|
///
|
||||||
|
/// Note that this error is different than an error due to an
|
||||||
|
/// *un*authenticated caller. This error code does not imply the request is
|
||||||
|
/// valid or the requested entity exists or satisfies any other
|
||||||
|
/// pre-conditions.
|
||||||
|
///
|
||||||
|
/// `kPermissionDenied` must not be used for rejections caused by exhausting
|
||||||
|
/// some resource. Instead, use `kResourceExhausted` for those errors.
|
||||||
|
/// `kPermissionDenied` must not be used if the caller cannot be identified.
|
||||||
|
/// Instead, use `kUnauthenticated` for those errors.
|
||||||
|
kPermissionDenied = 7,
|
||||||
|
|
||||||
|
/// `kResourceExhausted` (gRPC code `RESOURCE_EXHAUSTED`) indicates some
|
||||||
|
/// resource has been exhausted.
|
||||||
|
///
|
||||||
|
/// Examples include a per-user quota, or the entire file system being out of
|
||||||
|
/// space.
|
||||||
|
kResourceExhausted = 8,
|
||||||
|
|
||||||
|
/// `kFailedPrecondition` (gRPC code `FAILED_PRECONDITION`) indicates that the
|
||||||
|
/// operation was rejected because the system is not in a state required for
|
||||||
|
/// the operation's execution.
|
||||||
|
///
|
||||||
|
/// For example, a directory to be deleted may be non-empty, a "rmdir"
|
||||||
|
/// operation is applied to a non-directory, etc.
|
||||||
|
///
|
||||||
|
/// Some guidelines that may help a service implementer in deciding between
|
||||||
|
/// `kFailedPrecondition`, `kAborted`, and `kUnavailable`:
|
||||||
|
///
|
||||||
|
/// 1. Use `kUnavailable` if the client can retry just the failing call.
|
||||||
|
/// 2. Use `kAborted` if the client should retry at a higher transaction
|
||||||
|
/// level (such as when a client-specified test-and-set fails, indicating
|
||||||
|
/// the client should restart a read-modify-write sequence).
|
||||||
|
/// 3. Use `kFailedPrecondition` if the client should not retry until the
|
||||||
|
/// system state has been explicitly fixed. For example, if a "rmdir" fails
|
||||||
|
/// because the directory is non-empty, `kFailedPrecondition` should be
|
||||||
|
/// returned since the client should not retry unless the files are deleted
|
||||||
|
/// from the directory.
|
||||||
|
kFailedPrecondition = 9,
|
||||||
|
|
||||||
|
/// `kAborted` (gRPC code `ABORTED`) indicates the operation was aborted.
|
||||||
|
///
|
||||||
|
/// This is typically due to a concurrency issue such as a sequencer check
|
||||||
|
/// failure or a failed transaction.
|
||||||
|
///
|
||||||
|
/// See the guidelines above for deciding between `kFailedPrecondition`,
|
||||||
|
/// `kAborted`, and `kUnavailable`.
|
||||||
|
kAborted = 10,
|
||||||
|
|
||||||
|
/// `kOutOfRange` (gRPC code `OUT_OF_RANGE`) indicates the operation was
|
||||||
|
/// attempted past the valid range, such as seeking or reading past an
|
||||||
|
/// end-of-file.
|
||||||
|
///
|
||||||
|
/// Unlike `kInvalidArgument`, this error indicates a problem that may
|
||||||
|
/// be fixed if the system state changes. For example, a 32-bit file
|
||||||
|
/// system will generate `kInvalidArgument` if asked to read at an
|
||||||
|
/// offset that is not in the range [0,2^32-1], but it will generate
|
||||||
|
/// `kOutOfRange` if asked to read from an offset past the current
|
||||||
|
/// file size.
|
||||||
|
///
|
||||||
|
/// There is a fair bit of overlap between `kFailedPrecondition` and
|
||||||
|
/// `kOutOfRange`. We recommend using `kOutOfRange` (the more specific
|
||||||
|
/// error) when it applies so that callers who are iterating through
|
||||||
|
/// a space can easily look for an `kOutOfRange` error to detect when
|
||||||
|
/// they are done.
|
||||||
|
kOutOfRange = 11,
|
||||||
|
|
||||||
|
/// `kUnimplemented` (gRPC code `UNIMPLEMENTED`) indicates the operation is
|
||||||
|
/// not implemented or supported in this service.
|
||||||
|
///
|
||||||
|
/// In this case, the operation should not be re-attempted.
|
||||||
|
kUnimplemented = 12,
|
||||||
|
|
||||||
|
/// `kInternal` (gRPC code `INTERNAL`) indicates an internal error has
|
||||||
|
/// occurred and some invariants expected by the underlying system have not
|
||||||
|
/// been satisfied.
|
||||||
|
///
|
||||||
|
/// While this error code is reserved for serious errors, some services return
|
||||||
|
/// this error under overload conditions.
|
||||||
|
kInternal = 13,
|
||||||
|
|
||||||
|
/// `kUnavailable` (gRPC code `UNAVAILABLE`) indicates the service is
|
||||||
|
/// currently unavailable and that this is most likely a transient condition.
|
||||||
|
///
|
||||||
|
/// An error such as this can be corrected by retrying with a backoff scheme.
|
||||||
|
/// Note that it is not always safe to retry non-idempotent operations.
|
||||||
|
///
|
||||||
|
/// See the guidelines above for deciding between `kFailedPrecondition`,
|
||||||
|
/// `kAborted`, and `kUnavailable`.
|
||||||
|
kUnavailable = 14,
|
||||||
|
|
||||||
|
/// `kDataLoss` (gRPC code `DATA_LOSS`) indicates that unrecoverable data loss
|
||||||
|
/// or corruption has occurred.
|
||||||
|
///
|
||||||
|
/// As this error is serious, proper alerting should be attached to errors
|
||||||
|
/// such as this.
|
||||||
|
kDataLoss = 15,
|
||||||
|
|
||||||
|
/// `kUnauthenticated` (gRPC code `UNAUTHENTICATED`) indicates that the
|
||||||
|
/// request does not have valid authentication credentials for the operation.
|
||||||
|
///
|
||||||
|
/// Correct the authentication and try again.
|
||||||
|
kUnauthenticated = 16,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Convert @p code to a human readable string.
|
||||||
|
std::string StatusCodeToString(StatusCode code);
|
||||||
|
|
||||||
|
/// Integration with `std::iostreams`.
|
||||||
|
std::ostream &operator<<(std::ostream &os, StatusCode code);
|
||||||
|
|
||||||
|
class Status;
|
||||||
|
class ErrorInfo;
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
void AddMetadata(ErrorInfo &, std::string const &key, std::string value);
|
||||||
|
void SetPayload(Status &, std::string key, std::string payload);
|
||||||
|
ulib::optional<std::string> GetPayload(Status const &, std::string const &key);
|
||||||
|
}// namespace internal
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the cause of the error with structured details.
|
||||||
|
*
|
||||||
|
* @see https://cloud.google.com/apis/design/errors#error_info
|
||||||
|
*/
|
||||||
|
class ErrorInfo {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Default constructor.
|
||||||
|
*
|
||||||
|
* Post-condition: the `reason()`, `domain()`, and `metadata()` fields are
|
||||||
|
* empty.
|
||||||
|
*/
|
||||||
|
ErrorInfo() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param reason initializes the `reason()` value.
|
||||||
|
* @param domain initializes the `domain()` value.
|
||||||
|
* @param metadata initializes the `metadata()` value.
|
||||||
|
*/
|
||||||
|
explicit ErrorInfo(std::string reason,
|
||||||
|
std::string domain,
|
||||||
|
std::unordered_map<std::string, std::string> metadata)
|
||||||
|
: reason_(std::move(reason)),
|
||||||
|
domain_(std::move(domain)),
|
||||||
|
metadata_(std::move(metadata))
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The reason of the error.
|
||||||
|
*
|
||||||
|
* This is a constant value that identifies the proximate cause of the error.
|
||||||
|
* Error reasons are unique within a particular domain of errors. This should
|
||||||
|
* be at most 63 characters and match a regular expression of
|
||||||
|
* `[A-Z][A-Z0-9_]+[A-Z0-9]`, which represents UPPER_SNAKE_CASE.
|
||||||
|
*/
|
||||||
|
std::string const &reason() const { return reason_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The logical grouping to which the "reason" belongs.
|
||||||
|
*
|
||||||
|
* The error domain is typically the registered service name of the tool or
|
||||||
|
* product that generates the error. Example: "pubsub.googleapis.com". If the
|
||||||
|
* error is generated by some common infrastructure, the error domain must be
|
||||||
|
* a globally unique value that identifies the infrastructure. For Google API
|
||||||
|
* infrastructure, the error domain is "googleapis.com".
|
||||||
|
*
|
||||||
|
* For errors generated by the C++ client libraries the domain is
|
||||||
|
* `gcloud-cpp`.
|
||||||
|
*/
|
||||||
|
std::string const &domain() const { return domain_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Additional structured details about this error.
|
||||||
|
*
|
||||||
|
* Keys should match the regular expression `[a-zA-Z0-9-_]` and be limited
|
||||||
|
* to 64 characters in length.
|
||||||
|
*
|
||||||
|
* When identifying the current value of an exceeded limit, the units should
|
||||||
|
* be contained in the key, not the value. For example, if the client exceeds
|
||||||
|
* the number of instances that can be created in a single (batch) request
|
||||||
|
* return `{"instanceLimitPerRequest": "100"}` rather than
|
||||||
|
* `{"instanceLimit": "100/request"}`.
|
||||||
|
*/
|
||||||
|
std::unordered_map<std::string, std::string> const &metadata() const
|
||||||
|
{
|
||||||
|
return metadata_;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator==(ErrorInfo const &, ErrorInfo const &);
|
||||||
|
friend bool operator!=(ErrorInfo const &, ErrorInfo const &);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend void internal::AddMetadata(ErrorInfo &,
|
||||||
|
std::string const &key,
|
||||||
|
std::string value);
|
||||||
|
|
||||||
|
std::string reason_;
|
||||||
|
std::string domain_;
|
||||||
|
std::unordered_map<std::string, std::string> metadata_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents success or an error with info about the error.
|
||||||
|
*
|
||||||
|
* This class is typically used to indicate whether or not a function or other
|
||||||
|
* operation completed successfully. Success is indicated by an "OK" status. OK
|
||||||
|
* statuses will have `.code() == StatusCode::kOk` and `.ok() == true`, with
|
||||||
|
* all other properties having empty values. All OK statuses are equal. Any
|
||||||
|
* non-OK `Status` is considered an error. Users can inspect the error using
|
||||||
|
* the member functions, or they can simply stream the `Status` object, and it
|
||||||
|
* will print itself in some human readable way (the streamed format may change
|
||||||
|
* over time and you should *not* depend on the specific format of a streamed
|
||||||
|
* `Status` object remaining unchanged).
|
||||||
|
*
|
||||||
|
* This is a regular value type that can be copied, moved, compared for
|
||||||
|
* equality, and streamed.
|
||||||
|
*/
|
||||||
|
class Status {
|
||||||
|
public:
|
||||||
|
/// Default constructor, initializes to `StatusCode::kOk`.
|
||||||
|
Status();
|
||||||
|
/// Destructor.
|
||||||
|
~Status();
|
||||||
|
///@{
|
||||||
|
/**
|
||||||
|
* @name Copy construction and assignment.
|
||||||
|
*/
|
||||||
|
Status(Status const &);
|
||||||
|
Status &operator=(Status const &);
|
||||||
|
///@}
|
||||||
|
///@{
|
||||||
|
/**
|
||||||
|
* @name Move construction and assignment.
|
||||||
|
*/
|
||||||
|
Status(Status &&) noexcept;
|
||||||
|
Status &operator=(Status &&) noexcept;
|
||||||
|
///@}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct from a status code, message and (optional) error info.
|
||||||
|
*
|
||||||
|
* @param code the status code for the new `Status`.
|
||||||
|
* @param message the message for the new `Status`, ignored if @p code is
|
||||||
|
* `StatusCode::kOk`.
|
||||||
|
* @param info the `ErrorInfo` for the new `Status`, ignored if @p code is
|
||||||
|
* `SStatusCode::kOk`.
|
||||||
|
*/
|
||||||
|
explicit Status(StatusCode code, std::string message, ErrorInfo info = {});
|
||||||
|
|
||||||
|
/// Returns true if the status code is `StatusCode::kOk`.
|
||||||
|
bool ok() const { return !impl_; }
|
||||||
|
|
||||||
|
/// Returns the status code.
|
||||||
|
StatusCode code() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the message associated with the status.
|
||||||
|
*
|
||||||
|
* This is always empty if `code()` is `StatusCode::kOk`.
|
||||||
|
*/
|
||||||
|
std::string const &message() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the additional error info associated with the status.
|
||||||
|
*
|
||||||
|
* This is always a default-constructed error info if `code()` is
|
||||||
|
* `StatusCode::kOk`.
|
||||||
|
*/
|
||||||
|
ErrorInfo const &error_info() const;
|
||||||
|
|
||||||
|
friend inline bool operator==(Status const &a, Status const &b)
|
||||||
|
{
|
||||||
|
return (a.ok() && b.ok()) || Equals(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend inline bool operator!=(Status const &a, Status const &b)
|
||||||
|
{
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool Equals(Status const &a, Status const &b);
|
||||||
|
friend void internal::SetPayload(Status &, std::string, std::string);
|
||||||
|
friend ulib::optional<std::string>
|
||||||
|
internal::GetPayload(Status const &, std::string const &);
|
||||||
|
|
||||||
|
class Impl;
|
||||||
|
// A null `impl_` is an OK status. Only non-OK Statuses allocate an Impl.
|
||||||
|
std::unique_ptr<Impl> impl_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream @p s to @p os.
|
||||||
|
*
|
||||||
|
* This in intended for logging and troubleshooting. Applications should not
|
||||||
|
* depend on the format of this output.
|
||||||
|
*/
|
||||||
|
std::ostream &operator<<(std::ostream &os, Status const &s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A runtime error that wraps a `google::cloud::Status`.
|
||||||
|
*/
|
||||||
|
class RuntimeStatusError : public std::runtime_error {
|
||||||
|
public:
|
||||||
|
/// Constructor from a `Status`.
|
||||||
|
explicit RuntimeStatusError(Status status);
|
||||||
|
|
||||||
|
/// Returns the original status.
|
||||||
|
Status const &status() const { return status_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Status status_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}// namespace ulib
|
||||||
|
|
||||||
|
#endif// GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STATUS_H
|
355
src/ulib/status_or.h
Normal file
355
src/ulib/status_or.h
Normal file
@ -0,0 +1,355 @@
|
|||||||
|
// Copyright 2018 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STATUS_OR_H
|
||||||
|
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STATUS_OR_H
|
||||||
|
|
||||||
|
#include "status.h"
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace ulib {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds a value or a `Status` indicating why there is no value.
|
||||||
|
*
|
||||||
|
* `StatusOr<T>` represents either a usable `T` value or a `Status` object
|
||||||
|
* explaining why a `T` value is not present. Typical usage of `StatusOr<T>`
|
||||||
|
* looks like usage of a smart pointer, or even a `std::optional<T>`, in that
|
||||||
|
* you first check its validity using a conversion to bool (or by calling
|
||||||
|
* `StatusOr::ok()`), then you may dereference the object to access the
|
||||||
|
* contained value.
|
||||||
|
*
|
||||||
|
* It is undefined behavior (UB) to dereference a `StatusOr<T>` that is not
|
||||||
|
* "ok". For example:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* StatusOr<Foo> foo = FetchFoo();
|
||||||
|
* if (!foo) { // Same as !foo.ok()
|
||||||
|
* // handle error and probably look at foo.status()
|
||||||
|
* } else {
|
||||||
|
* foo->DoSomethingFooey(); // UB if !foo
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Alternatively, you may call the `StatusOr::value()` member function,
|
||||||
|
* which is defined to: (1) throw an exception if there is no `T` value, or (2)
|
||||||
|
* crash the program if exceptions are disabled. It is never UB to call
|
||||||
|
* `value()`.
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* StatusOr<Foo> foo = FetchFoo();
|
||||||
|
* foo.value().DoSomethingFooey(); // May throw/crash if there is no value
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Functions that can fail will often return a `StatusOr<T>` instead of
|
||||||
|
* returning an error code and taking a `T` out-param, or rather than directly
|
||||||
|
* returning the `T` and throwing an exception on error. `StatusOr<T>` is used
|
||||||
|
* so that callers can choose whether they want to explicitly check for errors,
|
||||||
|
* crash the program, or throw exceptions.
|
||||||
|
*
|
||||||
|
* Since constructors do not have a return value, they should be designed in
|
||||||
|
* such a way that they cannot fail by moving the object's complex
|
||||||
|
* initialization logic into a separate factory function that itself can return
|
||||||
|
* a `StatusOr<T>`. For example:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* class Bar {
|
||||||
|
* public:
|
||||||
|
* Bar(Arg arg);
|
||||||
|
* ...
|
||||||
|
* };
|
||||||
|
* StatusOr<Bar> MakeBar() {
|
||||||
|
* ... complicated logic that might fail
|
||||||
|
* return Bar(std::move(arg));
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* `StatusOr<T>` supports equality comparisons if the underlying type `T` does.
|
||||||
|
*
|
||||||
|
* @tparam T the type of the value.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
class StatusOr final {
|
||||||
|
public:
|
||||||
|
static_assert(!std::is_reference<T>::value,
|
||||||
|
"StatusOr<T> requires T to **not** be a reference type");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A `value_type` member for use in generic programming.
|
||||||
|
*
|
||||||
|
* This is analogous to that of `std::optional::value_type`.
|
||||||
|
*/
|
||||||
|
using value_type = T;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes with an error status (`StatusCode::kUnknown`).
|
||||||
|
*/
|
||||||
|
StatusOr() : StatusOr(MakeDefaultStatus()) {}
|
||||||
|
|
||||||
|
StatusOr(StatusOr const &) = default;
|
||||||
|
StatusOr &operator=(StatusOr const &) = default;
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(performance-noexcept-move-constructor)
|
||||||
|
StatusOr(StatusOr &&other)
|
||||||
|
: status_(std::move(other.status_)),
|
||||||
|
value_(std::move(other.value_))
|
||||||
|
{
|
||||||
|
other.status_ = MakeDefaultStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(performance-noexcept-move-constructor)
|
||||||
|
StatusOr &operator=(StatusOr &&other)
|
||||||
|
{
|
||||||
|
status_ = std::move(other.status_);
|
||||||
|
value_ = std::move(other.value_);
|
||||||
|
other.status_ = MakeDefaultStatus();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new `StatusOr<T>` holding the error condition @p rhs.
|
||||||
|
*
|
||||||
|
* @par Post-conditions
|
||||||
|
* `ok() == false` and `status() == rhs`.
|
||||||
|
*
|
||||||
|
* @param rhs the status to initialize the object.
|
||||||
|
* @throws std::invalid_argument if `rhs.ok()`. If exceptions are disabled the
|
||||||
|
* program terminates via `google::cloud::Terminate()`
|
||||||
|
*/
|
||||||
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||||
|
StatusOr(Status rhs) : status_(std::move(rhs))
|
||||||
|
{
|
||||||
|
if (status_.ok()) {
|
||||||
|
// google::cloud::internal::ThrowInvalidArgument(__func__);
|
||||||
|
throw std::invalid_argument(__func__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigns the given non-OK Status to this `StatusOr<T>`.
|
||||||
|
*
|
||||||
|
* @throws std::invalid_argument if `status.ok()`. If exceptions are disabled
|
||||||
|
* the program terminates via `google::cloud::Terminate()`
|
||||||
|
*/
|
||||||
|
StatusOr &operator=(Status status)
|
||||||
|
{
|
||||||
|
*this = StatusOr(std::move(status));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assign a `T` (or anything convertible to `T`) into the `StatusOr`.
|
||||||
|
*
|
||||||
|
* This function does not participate in overload resolution if `U` is equal
|
||||||
|
* to `StatusOr<T>` (or to a cv-ref-qualified `StatusOr<T>`).
|
||||||
|
*
|
||||||
|
* @return a reference to this object.
|
||||||
|
* @tparam U a type convertible to @p T.
|
||||||
|
*/
|
||||||
|
template<typename U = T,
|
||||||
|
/// @cond implementation_details
|
||||||
|
std::enable_if_t<!std::is_same<StatusOr, std::decay_t<U>>::value,
|
||||||
|
int> = 0
|
||||||
|
/// @endcond
|
||||||
|
>
|
||||||
|
StatusOr &operator=(U &&rhs)
|
||||||
|
{
|
||||||
|
status_ = Status();
|
||||||
|
value_ = std::forward<U>(rhs);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new `StatusOr<T>` holding the value @p rhs.
|
||||||
|
*
|
||||||
|
* @par Post-conditions
|
||||||
|
* `ok() == true` and `value() == rhs`.
|
||||||
|
*
|
||||||
|
* @param rhs the value used to initialize the object.
|
||||||
|
*
|
||||||
|
* @throws ... If `T`'s move constructor throws.
|
||||||
|
*/
|
||||||
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||||
|
StatusOr(T &&rhs) : value_(std::move(rhs)) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new `StatusOr<T>` holding the value @p rhs.
|
||||||
|
*
|
||||||
|
* @par Post-conditions
|
||||||
|
* `ok() == true` and `value() == rhs`.
|
||||||
|
*
|
||||||
|
* @param rhs the value used to initialize the object.
|
||||||
|
*
|
||||||
|
* @throws ... If `T` copy constructor throws.
|
||||||
|
*/
|
||||||
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||||
|
StatusOr(T const &rhs) : value_(rhs) {}
|
||||||
|
|
||||||
|
/// Returns `true` when `this` holds a value.
|
||||||
|
bool ok() const { return status_.ok(); }
|
||||||
|
|
||||||
|
/// Returns `true` when `this` holds a value.
|
||||||
|
explicit operator bool() const { return status_.ok(); }
|
||||||
|
|
||||||
|
///@{
|
||||||
|
/**
|
||||||
|
* @name Dereference operators.
|
||||||
|
*
|
||||||
|
* @par Pre-conditions
|
||||||
|
* @parblock
|
||||||
|
* `ok() == true`
|
||||||
|
*
|
||||||
|
* @warning Using these operators when `ok() == false` results in undefined
|
||||||
|
* behavior.
|
||||||
|
* @endparblock
|
||||||
|
*
|
||||||
|
* @return A properly ref and const-qualified reference to the underlying
|
||||||
|
* value.
|
||||||
|
*/
|
||||||
|
T &operator*() & { return *value_; }
|
||||||
|
|
||||||
|
T const &operator*() const & { return *value_; }
|
||||||
|
|
||||||
|
T &&operator*() && { return *std::move(value_); }
|
||||||
|
|
||||||
|
T const &&operator*() const && { return *std::move(value_); }
|
||||||
|
|
||||||
|
///@}
|
||||||
|
|
||||||
|
///@{
|
||||||
|
/**
|
||||||
|
* @name Member access operators.
|
||||||
|
*
|
||||||
|
* @par Pre-conditions
|
||||||
|
* @parblock
|
||||||
|
* `ok() == true`
|
||||||
|
*
|
||||||
|
* @warning Using these operators when `ok() == false` results in undefined
|
||||||
|
* behavior.
|
||||||
|
* @endparblock
|
||||||
|
*
|
||||||
|
* @return A properly ref and const-qualified pointer to the underlying value.
|
||||||
|
*/
|
||||||
|
T *operator->() & { return &*value_; }
|
||||||
|
|
||||||
|
T const *operator->() const & { return &*value_; }
|
||||||
|
|
||||||
|
///@}
|
||||||
|
|
||||||
|
///@{
|
||||||
|
/**
|
||||||
|
* @name Value accessors.
|
||||||
|
*
|
||||||
|
* @return All these member functions return a (properly ref and
|
||||||
|
* const-qualified) reference to the underlying value.
|
||||||
|
*
|
||||||
|
* @throws RuntimeStatusError with the contents of `status()` if the object
|
||||||
|
* does not contain a value, i.e., if `ok() == false`.
|
||||||
|
*/
|
||||||
|
T &value() &
|
||||||
|
{
|
||||||
|
CheckHasValue();
|
||||||
|
return **this;
|
||||||
|
}
|
||||||
|
|
||||||
|
T const &value() const &
|
||||||
|
{
|
||||||
|
CheckHasValue();
|
||||||
|
return **this;
|
||||||
|
}
|
||||||
|
|
||||||
|
T &&value() &&
|
||||||
|
{
|
||||||
|
CheckHasValue();
|
||||||
|
return std::move(**this);
|
||||||
|
}
|
||||||
|
|
||||||
|
T const &&value() const &&
|
||||||
|
{
|
||||||
|
CheckHasValue();
|
||||||
|
return std::move(**this);
|
||||||
|
}
|
||||||
|
|
||||||
|
///@}
|
||||||
|
|
||||||
|
///@{
|
||||||
|
/**
|
||||||
|
* @name Status accessors.
|
||||||
|
*
|
||||||
|
* @return A reference to the contained `Status`.
|
||||||
|
*/
|
||||||
|
Status const &status() const & { return status_; }
|
||||||
|
|
||||||
|
Status &&status() && { return std::move(status_); }
|
||||||
|
|
||||||
|
///@}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static Status MakeDefaultStatus()
|
||||||
|
{
|
||||||
|
return Status{StatusCode::kUnknown, "default"};
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckHasValue() const &
|
||||||
|
{
|
||||||
|
if (!ok()) {
|
||||||
|
// internal::ThrowStatus(status_);
|
||||||
|
throw RuntimeStatusError(std::move(status_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When possible, do not copy the status.
|
||||||
|
void CheckHasValue() &&
|
||||||
|
{
|
||||||
|
if (!ok()) {
|
||||||
|
// internal::ThrowStatus(std::move(status_));
|
||||||
|
throw RuntimeStatusError(std::move(status_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Status status_;
|
||||||
|
ulib::optional<T> value_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns true IFF both `StatusOr<T>` objects hold an equal `Status` or an
|
||||||
|
// equal instance of `T`. This function requires that `T` supports equality.
|
||||||
|
template<typename T>
|
||||||
|
bool
|
||||||
|
operator==(StatusOr<T> const &a, StatusOr<T> const &b)
|
||||||
|
{
|
||||||
|
if (!a || !b) return a.status() == b.status();
|
||||||
|
return *a == *b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true of `a` and `b` are not equal. See `operator==` docs above for
|
||||||
|
// the definition of equal.
|
||||||
|
template<typename T>
|
||||||
|
bool
|
||||||
|
operator!=(StatusOr<T> const &a, StatusOr<T> const &b)
|
||||||
|
{
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
StatusOr<T>
|
||||||
|
make_status_or(T rhs)
|
||||||
|
{
|
||||||
|
return StatusOr<T>(std::move(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace ulib
|
||||||
|
|
||||||
|
#endif// GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STATUS_OR_H
|
@ -3,23 +3,21 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
add_executable(ulib_test
|
add_executable(
|
||||||
ulib/base/types_unittest.cpp
|
ulib_test
|
||||||
ulib/log/log_unittest.cpp
|
ulib/base/types_unittest.cpp
|
||||||
ulib/concorrency/mutex_unittest.cpp
|
ulib/log/log_unittest.cpp
|
||||||
ulib/concorrency/event_unittest.cpp
|
ulib/concorrency/mutex_unittest.cpp
|
||||||
ulib/concorrency/countdown_latch_unittest.cpp
|
ulib/concorrency/event_unittest.cpp
|
||||||
ulib/system/thread_unittest.cpp
|
ulib/concorrency/countdown_latch_unittest.cpp
|
||||||
ulib/system/thread_pool_unittest.cpp
|
ulib/system/thread_unittest.cpp
|
||||||
ulib/system/timer_unittest.cpp
|
ulib/system/thread_pool_unittest.cpp
|
||||||
3party/inja/inja_unittest.cpp
|
ulib/system/timer_unittest.cpp
|
||||||
3party/optional/optional_unittest.cpp
|
3party/inja/inja_unittest.cpp
|
||||||
3party/sqlpp11/sqlpp11_unittest.cpp
|
3party/optional/optional_unittest.cpp
|
||||||
ulib/utils/defer_unittest.cpp
|
3party/sqlpp11/sqlpp11_unittest.cpp
|
||||||
)
|
ulib/utils/defer_unittest.cpp
|
||||||
target_link_libraries(ulib_test PRIVATE
|
ulib/status_or_unittest.cpp)
|
||||||
ulib
|
target_link_libraries(ulib_test PRIVATE ulib gtest gtest_main)
|
||||||
gtest
|
|
||||||
gtest_main)
|
|
||||||
|
|
||||||
add_test(NAME ulib_test COMMAND ulib_test)
|
add_test(NAME ulib_test COMMAND ulib_test)
|
||||||
|
11
tests/ulib/status_or_unittest.cpp
Normal file
11
tests/ulib/status_or_unittest.cpp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <ulib/status_or.h>
|
||||||
|
|
||||||
|
TEST(StatusOr, EmptyValue)
|
||||||
|
{
|
||||||
|
ulib::StatusOr<int> s;
|
||||||
|
EXPECT_FALSE(s.ok());
|
||||||
|
s = 1;
|
||||||
|
EXPECT_TRUE(s.ok());
|
||||||
|
EXPECT_EQ(s.value(), 1);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user