// // Copyright (C) 2022 The Android Open Source Project // // 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. #pragma once #include #include #include #include // IWYU pragma: export namespace cuttlefish { using android::base::Result; #define CF_ERR_MSG() \ " at " << __FILE__ << ":" << __LINE__ << "\n" \ << " in " << __PRETTY_FUNCTION__ /** * Error return macro that includes the location in the file in the error * message. Use CF_ERRNO when including information from errno, otherwise use * the base CF_ERR macro. * * Example usage: * * if (mkdir(path.c_str()) != 0) { * return CF_ERRNO("mkdir(\"" << path << "\") failed: " * << strerror(errno)); * } * * This will return an error with the text * * mkdir(...) failed: ... * at path/to/file.cpp:50 * in Result MyFunction() */ #define CF_ERR(MSG) android::base::Error() << MSG << "\n" << CF_ERR_MSG() #define CF_ERRNO(MSG) \ android::base::ErrnoError() << MSG << "\n" \ << CF_ERR_MSG() << "\n with errno " << errno template T OutcomeDereference(std::optional&& value) { return std::move(*value); } template typename std::conditional_t, bool, T> OutcomeDereference( Result&& result) { if constexpr (std::is_void::value) { return result.ok(); } else { return std::move(*result); } } template typename std::enable_if, T>::type OutcomeDereference(T&& value) { return std::forward(value); } inline bool TypeIsSuccess(bool value) { return value; } template bool TypeIsSuccess(std::optional& value) { return value.has_value(); } template bool TypeIsSuccess(Result& value) { return value.ok(); } template bool TypeIsSuccess(Result&& value) { return value.ok(); } inline auto ErrorFromType(bool) { return (android::base::Error() << "Received `false`").str(); } template inline auto ErrorFromType(std::optional) { return (android::base::Error() << "Received empty optional").str(); } template auto ErrorFromType(Result& value) { return value.error(); } template auto ErrorFromType(Result&& value) { return value.error(); } #define CF_EXPECT_OVERLOAD(_1, _2, NAME, ...) NAME #define CF_EXPECT2(RESULT, MSG) \ ({ \ decltype(RESULT)&& macro_intermediate_result = RESULT; \ if (!TypeIsSuccess(macro_intermediate_result)) { \ return android::base::Error() \ << ErrorFromType(macro_intermediate_result) << "\n" \ << MSG << "\n" \ << CF_ERR_MSG() << "\n" \ << " for CF_EXPECT(" << #RESULT << ")"; \ }; \ OutcomeDereference(std::move(macro_intermediate_result)); \ }) #define CF_EXPECT1(RESULT) CF_EXPECT2(RESULT, "Received error") /** * Error propagation macro that can be used as an expression. * * The first argument can be either a Result or a type that is convertible to * a boolean. A successful result will return the value inside the result, or * a conversion to a `true` value will return the unconverted value. This is * useful for e.g. pointers which can be tested through boolean conversion. * * In the failure case, this macro will return from the containing function * with a failing Result. The failing result will include information about the * call site, details from the optional second argument if given, and details * from the failing inner expression if it is a Result. * * This macro must be invoked only in functions that return a Result. * * Example usage: * * Result CreateTempDir(); * * Result CreatePopulatedTempDir() { * std::string dir = CF_EXPECT(CreateTempDir(), "Failed to create dir"); * // Do something with dir * return dir; * } * * If CreateTempDir fails, the function will returna Result with an error * message that looks like * * Internal error * at /path/to/otherfile.cpp:50 * in Result CreateTempDir() * Failed to create dir * at /path/to/file.cpp:81: * in Result CreatePopulatedTempDir() * for CF_EXPECT(CreateTempDir()) */ #define CF_EXPECT(...) \ CF_EXPECT_OVERLOAD(__VA_ARGS__, CF_EXPECT2, CF_EXPECT1)(__VA_ARGS__) } // namespace cuttlefish