blob: 2b732e4e9121c7a80587a1e21af00e5ddd3e0b7d [file] [log] [blame]
//
// 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 <optional>
#include <type_traits>
#include <android-base/logging.h>
#include <android-base/result.h> // 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<std::string> 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 <typename T>
T OutcomeDereference(std::optional<T>&& value) {
return std::move(*value);
}
template <typename T>
typename std::conditional_t<std::is_void_v<T>, bool, T> OutcomeDereference(
Result<T>&& result) {
if constexpr (std::is_void<T>::value) {
return result.ok();
} else {
return std::move(*result);
}
}
template <typename T>
typename std::enable_if<std::is_convertible_v<T, bool>, T>::type
OutcomeDereference(T&& value) {
return std::forward<T>(value);
}
inline bool TypeIsSuccess(bool value) { return value; }
template <typename T>
bool TypeIsSuccess(std::optional<T>& value) {
return value.has_value();
}
template <typename T>
bool TypeIsSuccess(Result<T>& value) {
return value.ok();
}
template <typename T>
bool TypeIsSuccess(Result<T>&& value) {
return value.ok();
}
inline auto ErrorFromType(bool) {
return (android::base::Error() << "Received `false`").str();
}
template <typename T>
inline auto ErrorFromType(std::optional<T>) {
return (android::base::Error() << "Received empty optional").str();
}
template <typename T>
auto ErrorFromType(Result<T>& value) {
return value.error();
}
template <typename T>
auto ErrorFromType(Result<T>&& 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<std::string> CreateTempDir();
*
* Result<std::string> 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<std::string> CreateTempDir()
* Failed to create dir
* at /path/to/file.cpp:81:
* in Result<std::string> CreatePopulatedTempDir()
* for CF_EXPECT(CreateTempDir())
*/
#define CF_EXPECT(...) \
CF_EXPECT_OVERLOAD(__VA_ARGS__, CF_EXPECT2, CF_EXPECT1)(__VA_ARGS__)
} // namespace cuttlefish