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
10 #ifndef mozilla_ResultExtensions_h
11 #define mozilla_ResultExtensions_h
13 #include "mozilla/Assertions.h"
16 #include "mozilla/dom/quota/RemoveParen.h"
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.
25 class [[nodiscard
]] GenericErrorResult
<nsresult
> {
28 template <typename V
, typename E2
>
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"
52 template <typename ResultType
>
53 struct ResultTypeTraits
;
56 struct ResultTypeTraits
<nsresult
> {
57 static nsresult
From(nsresult aValue
) { return aValue
; }
61 inline Result
<Ok
, E
> ToResult(nsresult aValue
) {
62 if (NS_FAILED(aValue
)) {
63 return Err(ResultTypeTraits
<E
>::From(aValue
));
69 inline Result
<Ok
, E
> ToResult(PRStatus aValue
) {
70 if (aValue
== PR_SUCCESS
) {
73 return Err(ResultTypeTraits
<E
>::From(NS_ERROR_FAILURE
));
78 auto ResultRefAsParam(R
& aResult
) {
82 template <typename R
, typename E
, typename RArgMapper
, typename Func
,
84 Result
<R
, E
> ToResultInvokeInternal(const Func
& aFunc
,
85 const RArgMapper
& aRArgMapper
,
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.
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");
96 nsresult rv
= aFunc(std::forward
<Args
>(aArgs
)..., aRArgMapper(res
));
98 return Err(ResultTypeTraits
<E
>::From(rv
));
103 template <typename T
>
104 struct outparam_as_pointer
;
106 template <typename T
>
107 struct outparam_as_pointer
<T
*> {
111 template <typename T
>
112 struct outparam_as_reference
;
114 template <typename T
>
115 struct outparam_as_reference
<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>
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),
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
)...);
178 template <typename T
>
183 template <typename
... Ts
>
185 using type
= typename
decltype((tag
<Ts
>{}, ...))::type
;
188 template <typename
... Ts
>
189 using select_last_t
= typename select_last
<Ts
...>::type
;
192 struct select_last
<> {
196 template <typename E
, typename RArg
, typename T
, typename Func
,
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
);
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
>
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
,
232 typename
= std::enable_if_t
<std::is_base_of_v
<U
, T
>>>
233 auto ToResultInvokeMember(T
& aObj
, nsresult (U::*aFunc
)(XArgs
...),
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
,
242 typename
= std::enable_if_t
<std::is_base_of_v
<U
, T
>>>
243 auto ToResultInvokeMember(const T
& aObj
, nsresult (U::*aFunc
)(XArgs
...) const,
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
,
252 auto ToResultInvokeMember(T
* const aObj
, nsresult (U::*aFunc
)(XArgs
...),
254 return ToResultInvokeMember
<E
>(*aObj
, aFunc
, std::forward
<Args
>(aArgs
)...);
257 template <typename E
= nsresult
, typename T
, typename U
, typename
... XArgs
,
259 auto ToResultInvokeMember(const T
* const aObj
,
260 nsresult (U::*aFunc
)(XArgs
...) const,
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,
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
,
287 typename
= std::enable_if_t
<std::is_base_of_v
<U
, T
>>>
288 auto ToResultInvokeMember(T
& aObj
, nsresult (__stdcall
U::*aFunc
)(XArgs
...),
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
,
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,
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
,
308 auto ToResultInvokeMember(T
* const aObj
,
309 nsresult (__stdcall
U::*aFunc
)(XArgs
...),
311 return ToResultInvokeMember
<E
>(*aObj
, aFunc
, std::forward
<Args
>(aArgs
)...);
314 template <typename E
= nsresult
, typename T
, typename U
, typename
... XArgs
,
316 auto ToResultInvokeMember(const T
* const aObj
,
317 nsresult (__stdcall
U::*aFunc
)(XArgs
...) const,
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
...),
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,
339 return ToResultInvokeMember
<E
>(*aObj
, aFunc
, std::forward
<Args
>(aArgs
)...);
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
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, \
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)>( \
366 &::mozilla::detail::DerefedType<decltype(obj)>::methodname), \
367 (obj), ##__VA_ARGS__)
369 } // namespace mozilla
371 #endif // mozilla_ResultExtensions_h