flex/utils/result.h (104 lines of code) (raw):
/** Copyright 2020 Alibaba Group Holding Limited.
*
* 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
*
* http://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 UTILS_RESULT_H_
#define UTILS_RESULT_H_
#include <filesystem>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
#include "flex/utils/error_pb/interactive.pb.h"
#include "glog/logging.h"
namespace gs {
using StatusCode = gs::flex::interactive::Code;
class Status {
public:
Status() noexcept;
Status(StatusCode error_code) noexcept;
Status(StatusCode error_code, std::string&& error_msg) noexcept;
Status(StatusCode error_code, const std::string& error_msg) noexcept;
bool ok() const;
std::string error_message() const;
StatusCode error_code() const;
static Status OK();
std::string ToString() const;
private:
StatusCode error_code_;
std::string error_msg_;
};
// Define a class with name Result<T>, which is a template class
// Stores the result of a function that may fail.
// If the function succeeds, the result contains the value returned by the
// function. If the function fails, the result contains the error message. The
// result is always valid and can be queried for success or failure. If the
// result is successful, the value can be obtained by calling the value()
// method. If the result fails, the error message can be obtained by calling the
// error() method. The result can be converted to bool, and the result is true
// if the result is successful. The result can be converted to bool, and the
// result is false if the result fails.
template <typename T>
class Result {
public:
using ValueType = T;
Result() : status_(StatusCode::OK) {}
Result(const ValueType& value) : status_(StatusCode::OK), value_(value) {}
Result(ValueType&& value)
: status_(StatusCode::OK), value_(std::move(value)) {}
Result(const Status& status, ValueType&& value)
: status_(status), value_(std::move(value)) {}
Result(const Status& status) : status_(status) {}
Result(const Status& status, const ValueType& value)
: status_(status), value_(value) {}
Result(StatusCode code, const std::string& error_msg, const ValueType& value)
: status_(code, error_msg), value_(value) {}
Result(StatusCode code, std::string&& error_msg, const ValueType& value)
: status_(code, std::move(error_msg)), value_(value) {}
bool ok() const noexcept { return status_.ok(); }
const Status& status() const noexcept { return status_; }
ValueType& value() noexcept { return value_; }
// return rvalue
ValueType&& move_value() noexcept { return std::move(value_); }
private:
Status status_;
ValueType value_;
};
template <typename T>
struct is_gs_result_type : std::false_type {};
template <typename T>
struct is_gs_result_type<Result<T>> : std::true_type {};
template <typename T>
struct is_gs_status_type : std::false_type {};
template <>
struct is_gs_status_type<Status> : std::true_type {};
// define a macro, which checks the return status of a function, if ok, continue
// to execute, otherwise, return the status.
// the macro accept the calling code of a function, and the function name.
#define RETURN_IF_NOT_OK(expr) \
do { \
auto status = (expr); \
if (!status.ok()) { \
return status; \
} \
} while (0)
// a Macro automatically assign the return value of a function, which returns
// result to a variable, and check the status of the result, if ok, continue to
// execute, otherwise, return the status. the macro accept the calling code of a
// function, the function name, and the variable name.
// reference:
// https://github.com/boostorg/leaf/blob/develop/include/boost/leaf/error.hpp
#define ASSIGN_AND_RETURN_IF_RESULT_NOT_OK(var, expr) \
{ \
auto&& FLEX_TMP_VAR = expr; \
static_assert( \
::gs::is_gs_result_type< \
typename std::decay<decltype(FLEX_TMP_VAR)>::type>::value, \
"The expression must return a Result type"); \
if (!FLEX_TMP_VAR.ok()) { \
return FLEX_TMP_VAR; \
} \
var = std::forward<decltype(FLEX_TMP_VAR)>(FLEX_TMP_VAR).move_value(); \
}
#define ASSIGN_AND_RETURN_IF_STATUS_NOT_OK(var, expr) \
{ \
auto&& FLEX_TMP_VAR = expr; \
static_assert( \
::gs::is_gs_status_type< \
typename std::decay<decltype(FLEX_TMP_VAR)>::type>::value, \
"The expression must return a Status type"); \
if (!FLEX_TMP_VAR.ok()) { \
return FLEX_TMP_VAR; \
} \
var = std::forward<decltype(FLEX_TMP_VAR)>(FLEX_TMP_VAR).move_value(); \
}
// A Marco automatically use a auto variable to store the return value of a
// function, which returns result, and check the status of the result, if ok,
// continue to execute, otherwise, return the status. the macro accept the
// calling code of a function, the function name, and the variable name.
#define FLEX_AUTO(var, expr) ASSIGN_AND_RETURN_IF_NOT_OK(auto var, expr)
// Return boost::leaf::error object with error code and error message,
#define RETURN_FLEX_LEAF_ERROR(code, msg) \
return ::boost::leaf::new_error( \
gs::Status(::gs::flex::interactive::Code::code, msg))
} // namespace gs
namespace std {
inline std::string to_string(const gs::flex::interactive::Code& status) {
// format the code into 0x-xxxx, where multiple zeros are prepend to the code
std::stringstream ss;
ss << "05-" << std::setw(4) << std::setfill('0')
<< static_cast<int32_t>(status);
return ss.str();
}
} // namespace std
#endif // UTILS_RESULT_H_