Bug 1719035 [wpt PR 29572] - Address flakiness in import-css-module-basic.html, a...
[gecko.git] / mfbt / ResultExtensions.h
blob89187232e21b3dc8ffb15f3a3766f50abdaf6a43
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* Extensions to the Result type to enable simpler handling of XPCOM/NSPR
8 * results. */
10 #ifndef mozilla_ResultExtensions_h
11 #define mozilla_ResultExtensions_h
13 #include "mozilla/Assertions.h"
14 #include "nscore.h"
15 #include "prtypes.h"
17 namespace mozilla {
19 struct ErrorPropagationTag;
21 // Allow nsresult errors to automatically convert to nsresult values, so MOZ_TRY
22 // can be used in XPCOM methods with Result<T, nserror> results.
23 template <>
24 class MOZ_MUST_USE_TYPE GenericErrorResult<nsresult> {
25 nsresult mErrorValue;
27 template <typename V, typename E2>
28 friend class Result;
30 public:
31 explicit GenericErrorResult(nsresult aErrorValue) : mErrorValue(aErrorValue) {
32 MOZ_ASSERT(NS_FAILED(aErrorValue));
35 GenericErrorResult(nsresult aErrorValue, const ErrorPropagationTag&)
36 : GenericErrorResult(aErrorValue) {}
38 operator nsresult() const { return mErrorValue; }
41 // Allow MOZ_TRY to handle `PRStatus` values.
42 inline Result<Ok, nsresult> ToResult(PRStatus aValue);
44 } // namespace mozilla
46 #include "mozilla/Result.h"
48 namespace mozilla {
50 inline Result<Ok, nsresult> ToResult(nsresult aValue) {
51 if (NS_FAILED(aValue)) {
52 return Err(aValue);
54 return Ok();
57 inline Result<Ok, nsresult> ToResult(PRStatus aValue) {
58 if (aValue == PR_SUCCESS) {
59 return Ok();
61 return Err(NS_ERROR_FAILURE);
64 namespace detail {
65 template <typename R>
66 auto ResultRefAsParam(R& aResult) {
67 return &aResult;
70 template <typename R, typename RArgMapper, typename Func, typename... Args>
71 Result<R, nsresult> ToResultInvokeInternal(const Func& aFunc,
72 const RArgMapper& aRArgMapper,
73 Args&&... aArgs) {
74 // XXX Thereotically, if R is a pointer to a non-refcounted type, this might
75 // be a non-owning pointer, but unless we find a case where this actually is
76 // relevant, it's safe to forbid any raw pointer result.
77 static_assert(
78 !std::is_pointer_v<R>,
79 "Raw pointer results are not supported, please specify a smart pointer "
80 "result type explicitly, so that getter_AddRefs is used");
82 R res;
83 nsresult rv = aFunc(std::forward<Args>(aArgs)..., aRArgMapper(res));
84 if (NS_FAILED(rv)) {
85 return Err(rv);
87 return res;
90 template <typename T>
91 struct outparam_as_pointer;
93 template <typename T>
94 struct outparam_as_pointer<T*> {
95 using type = T*;
98 template <typename T>
99 struct outparam_as_reference;
101 template <typename T>
102 struct outparam_as_reference<T*> {
103 using type = T&;
106 template <typename R, template <typename> typename RArg, typename Func,
107 typename... Args>
108 using to_result_retval_t =
109 decltype(std::declval<Func&>()(
110 std::declval<Args&&>()...,
111 std::declval<typename RArg<decltype(ResultRefAsParam(
112 std::declval<R&>()))>::type>()),
113 Result<R, nsresult>(Err(NS_ERROR_FAILURE)));
115 // There are two ToResultInvokeSelector overloads, which cover the cases of a) a
116 // pointer-typed output parameter, and b) a reference-typed output parameter,
117 // using to_result_retval_t in connection with outparam_as_pointer and
118 // outparam_as_reference type traits. These type traits may be specialized for
119 // types other than raw pointers to allow calling functions with argument types
120 // that implicitly convert/bind to a raw pointer/reference. The overload that is
121 // used is selected by expression SFINAE: the decltype expression in
122 // to_result_retval_t is only valid in either case.
123 template <typename R, typename Func, typename... Args>
124 auto ToResultInvokeSelector(const Func& aFunc, Args&&... aArgs)
125 -> to_result_retval_t<R, outparam_as_pointer, Func, Args...> {
126 return ToResultInvokeInternal<R>(
127 aFunc, [](R& res) -> decltype(auto) { return ResultRefAsParam(res); },
128 std::forward<Args>(aArgs)...);
131 template <typename R, typename Func, typename... Args>
132 auto ToResultInvokeSelector(const Func& aFunc, Args&&... aArgs)
133 -> to_result_retval_t<R, outparam_as_reference, Func, Args...> {
134 return ToResultInvokeInternal<R>(
135 aFunc, [](R& res) -> decltype(auto) { return *ResultRefAsParam(res); },
136 std::forward<Args>(aArgs)...);
139 } // namespace detail
142 * Adapts a function with a nsresult error type and an R* output parameter as
143 * the last parameter to a function returning a mozilla::Result<R, nsresult>
144 * object.
146 * This can also be used with member functions together with std::men_fn, e.g.
148 * nsCOMPtr<nsIFile> file = ...;
149 * auto existsOrErr = ToResultInvoke<bool>(std::mem_fn(&nsIFile::Exists),
150 * *file);
152 * but it is more convenient to use the member function overload, which
153 * has the additional benefit of enabling the deduction of the success result
154 * type:
156 * nsCOMPtr<nsIFile> file = ...;
157 * auto existsOrErr = ToResultInvoke(*file, &nsIFile::Exists);
159 template <typename R, typename Func, typename... Args>
160 Result<R, nsresult> ToResultInvoke(const Func& aFunc, Args&&... aArgs) {
161 return detail::ToResultInvokeSelector<R, Func, Args&&...>(
162 aFunc, std::forward<Args>(aArgs)...);
165 namespace detail {
166 template <typename T>
167 struct tag {
168 using type = T;
171 template <typename... Ts>
172 struct select_last {
173 using type = typename decltype((tag<Ts>{}, ...))::type;
176 template <typename... Ts>
177 using select_last_t = typename select_last<Ts...>::type;
179 template <>
180 struct select_last<> {
181 using type = void;
184 template <typename RArg, typename T, typename Func, typename... Args>
185 auto ToResultInvokeMemberFunction(T& aObj, const Func& aFunc, Args&&... aArgs) {
186 if constexpr (std::is_pointer_v<RArg> ||
187 (std::is_lvalue_reference_v<RArg> &&
188 !std::is_const_v<std::remove_reference_t<RArg>>)) {
189 auto lambda = [&](RArg res) {
190 return (aObj.*aFunc)(std::forward<Args>(aArgs)..., res);
192 return detail::ToResultInvokeSelector<
193 std::remove_reference_t<std::remove_pointer_t<RArg>>, decltype(lambda)>(
194 lambda);
195 } else {
196 // No output parameter present, return a Result<Ok, nsresult>
197 return mozilla::ToResult((aObj.*aFunc)(std::forward<Args>(aArgs)...));
201 // For use in MOZ_TO_RESULT_INVOKE.
202 template <typename T>
203 auto DerefHelper(const T&) -> T&;
205 template <typename T>
206 auto DerefHelper(T*) -> T&;
208 template <template <class> class SmartPtr, typename T,
209 typename = decltype(*std::declval<const SmartPtr<T>>())>
210 auto DerefHelper(const SmartPtr<T>&) -> T&;
212 template <typename T>
213 using DerefedType =
214 std::remove_reference_t<decltype(DerefHelper(std::declval<const T&>()))>;
215 } // namespace detail
217 template <typename T, typename U, typename... XArgs, typename... Args,
218 typename = std::enable_if_t<std::is_base_of_v<U, T>>>
219 auto ToResultInvoke(T& aObj, nsresult (U::*aFunc)(XArgs...), Args&&... aArgs) {
220 return detail::ToResultInvokeMemberFunction<detail::select_last_t<XArgs...>>(
221 aObj, aFunc, std::forward<Args>(aArgs)...);
224 template <typename T, typename U, typename... XArgs, typename... Args,
225 typename = std::enable_if_t<std::is_base_of_v<U, T>>>
226 auto ToResultInvoke(const T& aObj, nsresult (U::*aFunc)(XArgs...) const,
227 Args&&... aArgs) {
228 return detail::ToResultInvokeMemberFunction<detail::select_last_t<XArgs...>>(
229 aObj, aFunc, std::forward<Args>(aArgs)...);
232 template <typename T, typename U, typename... XArgs, typename... Args>
233 auto ToResultInvoke(T* const aObj, nsresult (U::*aFunc)(XArgs...),
234 Args&&... aArgs) {
235 return ToResultInvoke(*aObj, aFunc, std::forward<Args>(aArgs)...);
238 template <typename T, typename U, typename... XArgs, typename... Args>
239 auto ToResultInvoke(const T* const aObj, nsresult (U::*aFunc)(XArgs...) const,
240 Args&&... aArgs) {
241 return ToResultInvoke(*aObj, aFunc, std::forward<Args>(aArgs)...);
244 template <template <class> class SmartPtr, typename T, typename U,
245 typename... XArgs, typename... Args,
246 typename = std::enable_if_t<std::is_base_of_v<U, T>>,
247 typename = decltype(*std::declval<const SmartPtr<T>>())>
248 auto ToResultInvoke(const SmartPtr<T>& aObj, nsresult (U::*aFunc)(XArgs...),
249 Args&&... aArgs) {
250 return ToResultInvoke(*aObj, aFunc, std::forward<Args>(aArgs)...);
253 template <template <class> class SmartPtr, typename T, typename U,
254 typename... XArgs, typename... Args,
255 typename = std::enable_if_t<std::is_base_of_v<U, T>>,
256 typename = decltype(*std::declval<const SmartPtr<T>>())>
257 auto ToResultInvoke(const SmartPtr<const T>& aObj,
258 nsresult (U::*aFunc)(XArgs...) const, Args&&... aArgs) {
259 return ToResultInvoke(*aObj, aFunc, std::forward<Args>(aArgs)...);
262 #if defined(XP_WIN) && !defined(_WIN64)
263 template <typename T, typename U, typename... XArgs, typename... Args,
264 typename = std::enable_if_t<std::is_base_of_v<U, T>>>
265 auto ToResultInvoke(T& aObj, nsresult (__stdcall U::*aFunc)(XArgs...),
266 Args&&... aArgs) {
267 return detail::ToResultInvokeMemberFunction<detail::select_last_t<XArgs...>>(
268 aObj, aFunc, std::forward<Args>(aArgs)...);
271 template <typename T, typename U, typename... XArgs, typename... Args,
272 typename = std::enable_if_t<std::is_base_of_v<U, T>>>
273 auto ToResultInvoke(const T& aObj,
274 nsresult (__stdcall U::*aFunc)(XArgs...) const,
275 Args&&... aArgs) {
276 return detail::ToResultInvokeMemberFunction<detail::select_last_t<XArgs...>>(
277 aObj, aFunc, std::forward<Args>(aArgs)...);
280 template <typename T, typename U, typename... XArgs, typename... Args>
281 auto ToResultInvoke(T* const aObj, nsresult (__stdcall U::*aFunc)(XArgs...),
282 Args&&... aArgs) {
283 return ToResultInvoke(*aObj, aFunc, std::forward<Args>(aArgs)...);
286 template <typename T, typename U, typename... XArgs, typename... Args>
287 auto ToResultInvoke(const T* const aObj,
288 nsresult (__stdcall U::*aFunc)(XArgs...) const,
289 Args&&... aArgs) {
290 return ToResultInvoke(*aObj, aFunc, std::forward<Args>(aArgs)...);
293 template <template <class> class SmartPtr, typename T, typename U,
294 typename... XArgs, typename... Args,
295 typename = std::enable_if_t<std::is_base_of_v<U, T>>,
296 typename = decltype(*std::declval<const SmartPtr<T>>())>
297 auto ToResultInvoke(const SmartPtr<T>& aObj,
298 nsresult (__stdcall U::*aFunc)(XArgs...), Args&&... aArgs) {
299 return ToResultInvoke(*aObj, aFunc, std::forward<Args>(aArgs)...);
302 template <template <class> class SmartPtr, typename T, typename U,
303 typename... XArgs, typename... Args,
304 typename = std::enable_if_t<std::is_base_of_v<U, T>>,
305 typename = decltype(*std::declval<const SmartPtr<T>>())>
306 auto ToResultInvoke(const SmartPtr<const T>& aObj,
307 nsresult (__stdcall U::*aFunc)(XArgs...) const,
308 Args&&... aArgs) {
309 return ToResultInvoke(*aObj, aFunc, std::forward<Args>(aArgs)...);
311 #endif
313 // Macro version of ToResultInvoke for member functions. The macro has the
314 // advantage of not requiring spelling out the member function's declarator type
315 // name, at the expense of having a non-standard syntax. It can be used like
316 // this:
318 // nsCOMPtr<nsIFile> file;
319 // auto existsOrErr = MOZ_TO_RESULT_INVOKE(file, Exists);
320 #define MOZ_TO_RESULT_INVOKE(obj, methodname, ...) \
321 ::mozilla::ToResultInvoke( \
322 (obj), &::mozilla::detail::DerefedType<decltype(obj)>::methodname, \
323 ##__VA_ARGS__)
325 // Macro version of ToResultInvoke for member functions, where the result type
326 // does not match the output parameter type. The macro has the advantage of not
327 // requiring spelling out the member function's declarator type name, at the
328 // expense of having a non-standard syntax. It can be used like this:
330 // nsCOMPtr<nsIFile> file;
331 // auto existsOrErr = MOZ_TO_RESULT_INVOKE(nsCOMPtr<nsIFile>, file, Clone);
332 #define MOZ_TO_RESULT_INVOKE_TYPED(resultType, obj, methodname, ...) \
333 ::mozilla::ToResultInvoke<resultType>( \
334 ::std::mem_fn( \
335 &::mozilla::detail::DerefedType<decltype(obj)>::methodname), \
336 (obj), ##__VA_ARGS__)
338 } // namespace mozilla
340 #endif // mozilla_ResultExtensions_h