Bug 1826564 [wpt PR 39394] - Update mypy, a=testonly
[gecko.git] / mfbt / ResultExtensions.h
blob97f197d800c009c311e650c39d245170426435d7
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"
16 #include "mozilla/dom/quota/RemoveParen.h"
18 namespace mozilla {
20 struct ErrorPropagationTag;
22 // Allow nsresult errors to automatically convert to nsresult values, so MOZ_TRY
23 // can be used in XPCOM methods with Result<T, nserror> results.
24 template <>
25 class [[nodiscard]] GenericErrorResult<nsresult> {
26 nsresult mErrorValue;
28 template <typename V, typename E2>
29 friend class Result;
31 public:
32 explicit GenericErrorResult(nsresult aErrorValue) : mErrorValue(aErrorValue) {
33 MOZ_ASSERT(NS_FAILED(aErrorValue));
36 GenericErrorResult(nsresult aErrorValue, const ErrorPropagationTag&)
37 : GenericErrorResult(aErrorValue) {}
39 operator nsresult() const { return mErrorValue; }
42 // Allow MOZ_TRY to handle `PRStatus` values.
43 template <typename E = nsresult>
44 inline Result<Ok, E> ToResult(PRStatus aValue);
46 } // namespace mozilla
48 #include "mozilla/Result.h"
50 namespace mozilla {
52 template <typename ResultType>
53 struct ResultTypeTraits;
55 template <>
56 struct ResultTypeTraits<nsresult> {
57 static nsresult From(nsresult aValue) { return aValue; }
60 template <typename E>
61 inline Result<Ok, E> ToResult(nsresult aValue) {
62 if (NS_FAILED(aValue)) {
63 return Err(ResultTypeTraits<E>::From(aValue));
65 return Ok();
68 template <typename E>
69 inline Result<Ok, E> ToResult(PRStatus aValue) {
70 if (aValue == PR_SUCCESS) {
71 return Ok();
73 return Err(ResultTypeTraits<E>::From(NS_ERROR_FAILURE));
76 namespace detail {
77 template <typename R>
78 auto ResultRefAsParam(R& aResult) {
79 return &aResult;
82 template <typename R, typename E, typename RArgMapper, typename Func,
83 typename... Args>
84 Result<R, E> ToResultInvokeInternal(const Func& aFunc,
85 const RArgMapper& aRArgMapper,
86 Args&&... aArgs) {
87 // XXX Thereotically, if R is a pointer to a non-refcounted type, this might
88 // be a non-owning pointer, but unless we find a case where this actually is
89 // relevant, it's safe to forbid any raw pointer result.
90 static_assert(
91 !std::is_pointer_v<R>,
92 "Raw pointer results are not supported, please specify a smart pointer "
93 "result type explicitly, so that getter_AddRefs is used");
95 R res;
96 nsresult rv = aFunc(std::forward<Args>(aArgs)..., aRArgMapper(res));
97 if (NS_FAILED(rv)) {
98 return Err(ResultTypeTraits<E>::From(rv));
100 return res;
103 template <typename T>
104 struct outparam_as_pointer;
106 template <typename T>
107 struct outparam_as_pointer<T*> {
108 using type = T*;
111 template <typename T>
112 struct outparam_as_reference;
114 template <typename T>
115 struct outparam_as_reference<T*> {
116 using type = T&;
119 template <typename R, typename E, template <typename> typename RArg,
120 typename Func, typename... Args>
121 using to_result_retval_t =
122 decltype(std::declval<Func&>()(
123 std::declval<Args&&>()...,
124 std::declval<typename RArg<decltype(ResultRefAsParam(
125 std::declval<R&>()))>::type>()),
126 Result<R, E>(Err(ResultTypeTraits<E>::From(NS_ERROR_FAILURE))));
128 // There are two ToResultInvokeSelector overloads, which cover the cases of a) a
129 // pointer-typed output parameter, and b) a reference-typed output parameter,
130 // using to_result_retval_t in connection with outparam_as_pointer and
131 // outparam_as_reference type traits. These type traits may be specialized for
132 // types other than raw pointers to allow calling functions with argument types
133 // that implicitly convert/bind to a raw pointer/reference. The overload that is
134 // used is selected by expression SFINAE: the decltype expression in
135 // to_result_retval_t is only valid in either case.
136 template <typename R, typename E, typename Func, typename... Args>
137 auto ToResultInvokeSelector(const Func& aFunc, Args&&... aArgs)
138 -> to_result_retval_t<R, E, outparam_as_pointer, Func, Args...> {
139 return ToResultInvokeInternal<R, E>(
140 aFunc, [](R& res) -> decltype(auto) { return ResultRefAsParam(res); },
141 std::forward<Args>(aArgs)...);
144 template <typename R, typename E, typename Func, typename... Args>
145 auto ToResultInvokeSelector(const Func& aFunc, Args&&... aArgs)
146 -> to_result_retval_t<R, E, outparam_as_reference, Func, Args...> {
147 return ToResultInvokeInternal<R, E>(
148 aFunc, [](R& res) -> decltype(auto) { return *ResultRefAsParam(res); },
149 std::forward<Args>(aArgs)...);
152 } // namespace detail
155 * Adapts a function with a nsresult error type and an R* output parameter as
156 * the last parameter to a function returning a mozilla::Result<R, nsresult>
157 * object.
159 * This can also be used with member functions together with std::men_fn, e.g.
161 * nsCOMPtr<nsIFile> file = ...;
162 * auto existsOrErr = ToResultInvoke<bool>(std::mem_fn(&nsIFile::Exists),
163 * *file);
165 * but it is more convenient to use the member function version, which has the
166 * additional benefit of enabling the deduction of the success result type:
168 * nsCOMPtr<nsIFile> file = ...;
169 * auto existsOrErr = ToResultInvokeMember(*file, &nsIFile::Exists);
171 template <typename R, typename E = nsresult, typename Func, typename... Args>
172 Result<R, E> ToResultInvoke(const Func& aFunc, Args&&... aArgs) {
173 return detail::ToResultInvokeSelector<R, E, Func, Args&&...>(
174 aFunc, std::forward<Args>(aArgs)...);
177 namespace detail {
178 template <typename T>
179 struct tag {
180 using type = T;
183 template <typename... Ts>
184 struct select_last {
185 using type = typename decltype((tag<Ts>{}, ...))::type;
188 template <typename... Ts>
189 using select_last_t = typename select_last<Ts...>::type;
191 template <>
192 struct select_last<> {
193 using type = void;
196 template <typename E, typename RArg, typename T, typename Func,
197 typename... Args>
198 auto ToResultInvokeMemberInternal(T& aObj, const Func& aFunc, Args&&... aArgs) {
199 if constexpr (std::is_pointer_v<RArg> ||
200 (std::is_lvalue_reference_v<RArg> &&
201 !std::is_const_v<std::remove_reference_t<RArg>>)) {
202 auto lambda = [&](RArg res) {
203 return (aObj.*aFunc)(std::forward<Args>(aArgs)..., res);
205 return detail::ToResultInvokeSelector<
206 std::remove_reference_t<std::remove_pointer_t<RArg>>, E,
207 decltype(lambda)>(lambda);
208 } else {
209 // No output parameter present, return a Result<Ok, E>
210 return mozilla::ToResult<E>((aObj.*aFunc)(std::forward<Args>(aArgs)...));
214 // For use in MOZ_TO_RESULT_INVOKE_MEMBER/MOZ_TO_RESULT_INVOKE_MEMBER_TYPED.
215 template <typename T>
216 auto DerefHelper(const T&) -> T&;
218 template <typename T>
219 auto DerefHelper(T*) -> T&;
221 template <template <class> class SmartPtr, typename T,
222 typename = decltype(*std::declval<const SmartPtr<T>>())>
223 auto DerefHelper(const SmartPtr<T>&) -> T&;
225 template <typename T>
226 using DerefedType =
227 std::remove_reference_t<decltype(DerefHelper(std::declval<const T&>()))>;
228 } // namespace detail
230 template <typename E = nsresult, typename T, typename U, typename... XArgs,
231 typename... Args,
232 typename = std::enable_if_t<std::is_base_of_v<U, T>>>
233 auto ToResultInvokeMember(T& aObj, nsresult (U::*aFunc)(XArgs...),
234 Args&&... aArgs) {
235 return detail::ToResultInvokeMemberInternal<E,
236 detail::select_last_t<XArgs...>>(
237 aObj, aFunc, std::forward<Args>(aArgs)...);
240 template <typename E = nsresult, typename T, typename U, typename... XArgs,
241 typename... Args,
242 typename = std::enable_if_t<std::is_base_of_v<U, T>>>
243 auto ToResultInvokeMember(const T& aObj, nsresult (U::*aFunc)(XArgs...) const,
244 Args&&... aArgs) {
245 return detail::ToResultInvokeMemberInternal<E,
246 detail::select_last_t<XArgs...>>(
247 aObj, aFunc, std::forward<Args>(aArgs)...);
250 template <typename E = nsresult, typename T, typename U, typename... XArgs,
251 typename... Args>
252 auto ToResultInvokeMember(T* const aObj, nsresult (U::*aFunc)(XArgs...),
253 Args&&... aArgs) {
254 return ToResultInvokeMember<E>(*aObj, aFunc, std::forward<Args>(aArgs)...);
257 template <typename E = nsresult, typename T, typename U, typename... XArgs,
258 typename... Args>
259 auto ToResultInvokeMember(const T* const aObj,
260 nsresult (U::*aFunc)(XArgs...) const,
261 Args&&... aArgs) {
262 return ToResultInvokeMember<E>(*aObj, aFunc, std::forward<Args>(aArgs)...);
265 template <typename E = nsresult, template <class> class SmartPtr, typename T,
266 typename U, typename... XArgs, typename... Args,
267 typename = std::enable_if_t<std::is_base_of_v<U, T>>,
268 typename = decltype(*std::declval<const SmartPtr<T>>())>
269 auto ToResultInvokeMember(const SmartPtr<T>& aObj,
270 nsresult (U::*aFunc)(XArgs...), Args&&... aArgs) {
271 return ToResultInvokeMember<E>(*aObj, aFunc, std::forward<Args>(aArgs)...);
274 template <typename E = nsresult, template <class> class SmartPtr, typename T,
275 typename U, typename... XArgs, typename... Args,
276 typename = std::enable_if_t<std::is_base_of_v<U, T>>,
277 typename = decltype(*std::declval<const SmartPtr<T>>())>
278 auto ToResultInvokeMember(const SmartPtr<const T>& aObj,
279 nsresult (U::*aFunc)(XArgs...) const,
280 Args&&... aArgs) {
281 return ToResultInvokeMember<E>(*aObj, aFunc, std::forward<Args>(aArgs)...);
284 #if defined(XP_WIN) && !defined(_WIN64)
285 template <typename E = nsresult, typename T, typename U, typename... XArgs,
286 typename... Args,
287 typename = std::enable_if_t<std::is_base_of_v<U, T>>>
288 auto ToResultInvokeMember(T& aObj, nsresult (__stdcall U::*aFunc)(XArgs...),
289 Args&&... aArgs) {
290 return detail::ToResultInvokeMemberInternal<E,
291 detail::select_last_t<XArgs...>>(
292 aObj, aFunc, std::forward<Args>(aArgs)...);
295 template <typename E = nsresult, typename T, typename U, typename... XArgs,
296 typename... Args,
297 typename = std::enable_if_t<std::is_base_of_v<U, T>>>
298 auto ToResultInvokeMember(const T& aObj,
299 nsresult (__stdcall U::*aFunc)(XArgs...) const,
300 Args&&... aArgs) {
301 return detail::ToResultInvokeMemberInternal<E,
302 detail::select_last_t<XArgs...>>(
303 aObj, aFunc, std::forward<Args>(aArgs)...);
306 template <typename E = nsresult, typename T, typename U, typename... XArgs,
307 typename... Args>
308 auto ToResultInvokeMember(T* const aObj,
309 nsresult (__stdcall U::*aFunc)(XArgs...),
310 Args&&... aArgs) {
311 return ToResultInvokeMember<E>(*aObj, aFunc, std::forward<Args>(aArgs)...);
314 template <typename E = nsresult, typename T, typename U, typename... XArgs,
315 typename... Args>
316 auto ToResultInvokeMember(const T* const aObj,
317 nsresult (__stdcall U::*aFunc)(XArgs...) const,
318 Args&&... aArgs) {
319 return ToResultInvokeMember<E>(*aObj, aFunc, std::forward<Args>(aArgs)...);
322 template <typename E = nsresult, template <class> class SmartPtr, typename T,
323 typename U, typename... XArgs, typename... Args,
324 typename = std::enable_if_t<std::is_base_of_v<U, T>>,
325 typename = decltype(*std::declval<const SmartPtr<T>>())>
326 auto ToResultInvokeMember(const SmartPtr<T>& aObj,
327 nsresult (__stdcall U::*aFunc)(XArgs...),
328 Args&&... aArgs) {
329 return ToResultInvokeMember<E>(*aObj, aFunc, std::forward<Args>(aArgs)...);
332 template <typename E = nsresult, template <class> class SmartPtr, typename T,
333 typename U, typename... XArgs, typename... Args,
334 typename = std::enable_if_t<std::is_base_of_v<U, T>>,
335 typename = decltype(*std::declval<const SmartPtr<T>>())>
336 auto ToResultInvokeMember(const SmartPtr<const T>& aObj,
337 nsresult (__stdcall U::*aFunc)(XArgs...) const,
338 Args&&... aArgs) {
339 return ToResultInvokeMember<E>(*aObj, aFunc, std::forward<Args>(aArgs)...);
341 #endif
343 // Macro version of ToResultInvokeMember for member functions. The macro has
344 // the advantage of not requiring spelling out the member function's declarator
345 // type name, at the expense of having a non-standard syntax. It can be used
346 // like this:
348 // nsCOMPtr<nsIFile> file;
349 // auto existsOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(file, Exists);
350 #define MOZ_TO_RESULT_INVOKE_MEMBER(obj, methodname, ...) \
351 ::mozilla::ToResultInvokeMember( \
352 (obj), &::mozilla::detail::DerefedType<decltype(obj)>::methodname, \
353 ##__VA_ARGS__)
355 // Macro version of ToResultInvokeMember for member functions, where the result
356 // type does not match the output parameter type. The macro has the advantage
357 // of not requiring spelling out the member function's declarator type name, at
358 // the expense of having a non-standard syntax. It can be used like this:
360 // nsCOMPtr<nsIFile> file;
361 // auto existsOrErr =
362 // MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCOMPtr<nsIFile>, file, Clone);
363 #define MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(resultType, obj, methodname, ...) \
364 ::mozilla::ToResultInvoke<MOZ_REMOVE_PAREN(resultType)>( \
365 ::std::mem_fn( \
366 &::mozilla::detail::DerefedType<decltype(obj)>::methodname), \
367 (obj), ##__VA_ARGS__)
369 } // namespace mozilla
371 #endif // mozilla_ResultExtensions_h