1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef ANDROID_FRAMEWORKS_ML_NN_COMMON_NNAPI_RESULT_H
18 #define ANDROID_FRAMEWORKS_ML_NN_COMMON_NNAPI_RESULT_H
19
20 #include <android-base/expected.h>
21
22 #include <optional>
23 #include <sstream>
24 #include <string>
25 #include <tuple>
26 #include <utility>
27
28 namespace android::nn {
29
30 /**
31 * Type alias for `::android::base::expected` where the unexpected state is represented by a
32 * std::string describing the error.
33 *
34 * See the following file for more information on ::android::base::expected:
35 * system/libbase/include/android-base/expected.h
36 */
37 template <typename Type>
38 using Result = base::expected<Type, std::string>;
39
40 namespace detail {
41
42 template <typename... Ts>
43 class ErrorBuilder {
44 public:
45 template <typename... Us>
ErrorBuilder(Us &&...args)46 explicit ErrorBuilder(Us&&... args) : mArgs(std::forward<Us>(args)...) {}
47
48 template <typename T, typename E>
49 operator base::expected<T, E>() /* NOLINT(google-explicit-constructor) */ {
50 return std::apply(
51 [this](Ts&&... args) {
52 return base::unexpected<E>(E{std::move(mStream).str(), std::move(args)...});
53 },
54 std::move(mArgs));
55 }
56
57 template <typename T>
58 ErrorBuilder operator<<(const T& t) {
59 mStream << t;
60 return std::move(*this);
61 }
62
63 private:
64 std::tuple<Ts...> mArgs;
65 std::ostringstream mStream;
66 };
67
68 } // namespace detail
69
70 /**
71 * Creates an error builder for the case where no arguments are provided.
72 */
73 template <typename... Types>
error(Types &&...args)74 inline detail::ErrorBuilder<std::decay_t<Types>...> error(Types&&... args) {
75 return detail::ErrorBuilder<std::decay_t<Types>...>(std::forward<Types>(args)...);
76 }
77
78 /**
79 * Helper macro that will create an error builder already populated with the file name and line
80 * number.
81 *
82 * This macro uses the following customization points:
83 * * `::android::nn::error` is a set of functions that can be customized to return a specialized
84 * error builder object. Customization is based on the types of arguments passed and the number
85 * of arguments passed to `error`.
86 *
87 * Usage at error site:
88 * if (errorDetected) {
89 * return NN_ERROR() << "<error_message>";
90 * }
91 * return <regular_return_value>;
92 */
93 #define NN_ERROR(...) \
94 [&] { \
95 using ::android::nn::error; \
96 return error(__VA_ARGS__) << __FILE__ << ":" << __LINE__ << ": "; \
97 }()
98
99 template <typename T, typename E>
nnTryHasValue(const base::expected<T,E> & o)100 bool nnTryHasValue(const base::expected<T, E>& o) {
101 return o.has_value();
102 }
103
104 template <typename T, typename E>
nnTryGetValue(base::expected<T,E> o)105 T nnTryGetValue(base::expected<T, E> o) {
106 return std::move(o).value();
107 }
108
109 template <typename T, typename E>
nnTryGetError(base::expected<T,E> o)110 base::unexpected<E> nnTryGetError(base::expected<T, E> o) {
111 return base::unexpected(std::move(o).error());
112 }
113
114 template <typename T>
nnTryHasValue(const std::optional<T> & o)115 bool nnTryHasValue(const std::optional<T>& o) {
116 return o.has_value();
117 }
118
119 template <typename T>
nnTryGetValue(std::optional<T> o)120 T nnTryGetValue(std::optional<T> o) {
121 return std::move(o).value();
122 }
123
124 template <typename T>
nnTryGetError(std::optional<T>)125 std::nullopt_t nnTryGetError(std::optional<T> /*o*/) {
126 return std::nullopt;
127 }
128
129 /**
130 * A macro that will exit from the current function if `expr` is unexpected or return the expected
131 * value from the macro if `expr` is expected.
132 *
133 * This macro can currently be used on `::android::nn::Result`, `::android::base::expected`, or
134 * `std::optional` values. To enable this macro to be used with other values, implement the
135 * following functions for the type:
136 * * `::android::nn::nnTryHasValue` returns `true` if the `expr` holds a successful value, false if
137 * the `expr` value holds an error
138 * * `::android::nn::nnTryGetError` returns the successful value of `expr` or crashes
139 * * `::android::nn::nnTryGetValue` returns the error value of `expr` or crashes
140 *
141 * Usage at call site:
142 * const auto [a, b, c] = NN_TRY(failableFunction(args));
143 */
144 #define NN_TRY(expr) \
145 ({ \
146 using ::android::nn::nnTryHasValue; \
147 using ::android::nn::nnTryGetValue; \
148 using ::android::nn::nnTryGetError; \
149 auto nnTryTemporaryResult = expr; \
150 if (!nnTryHasValue(nnTryTemporaryResult)) { \
151 return nnTryGetError(std::move(nnTryTemporaryResult)); \
152 } \
153 nnTryGetValue(std::move(nnTryTemporaryResult)); \
154 })
155
156 } // namespace android::nn
157
158 #endif // ANDROID_FRAMEWORKS_ML_NN_COMMON_NNAPI_RESULT_H
159