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_