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 // Allow nsresult errors to automatically convert to nsresult values, so MOZ_TRY
20 // can be used in XPCOM methods with Result<T, nserror> results.
22 class MOZ_MUST_USE_TYPE GenericErrorResult
<nsresult
> {
25 template <typename V
, typename E2
>
29 explicit GenericErrorResult(nsresult aErrorValue
) : mErrorValue(aErrorValue
) {
30 MOZ_ASSERT(NS_FAILED(aErrorValue
));
33 operator nsresult() const { return mErrorValue
; }
36 // Allow MOZ_TRY to handle `PRStatus` values.
37 inline Result
<Ok
, nsresult
> ToResult(PRStatus aValue
);
39 } // namespace mozilla
41 #include "mozilla/Result.h"
45 inline Result
<Ok
, nsresult
> ToResult(nsresult aValue
) {
46 if (NS_FAILED(aValue
)) {
52 inline Result
<Ok
, nsresult
> ToResult(PRStatus aValue
) {
53 if (aValue
== PR_SUCCESS
) {
56 return Err(NS_ERROR_FAILURE
);
61 auto ResultRefAsParam(R
& aResult
) {
65 template <typename R
, typename RArgMapper
, typename Func
, typename
... Args
>
66 Result
<R
, nsresult
> ToResultInvokeInternal(const Func
& aFunc
,
67 const RArgMapper
& aRArgMapper
,
69 // XXX Thereotically, if R is a pointer to a non-refcounted type, this might
70 // be a non-owning pointer, but unless we find a case where this actually is
71 // relevant, it's safe to forbid any raw pointer result.
73 !std::is_pointer_v
<R
>,
74 "Raw pointer results are not supported, please specify a smart pointer "
75 "result type explicitly, so that getter_AddRefs is used");
78 nsresult rv
= aFunc(std::forward
<Args
>(aArgs
)..., aRArgMapper(res
));
86 struct outparam_as_pointer
;
89 struct outparam_as_pointer
<T
*> {
94 struct outparam_as_reference
;
97 struct outparam_as_reference
<T
*> {
101 template <typename R
, template <typename
> typename RArg
, typename Func
,
103 using to_result_retval_t
= decltype(
104 std::declval
<Func
&>()(std::declval
<Args
&&>()...,
105 std::declval
<typename RArg
<decltype(
106 ResultRefAsParam(std::declval
<R
&>()))>::type
>()),
107 Result
<R
, nsresult
>(Err(NS_ERROR_FAILURE
)));
109 // There are two ToResultInvokeSelector overloads, which cover the cases of a) a
110 // pointer-typed output parameter, and b) a reference-typed output parameter,
111 // using to_result_retval_t in connection with outparam_as_pointer and
112 // outparam_as_reference type traits. These type traits may be specialized for
113 // types other than raw pointers to allow calling functions with argument types
114 // that implicitly convert/bind to a raw pointer/reference. The overload that is
115 // used is selected by expression SFINAE: the decltype expression in
116 // to_result_retval_t is only valid in either case.
117 template <typename R
, typename Func
, typename
... Args
>
118 auto ToResultInvokeSelector(const Func
& aFunc
, Args
&&... aArgs
)
119 -> to_result_retval_t
<R
, outparam_as_pointer
, Func
, Args
...> {
120 return ToResultInvokeInternal
<R
>(
121 aFunc
, [](R
& res
) -> decltype(auto) { return ResultRefAsParam(res
); },
122 std::forward
<Args
>(aArgs
)...);
125 template <typename R
, typename Func
, typename
... Args
>
126 auto ToResultInvokeSelector(const Func
& aFunc
, Args
&&... aArgs
)
127 -> to_result_retval_t
<R
, outparam_as_reference
, Func
, Args
...> {
128 return ToResultInvokeInternal
<R
>(
129 aFunc
, [](R
& res
) -> decltype(auto) { return *ResultRefAsParam(res
); },
130 std::forward
<Args
>(aArgs
)...);
133 } // namespace detail
136 * Adapts a function with a nsresult error type and an R* output parameter as
137 * the last parameter to a function returning a mozilla::Result<R, nsresult>
140 * This can also be used with member functions together with std::men_fn, e.g.
142 * nsCOMPtr<nsIFile> file = ...;
143 * auto existsOrErr = ToResultInvoke<bool>(std::mem_fn(&nsIFile::Exists),
146 * but it is more convenient to use the member function overload, which
147 * has the additional benefit of enabling the deduction of the success result
150 * nsCOMPtr<nsIFile> file = ...;
151 * auto existsOrErr = ToResultInvoke(*file, &nsIFile::Exists);
153 template <typename R
, typename Func
, typename
... Args
>
154 Result
<R
, nsresult
> ToResultInvoke(const Func
& aFunc
, Args
&&... aArgs
) {
155 return detail::ToResultInvokeSelector
<R
, Func
, Args
&&...>(
156 aFunc
, std::forward
<Args
>(aArgs
)...);
160 template <typename T
>
165 template <typename
... Ts
>
167 using type
= typename
decltype((tag
<Ts
>{}, ...))::type
;
170 template <typename
... Ts
>
171 using select_last_t
= typename select_last
<Ts
...>::type
;
174 struct select_last
<> {
178 template <typename RArg
, typename T
, typename Func
, typename
... Args
>
179 auto ToResultInvokeMemberFunction(T
& aObj
, const Func
& aFunc
, Args
&&... aArgs
) {
180 if constexpr (std::is_pointer_v
<RArg
> ||
181 (std::is_lvalue_reference_v
<RArg
> &&
182 !std::is_const_v
<std::remove_reference_t
<RArg
>>)) {
183 auto lambda
= [&](RArg res
) {
184 return (aObj
.*aFunc
)(std::forward
<Args
>(aArgs
)..., res
);
186 return detail::ToResultInvokeSelector
<
187 std::remove_reference_t
<std::remove_pointer_t
<RArg
>>, decltype(lambda
)>(
190 // No output parameter present, return a Result<Ok, nsresult>
191 return mozilla::ToResult((aObj
.*aFunc
)(std::forward
<Args
>(aArgs
)...));
195 // For use in MOZ_TO_RESULT_INVOKE.
196 template <typename T
>
197 auto DerefHelper(const T
&) -> T
&;
199 template <typename T
>
200 auto DerefHelper(T
*) -> T
&;
202 template <template <class> class SmartPtr
, typename T
,
203 typename
= decltype(*std::declval
<const SmartPtr
<T
>>())>
204 auto DerefHelper(const SmartPtr
<T
>&) -> T
&;
206 template <typename T
>
208 std::remove_reference_t
<decltype(DerefHelper(std::declval
<const T
&>()))>;
209 } // namespace detail
211 template <typename T
, typename U
, typename
... XArgs
, typename
... Args
,
212 typename
= std::enable_if_t
<std::is_base_of_v
<U
, T
>>>
213 auto ToResultInvoke(T
& aObj
, nsresult (U::*aFunc
)(XArgs
...), Args
&&... aArgs
) {
214 return detail::ToResultInvokeMemberFunction
<detail::select_last_t
<XArgs
...>>(
215 aObj
, aFunc
, std::forward
<Args
>(aArgs
)...);
218 template <typename T
, typename U
, typename
... XArgs
, typename
... Args
,
219 typename
= std::enable_if_t
<std::is_base_of_v
<U
, T
>>>
220 auto ToResultInvoke(const T
& aObj
, nsresult (U::*aFunc
)(XArgs
...) const,
222 return detail::ToResultInvokeMemberFunction
<detail::select_last_t
<XArgs
...>>(
223 aObj
, aFunc
, std::forward
<Args
>(aArgs
)...);
226 template <typename T
, typename U
, typename
... XArgs
, typename
... Args
>
227 auto ToResultInvoke(T
* const aObj
, nsresult (U::*aFunc
)(XArgs
...),
229 return ToResultInvoke(*aObj
, aFunc
, std::forward
<Args
>(aArgs
)...);
232 template <typename T
, typename U
, typename
... XArgs
, typename
... Args
>
233 auto ToResultInvoke(const T
* const aObj
, nsresult (U::*aFunc
)(XArgs
...) const,
235 return ToResultInvoke(*aObj
, aFunc
, std::forward
<Args
>(aArgs
)...);
238 template <template <class> class SmartPtr
, typename T
, typename U
,
239 typename
... XArgs
, typename
... Args
,
240 typename
= std::enable_if_t
<std::is_base_of_v
<U
, T
>>,
241 typename
= decltype(*std::declval
<const SmartPtr
<T
>>())>
242 auto ToResultInvoke(const SmartPtr
<T
>& aObj
, nsresult (U::*aFunc
)(XArgs
...),
244 return ToResultInvoke(*aObj
, aFunc
, std::forward
<Args
>(aArgs
)...);
247 template <template <class> class SmartPtr
, typename T
, typename U
,
248 typename
... XArgs
, typename
... Args
,
249 typename
= std::enable_if_t
<std::is_base_of_v
<U
, T
>>,
250 typename
= decltype(*std::declval
<const SmartPtr
<T
>>())>
251 auto ToResultInvoke(const SmartPtr
<const T
>& aObj
,
252 nsresult (U::*aFunc
)(XArgs
...) const, Args
&&... aArgs
) {
253 return ToResultInvoke(*aObj
, aFunc
, std::forward
<Args
>(aArgs
)...);
256 #if defined(XP_WIN) && !defined(_WIN64)
257 template <typename T
, typename U
, typename
... XArgs
, typename
... Args
,
258 typename
= std::enable_if_t
<std::is_base_of_v
<U
, T
>>>
259 auto ToResultInvoke(T
& aObj
, nsresult (__stdcall
U::*aFunc
)(XArgs
...),
261 return detail::ToResultInvokeMemberFunction
<detail::select_last_t
<XArgs
...>>(
262 aObj
, aFunc
, std::forward
<Args
>(aArgs
)...);
265 template <typename T
, typename U
, typename
... XArgs
, typename
... Args
,
266 typename
= std::enable_if_t
<std::is_base_of_v
<U
, T
>>>
267 auto ToResultInvoke(const T
& aObj
,
268 nsresult (__stdcall
U::*aFunc
)(XArgs
...) const,
270 return detail::ToResultInvokeMemberFunction
<detail::select_last_t
<XArgs
...>>(
271 aObj
, aFunc
, std::forward
<Args
>(aArgs
)...);
274 template <typename T
, typename U
, typename
... XArgs
, typename
... Args
>
275 auto ToResultInvoke(T
* const aObj
, nsresult (__stdcall
U::*aFunc
)(XArgs
...),
277 return ToResultInvoke(*aObj
, aFunc
, std::forward
<Args
>(aArgs
)...);
280 template <typename T
, typename U
, typename
... XArgs
, typename
... Args
>
281 auto ToResultInvoke(const T
* const aObj
,
282 nsresult (__stdcall
U::*aFunc
)(XArgs
...) const,
284 return ToResultInvoke(*aObj
, aFunc
, std::forward
<Args
>(aArgs
)...);
287 template <template <class> class SmartPtr
, typename T
, typename U
,
288 typename
... XArgs
, typename
... Args
,
289 typename
= std::enable_if_t
<std::is_base_of_v
<U
, T
>>,
290 typename
= decltype(*std::declval
<const SmartPtr
<T
>>())>
291 auto ToResultInvoke(const SmartPtr
<T
>& aObj
,
292 nsresult (__stdcall
U::*aFunc
)(XArgs
...), Args
&&... aArgs
) {
293 return ToResultInvoke(*aObj
, aFunc
, std::forward
<Args
>(aArgs
)...);
296 template <template <class> class SmartPtr
, typename T
, typename U
,
297 typename
... XArgs
, typename
... Args
,
298 typename
= std::enable_if_t
<std::is_base_of_v
<U
, T
>>,
299 typename
= decltype(*std::declval
<const SmartPtr
<T
>>())>
300 auto ToResultInvoke(const SmartPtr
<const T
>& aObj
,
301 nsresult (__stdcall
U::*aFunc
)(XArgs
...) const,
303 return ToResultInvoke(*aObj
, aFunc
, std::forward
<Args
>(aArgs
)...);
307 // Macro version of ToResultInvoke for member functions. The macro has the
308 // advantage of not requiring spelling out the member function's declarator type
309 // name, at the expense of having a non-standard syntax. It can be used like
312 // nsCOMPtr<nsIFile> file;
313 // auto existsOrErr = MOZ_TO_RESULT_INVOKE(file, Exists);
314 #define MOZ_TO_RESULT_INVOKE(obj, methodname, ...) \
315 ::mozilla::ToResultInvoke( \
316 (obj), &::mozilla::detail::DerefedType<decltype(obj)>::methodname, \
319 // Macro version of ToResultInvoke for member functions, where the result type
320 // does not match the output parameter type. The macro has the advantage of not
321 // requiring spelling out the member function's declarator type name, at the
322 // expense of having a non-standard syntax. It can be used like this:
324 // nsCOMPtr<nsIFile> file;
325 // auto existsOrErr = MOZ_TO_RESULT_INVOKE(nsCOMPtr<nsIFile>, file, Clone);
326 #define MOZ_TO_RESULT_INVOKE_TYPED(resultType, obj, methodname, ...) \
327 ::mozilla::ToResultInvoke<resultType>( \
329 &::mozilla::detail::DerefedType<decltype(obj)>::methodname), \
330 (obj), ##__VA_ARGS__)
332 } // namespace mozilla
334 #endif // mozilla_ResultExtensions_h