2014-10-22 23:20:01 -04:00
|
|
|
// Copyright 2014 Citra Emulator Project
|
2014-12-17 00:38:14 -05:00
|
|
|
// Licensed under GPLv2 or any later version
|
2014-10-22 23:20:01 -04:00
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
2015-06-21 08:40:28 -04:00
|
|
|
#include <new>
|
2014-10-22 23:20:01 -04:00
|
|
|
#include <utility>
|
2015-05-06 03:06:12 -04:00
|
|
|
#include "common/assert.h"
|
2014-10-22 23:20:01 -04:00
|
|
|
#include "common/bit_field.h"
|
2015-01-11 00:42:18 -05:00
|
|
|
#include "common/common_types.h"
|
2014-10-22 23:20:01 -04:00
|
|
|
|
2017-10-31 19:26:11 -04:00
|
|
|
// All the constants in this file come from http://switchbrew.org/index.php?title=Error_codes
|
2014-10-22 23:20:01 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Identifies the module which caused the error. Error codes can be propagated through a call
|
|
|
|
* chain, meaning that this doesn't always correspond to the module where the API call made is
|
|
|
|
* contained.
|
|
|
|
*/
|
|
|
|
enum class ErrorModule : u32 {
|
|
|
|
Common = 0,
|
|
|
|
Kernel = 1,
|
2017-10-31 19:26:11 -04:00
|
|
|
FS = 2,
|
2018-05-23 08:22:42 -04:00
|
|
|
OS = 3, // used for Memory, Thread, Mutex, Nvidia
|
|
|
|
HTCS = 4,
|
2017-10-31 19:26:11 -04:00
|
|
|
NCM = 5,
|
|
|
|
DD = 6,
|
|
|
|
LR = 8,
|
|
|
|
Loader = 9,
|
|
|
|
CMIF = 10,
|
|
|
|
HIPC = 11,
|
|
|
|
PM = 15,
|
|
|
|
NS = 16,
|
|
|
|
HTC = 18,
|
2018-05-19 18:02:24 -04:00
|
|
|
NCMContent = 20,
|
2017-10-31 19:26:11 -04:00
|
|
|
SM = 21,
|
|
|
|
RO = 22,
|
|
|
|
SDMMC = 24,
|
2018-05-19 18:02:24 -04:00
|
|
|
OVLN = 25,
|
2017-10-31 19:26:11 -04:00
|
|
|
SPL = 26,
|
|
|
|
ETHC = 100,
|
|
|
|
I2C = 101,
|
2018-05-19 18:02:24 -04:00
|
|
|
GPIO = 102,
|
|
|
|
UART = 103,
|
2017-10-31 19:26:11 -04:00
|
|
|
Settings = 105,
|
2018-05-19 18:02:24 -04:00
|
|
|
WLAN = 107,
|
|
|
|
XCD = 108,
|
2017-10-31 19:26:11 -04:00
|
|
|
NIFM = 110,
|
2018-05-19 18:02:24 -04:00
|
|
|
Hwopus = 111,
|
|
|
|
Bluetooth = 113,
|
|
|
|
VI = 114,
|
|
|
|
NFP = 115,
|
|
|
|
Time = 116,
|
2017-10-31 19:26:11 -04:00
|
|
|
FGM = 117,
|
2018-05-23 08:22:42 -04:00
|
|
|
OE = 118,
|
2018-05-19 18:02:24 -04:00
|
|
|
PCIe = 120,
|
2017-10-31 19:26:11 -04:00
|
|
|
Friends = 121,
|
2018-05-19 18:02:24 -04:00
|
|
|
BCAT = 122,
|
2021-05-16 01:46:30 -04:00
|
|
|
SSLSrv = 123,
|
2017-10-31 19:26:11 -04:00
|
|
|
Account = 124,
|
2018-05-19 18:02:24 -04:00
|
|
|
News = 125,
|
2017-10-31 19:26:11 -04:00
|
|
|
Mii = 126,
|
2018-05-19 18:02:24 -04:00
|
|
|
NFC = 127,
|
2017-10-31 19:26:11 -04:00
|
|
|
AM = 128,
|
|
|
|
PlayReport = 129,
|
2018-05-19 18:02:24 -04:00
|
|
|
AHID = 130,
|
|
|
|
Qlaunch = 132,
|
2017-10-31 19:26:11 -04:00
|
|
|
PCV = 133,
|
|
|
|
OMM = 134,
|
2018-05-19 18:02:24 -04:00
|
|
|
BPC = 135,
|
|
|
|
PSM = 136,
|
2017-10-31 19:26:11 -04:00
|
|
|
NIM = 137,
|
|
|
|
PSC = 138,
|
2018-05-19 18:02:24 -04:00
|
|
|
TC = 139,
|
2017-10-31 19:26:11 -04:00
|
|
|
USB = 140,
|
2018-05-19 18:02:24 -04:00
|
|
|
NSD = 141,
|
|
|
|
PCTL = 142,
|
2017-10-31 19:26:11 -04:00
|
|
|
BTM = 143,
|
2018-05-19 18:02:24 -04:00
|
|
|
ETicket = 145,
|
|
|
|
NGC = 146,
|
2017-10-31 19:26:11 -04:00
|
|
|
ERPT = 147,
|
|
|
|
APM = 148,
|
2018-05-23 08:22:42 -04:00
|
|
|
Profiler = 150,
|
2018-05-19 18:02:24 -04:00
|
|
|
ErrorUpload = 151,
|
|
|
|
Audio = 153,
|
2017-10-31 19:26:11 -04:00
|
|
|
NPNS = 154,
|
2018-05-19 18:02:24 -04:00
|
|
|
NPNSHTTPSTREAM = 155,
|
2017-10-31 19:26:11 -04:00
|
|
|
ARP = 157,
|
2018-05-23 08:22:42 -04:00
|
|
|
SWKBD = 158,
|
|
|
|
BOOT = 159,
|
2018-05-19 18:02:24 -04:00
|
|
|
NFCMifare = 161,
|
2017-10-31 19:26:11 -04:00
|
|
|
UserlandAssert = 162,
|
2018-05-19 18:02:24 -04:00
|
|
|
Fatal = 163,
|
|
|
|
NIMShop = 164,
|
|
|
|
SPSM = 165,
|
|
|
|
BGTC = 167,
|
2017-10-31 19:26:11 -04:00
|
|
|
UserlandCrash = 168,
|
2018-05-19 18:02:24 -04:00
|
|
|
SREPO = 180,
|
2018-05-23 08:22:42 -04:00
|
|
|
Dauth = 181,
|
2018-05-19 18:02:24 -04:00
|
|
|
HID = 202,
|
|
|
|
LDN = 203,
|
|
|
|
Irsensor = 205,
|
2017-10-31 19:26:11 -04:00
|
|
|
Capture = 206,
|
2018-05-19 18:02:24 -04:00
|
|
|
Manu = 208,
|
2018-05-23 08:22:42 -04:00
|
|
|
ATK = 209,
|
2018-05-19 18:02:24 -04:00
|
|
|
GRC = 212,
|
|
|
|
Migration = 216,
|
|
|
|
MigrationLdcServ = 217,
|
2017-10-31 19:26:11 -04:00
|
|
|
GeneralWebApplet = 800,
|
|
|
|
WifiWebAuthApplet = 809,
|
|
|
|
WhitelistedApplet = 810,
|
|
|
|
ShopN = 811,
|
2014-10-22 23:20:01 -04:00
|
|
|
};
|
|
|
|
|
2019-03-10 18:29:28 -04:00
|
|
|
/// Encapsulates a Horizon OS error code, allowing it to be separated into its constituent fields.
|
2014-10-22 23:20:01 -04:00
|
|
|
union ResultCode {
|
|
|
|
u32 raw;
|
|
|
|
|
2017-10-31 19:26:11 -04:00
|
|
|
BitField<0, 9, ErrorModule> module;
|
|
|
|
BitField<9, 13, u32> description;
|
2014-10-22 23:20:01 -04:00
|
|
|
|
2020-11-25 15:21:03 -05:00
|
|
|
constexpr explicit ResultCode(u32 raw_) : raw(raw_) {}
|
2017-05-20 23:40:13 -04:00
|
|
|
|
2017-10-31 19:26:11 -04:00
|
|
|
constexpr ResultCode(ErrorModule module_, u32 description_)
|
|
|
|
: raw(module.FormatValue(module_) | description.FormatValue(description_)) {}
|
2014-10-22 23:20:01 -04:00
|
|
|
|
2017-05-20 23:40:13 -04:00
|
|
|
constexpr bool IsSuccess() const {
|
2018-02-19 00:30:30 -05:00
|
|
|
return raw == 0;
|
2014-10-22 23:20:01 -04:00
|
|
|
}
|
|
|
|
|
2017-05-20 23:40:13 -04:00
|
|
|
constexpr bool IsError() const {
|
2018-02-19 00:30:30 -05:00
|
|
|
return raw != 0;
|
2014-10-22 23:20:01 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-05-20 23:40:13 -04:00
|
|
|
constexpr bool operator==(const ResultCode& a, const ResultCode& b) {
|
2014-10-22 23:20:01 -04:00
|
|
|
return a.raw == b.raw;
|
|
|
|
}
|
|
|
|
|
2017-05-20 23:40:13 -04:00
|
|
|
constexpr bool operator!=(const ResultCode& a, const ResultCode& b) {
|
2014-10-22 23:20:01 -04:00
|
|
|
return a.raw != b.raw;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convenience functions for creating some common kinds of errors:
|
|
|
|
|
|
|
|
/// The default success `ResultCode`.
|
2021-05-21 01:05:04 -04:00
|
|
|
constexpr ResultCode ResultSuccess(0);
|
2014-10-22 23:20:01 -04:00
|
|
|
|
2019-11-12 04:45:21 -05:00
|
|
|
/**
|
|
|
|
* Placeholder result code used for unknown error codes.
|
|
|
|
*
|
|
|
|
* @note This should only be used when a particular error code
|
|
|
|
* is not known yet.
|
|
|
|
*/
|
2021-05-21 01:06:31 -04:00
|
|
|
constexpr ResultCode ResultUnknown(UINT32_MAX);
|
2019-11-12 04:45:21 -05:00
|
|
|
|
2014-10-22 23:20:01 -04:00
|
|
|
/**
|
|
|
|
* This is an optional value type. It holds a `ResultCode` and, if that code is a success code,
|
|
|
|
* also holds a result of type `T`. If the code is an error code then trying to access the inner
|
|
|
|
* value fails, thus ensuring that the ResultCode of functions is always checked properly before
|
|
|
|
* their return value is used. It is similar in concept to the `std::optional` type
|
|
|
|
* (http://en.cppreference.com/w/cpp/experimental/optional) originally proposed for inclusion in
|
|
|
|
* C++14, or the `Result` type in Rust (http://doc.rust-lang.org/std/result/index.html).
|
|
|
|
*
|
|
|
|
* An example of how it could be used:
|
|
|
|
* \code
|
|
|
|
* ResultVal<int> Frobnicate(float strength) {
|
|
|
|
* if (strength < 0.f || strength > 1.0f) {
|
|
|
|
* // Can't frobnicate too weakly or too strongly
|
|
|
|
* return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Common,
|
|
|
|
* ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
|
|
|
|
* } else {
|
|
|
|
* // Frobnicated! Give caller a cookie
|
|
|
|
* return MakeResult<int>(42);
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* \endcode
|
|
|
|
*
|
|
|
|
* \code
|
|
|
|
* ResultVal<int> frob_result = Frobnicate(0.75f);
|
|
|
|
* if (frob_result) {
|
|
|
|
* // Frobbed ok
|
|
|
|
* printf("My cookie is %d\n", *frob_result);
|
|
|
|
* } else {
|
|
|
|
* printf("Guess I overdid it. :( Error code: %ux\n", frob_result.code().hex);
|
|
|
|
* }
|
|
|
|
* \endcode
|
|
|
|
*/
|
|
|
|
template <typename T>
|
|
|
|
class ResultVal {
|
|
|
|
public:
|
2016-09-17 20:38:01 -04:00
|
|
|
/// Constructs an empty `ResultVal` with the given error code. The code must not be a success
|
|
|
|
/// code.
|
2021-05-21 01:06:31 -04:00
|
|
|
ResultVal(ResultCode error_code = ResultUnknown) : result_code(error_code) {
|
2015-02-19 00:52:36 -05:00
|
|
|
ASSERT(error_code.IsError());
|
2014-10-22 23:20:01 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Similar to the non-member function `MakeResult`, with the exception that you can manually
|
|
|
|
* specify the success code. `success_code` must not be an error code.
|
|
|
|
*/
|
|
|
|
template <typename... Args>
|
|
|
|
static ResultVal WithCode(ResultCode success_code, Args&&... args) {
|
|
|
|
ResultVal<T> result;
|
|
|
|
result.emplace(success_code, std::forward<Args>(args)...);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-09-17 20:38:01 -04:00
|
|
|
ResultVal(const ResultVal& o) : result_code(o.result_code) {
|
2014-10-22 23:20:01 -04:00
|
|
|
if (!o.empty()) {
|
2016-03-05 03:10:52 -05:00
|
|
|
new (&object) T(o.object);
|
2014-10-22 23:20:01 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
hle/result: Make ResultVal's move constructor as noexcept
Many containers within the standard library provide different behaviors
based on whether or not a move constructor/assignment operator can be
guaranteed not to throw or not.
Notably, implementations will generally use std::move_if_noexcept (or an
internal implementation of it) to provide strong exception guarantees.
If a move constructor potentially throws (in other words, is not
noexcept), then certain behaviors will create copies, rather than moving
the values.
For example, consider std::vector. When a std::vector calls resize(),
there are two ways the elements can be relocated to the new block of
memory (if a reallocation happens), by copy, or by moving the existing
elements into the new block of memory. If a type does not have a
guarantee that it will not throw in the move constructor, a copy will
happen. However, if it can be guaranteed that the move constructor won't
throw, then the elements will be moved.
This just allows ResultVal to be moved instead of copied all the time if
ever used in conjunction with containers for whatever reason.
2018-08-28 09:39:47 -04:00
|
|
|
ResultVal(ResultVal&& o) noexcept : result_code(o.result_code) {
|
2014-10-22 23:20:01 -04:00
|
|
|
if (!o.empty()) {
|
2016-03-05 03:10:52 -05:00
|
|
|
new (&object) T(std::move(o.object));
|
2014-10-22 23:20:01 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
~ResultVal() {
|
|
|
|
if (!empty()) {
|
2016-03-05 03:10:52 -05:00
|
|
|
object.~T();
|
2014-10-22 23:20:01 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ResultVal& operator=(const ResultVal& o) {
|
2018-03-28 18:01:46 -04:00
|
|
|
if (this == &o) {
|
|
|
|
return *this;
|
|
|
|
}
|
2015-02-07 13:05:17 -05:00
|
|
|
if (!empty()) {
|
|
|
|
if (!o.empty()) {
|
2016-03-05 03:10:52 -05:00
|
|
|
object = o.object;
|
2014-10-22 23:20:01 -04:00
|
|
|
} else {
|
2016-03-05 03:10:52 -05:00
|
|
|
object.~T();
|
2014-10-22 23:20:01 -04:00
|
|
|
}
|
|
|
|
} else {
|
2015-02-07 13:05:17 -05:00
|
|
|
if (!o.empty()) {
|
2016-03-05 03:10:52 -05:00
|
|
|
new (&object) T(o.object);
|
2014-10-22 23:20:01 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
result_code = o.result_code;
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replaces the current result with a new constructed result value in-place. The code must not
|
|
|
|
* be an error code.
|
|
|
|
*/
|
|
|
|
template <typename... Args>
|
|
|
|
void emplace(ResultCode success_code, Args&&... args) {
|
2015-02-19 00:52:36 -05:00
|
|
|
ASSERT(success_code.IsSuccess());
|
2014-10-22 23:20:01 -04:00
|
|
|
if (!empty()) {
|
2016-03-05 03:10:52 -05:00
|
|
|
object.~T();
|
2014-10-22 23:20:01 -04:00
|
|
|
}
|
2016-03-05 03:10:52 -05:00
|
|
|
new (&object) T(std::forward<Args>(args)...);
|
2014-10-22 23:20:01 -04:00
|
|
|
result_code = success_code;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns true if the `ResultVal` contains an error code and no value.
|
2016-09-17 20:38:01 -04:00
|
|
|
bool empty() const {
|
|
|
|
return result_code.IsError();
|
|
|
|
}
|
2014-10-22 23:20:01 -04:00
|
|
|
|
|
|
|
/// Returns true if the `ResultVal` contains a return value.
|
2016-09-17 20:38:01 -04:00
|
|
|
bool Succeeded() const {
|
|
|
|
return result_code.IsSuccess();
|
|
|
|
}
|
2014-10-22 23:20:01 -04:00
|
|
|
/// Returns true if the `ResultVal` contains an error code and no value.
|
2016-09-17 20:38:01 -04:00
|
|
|
bool Failed() const {
|
|
|
|
return empty();
|
|
|
|
}
|
2014-10-22 23:20:01 -04:00
|
|
|
|
2016-09-17 20:38:01 -04:00
|
|
|
ResultCode Code() const {
|
|
|
|
return result_code;
|
|
|
|
}
|
2014-10-22 23:20:01 -04:00
|
|
|
|
2016-09-17 20:38:01 -04:00
|
|
|
const T& operator*() const {
|
|
|
|
return object;
|
|
|
|
}
|
|
|
|
T& operator*() {
|
|
|
|
return object;
|
|
|
|
}
|
|
|
|
const T* operator->() const {
|
|
|
|
return &object;
|
|
|
|
}
|
|
|
|
T* operator->() {
|
|
|
|
return &object;
|
|
|
|
}
|
2014-10-22 23:20:01 -04:00
|
|
|
|
|
|
|
/// Returns the value contained in this `ResultVal`, or the supplied default if it is missing.
|
|
|
|
template <typename U>
|
|
|
|
T ValueOr(U&& value) const {
|
2016-03-05 03:10:52 -05:00
|
|
|
return !empty() ? object : std::move(value);
|
2014-10-22 23:20:01 -04:00
|
|
|
}
|
|
|
|
|
2015-01-11 00:42:18 -05:00
|
|
|
/// Asserts that the result succeeded and returns a reference to it.
|
2017-06-18 21:49:46 -04:00
|
|
|
T& Unwrap() & {
|
2015-01-20 20:16:47 -05:00
|
|
|
ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal");
|
2015-01-11 00:42:18 -05:00
|
|
|
return **this;
|
|
|
|
}
|
|
|
|
|
2017-06-18 21:49:46 -04:00
|
|
|
T&& Unwrap() && {
|
|
|
|
ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal");
|
|
|
|
return std::move(**this);
|
|
|
|
}
|
|
|
|
|
2014-10-22 23:20:01 -04:00
|
|
|
private:
|
2016-03-05 03:10:52 -05:00
|
|
|
// A union is used to allocate the storage for the value, while allowing us to construct and
|
|
|
|
// destruct it at will.
|
2016-09-17 20:38:01 -04:00
|
|
|
union {
|
|
|
|
T object;
|
|
|
|
};
|
2014-10-22 23:20:01 -04:00
|
|
|
ResultCode result_code;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This function is a helper used to construct `ResultVal`s. It receives the arguments to construct
|
|
|
|
* `T` with and creates a success `ResultVal` contained the constructed value.
|
|
|
|
*/
|
|
|
|
template <typename T, typename... Args>
|
|
|
|
ResultVal<T> MakeResult(Args&&... args) {
|
2021-05-21 01:05:04 -04:00
|
|
|
return ResultVal<T>::WithCode(ResultSuccess, std::forward<Args>(args)...);
|
2014-10-22 23:20:01 -04:00
|
|
|
}
|
2015-01-11 00:42:18 -05:00
|
|
|
|
2017-06-06 02:13:58 -04:00
|
|
|
/**
|
|
|
|
* Deducible overload of MakeResult, allowing the template parameter to be ommited if you're just
|
|
|
|
* copy or move constructing.
|
|
|
|
*/
|
|
|
|
template <typename Arg>
|
|
|
|
ResultVal<std::remove_reference_t<Arg>> MakeResult(Arg&& arg) {
|
2021-05-21 01:05:04 -04:00
|
|
|
return ResultVal<std::remove_reference_t<Arg>>::WithCode(ResultSuccess, std::forward<Arg>(arg));
|
2017-06-06 02:13:58 -04:00
|
|
|
}
|
|
|
|
|
2015-01-11 00:42:18 -05:00
|
|
|
/**
|
|
|
|
* Check for the success of `source` (which must evaluate to a ResultVal). If it succeeds, unwraps
|
|
|
|
* the contained value and assigns it to `target`, which can be either an l-value expression or a
|
|
|
|
* variable declaration. If it fails the return code is returned from the current function. Thus it
|
|
|
|
* can be used to cascade errors out, achieving something akin to exception handling.
|
|
|
|
*/
|
2016-09-17 20:38:01 -04:00
|
|
|
#define CASCADE_RESULT(target, source) \
|
|
|
|
auto CONCAT2(check_result_L, __LINE__) = source; \
|
2020-08-14 09:04:44 -04:00
|
|
|
if (CONCAT2(check_result_L, __LINE__).Failed()) { \
|
2016-09-17 20:38:01 -04:00
|
|
|
return CONCAT2(check_result_L, __LINE__).Code(); \
|
2020-08-14 09:04:44 -04:00
|
|
|
} \
|
2016-09-17 20:38:01 -04:00
|
|
|
target = std::move(*CONCAT2(check_result_L, __LINE__))
|
2017-06-06 02:13:58 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Analogous to CASCADE_RESULT, but for a bare ResultCode. The code will be propagated if
|
|
|
|
* non-success, or discarded otherwise.
|
|
|
|
*/
|
|
|
|
#define CASCADE_CODE(source) \
|
2020-08-14 09:04:44 -04:00
|
|
|
do { \
|
|
|
|
auto CONCAT2(check_result_L, __LINE__) = source; \
|
|
|
|
if (CONCAT2(check_result_L, __LINE__).IsError()) { \
|
|
|
|
return CONCAT2(check_result_L, __LINE__); \
|
|
|
|
} \
|
|
|
|
} while (false)
|
2021-05-31 04:15:49 -04:00
|
|
|
|
|
|
|
#define R_SUCCEEDED(res) (res.IsSuccess())
|
|
|
|
|
|
|
|
/// Evaluates a boolean expression, and succeeds if that expression is true.
|
2021-05-21 01:05:04 -04:00
|
|
|
#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess)
|
2021-05-31 04:15:49 -04:00
|
|
|
|
|
|
|
/// Evaluates a boolean expression, and returns a result unless that expression is true.
|
|
|
|
#define R_UNLESS(expr, res) \
|
|
|
|
{ \
|
|
|
|
if (!(expr)) { \
|
|
|
|
if (res.IsError()) { \
|
|
|
|
LOG_ERROR(Kernel, "Failed with result: {}", res.raw); \
|
|
|
|
} \
|
|
|
|
return res; \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Evaluates an expression that returns a result, and returns the result if it would fail.
|
|
|
|
#define R_TRY(res_expr) \
|
|
|
|
{ \
|
|
|
|
const auto _tmp_r_try_rc = (res_expr); \
|
|
|
|
if (_tmp_r_try_rc.IsError()) { \
|
|
|
|
return _tmp_r_try_rc; \
|
|
|
|
} \
|
|
|
|
}
|