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"
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.
24 class MOZ_MUST_USE_TYPE GenericErrorResult
<nsresult
> {
27 template <typename V
, typename E2
>
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"
50 inline Result
<Ok
, nsresult
> ToResult(nsresult aValue
) {
51 if (NS_FAILED(aValue
)) {
57 inline Result
<Ok
, nsresult
> ToResult(PRStatus aValue
) {
58 if (aValue
== PR_SUCCESS
) {
61 return Err(NS_ERROR_FAILURE
);
66 auto ResultRefAsParam(R
& aResult
) {
70 template <typename R
, typename RArgMapper
, typename Func
, typename
... Args
>
71 Result
<R
, nsresult
> ToResultInvokeInternal(const Func
& aFunc
,
72 const RArgMapper
& aRArgMapper
,
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.
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");
83 nsresult rv
= aFunc(std::forward
<Args
>(aArgs
)..., aRArgMapper(res
));
91 struct outparam_as_pointer
;
94 struct outparam_as_pointer
<T
*> {
99 struct outparam_as_reference
;
101 template <typename T
>
102 struct outparam_as_reference
<T
*> {
106 template <typename R
, template <typename
> typename RArg
, typename Func
,
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>
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),
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
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
)...);
166 template <typename T
>
171 template <typename
... Ts
>
173 using type
= typename
decltype((tag
<Ts
>{}, ...))::type
;
176 template <typename
... Ts
>
177 using select_last_t
= typename select_last
<Ts
...>::type
;
180 struct select_last
<> {
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
)>(
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
>
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,
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
...),
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,
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
...),
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
...),
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,
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
...),
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,
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,
309 return ToResultInvoke(*aObj
, aFunc
, std::forward
<Args
>(aArgs
)...);
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
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, \
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>( \
335 &::mozilla::detail::DerefedType<decltype(obj)>::methodname), \
336 (obj), ##__VA_ARGS__)
338 } // namespace mozilla
340 #endif // mozilla_ResultExtensions_h