Bug 1700051: part 36) Reduce accessibility of `SoftText::mBegin` to `private`. r...
[gecko.git] / mfbt / ResultExtensions.h
blob7e15e652564eb5826c5e356d1c7f0666f85267b6
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 // Allow nsresult errors to automatically convert to nsresult values, so MOZ_TRY
20 // can be used in XPCOM methods with Result<T, nserror> results.
21 template <>
22 class MOZ_MUST_USE_TYPE GenericErrorResult<nsresult> {
23 nsresult mErrorValue;
25 template <typename V, typename E2>
26 friend class Result;
28 public:
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"
43 namespace mozilla {
45 inline Result<Ok, nsresult> ToResult(nsresult aValue) {
46 if (NS_FAILED(aValue)) {
47 return Err(aValue);
49 return Ok();
52 inline Result<Ok, nsresult> ToResult(PRStatus aValue) {
53 if (aValue == PR_SUCCESS) {
54 return Ok();
56 return Err(NS_ERROR_FAILURE);
59 namespace detail {
60 template <typename R>
61 auto ResultRefAsParam(R& aResult) {
62 return &aResult;
65 template <typename R, typename RArgMapper, typename Func, typename... Args>
66 Result<R, nsresult> ToResultInvokeInternal(const Func& aFunc,
67 const RArgMapper& aRArgMapper,
68 Args&&... aArgs) {
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.
72 static_assert(
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");
77 R res;
78 nsresult rv = aFunc(std::forward<Args>(aArgs)..., aRArgMapper(res));
79 if (NS_FAILED(rv)) {
80 return Err(rv);
82 return res;
85 template <typename T>
86 struct outparam_as_pointer;
88 template <typename T>
89 struct outparam_as_pointer<T*> {
90 using type = T*;
93 template <typename T>
94 struct outparam_as_reference;
96 template <typename T>
97 struct outparam_as_reference<T*> {
98 using type = T&;
101 template <typename R, template <typename> typename RArg, typename Func,
102 typename... Args>
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>
138 * object.
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),
144 * *file);
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
148 * type:
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)...);
159 namespace detail {
160 template <typename T>
161 struct tag {
162 using type = T;
165 template <typename... Ts>
166 struct select_last {
167 using type = typename decltype((tag<Ts>{}, ...))::type;
170 template <typename... Ts>
171 using select_last_t = typename select_last<Ts...>::type;
173 template <>
174 struct select_last<> {
175 using type = void;
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)>(
188 lambda);
189 } else {
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>
207 using DerefedType =
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,
221 Args&&... aArgs) {
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...),
228 Args&&... aArgs) {
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,
234 Args&&... aArgs) {
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...),
243 Args&&... aArgs) {
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...),
260 Args&&... aArgs) {
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,
269 Args&&... aArgs) {
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...),
276 Args&&... aArgs) {
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,
283 Args&&... aArgs) {
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,
302 Args&&... aArgs) {
303 return ToResultInvoke(*aObj, aFunc, std::forward<Args>(aArgs)...);
305 #endif
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
310 // this:
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, \
317 ##__VA_ARGS__)
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>( \
328 ::std::mem_fn( \
329 &::mozilla::detail::DerefedType<decltype(obj)>::methodname), \
330 (obj), ##__VA_ARGS__)
332 } // namespace mozilla
334 #endif // mozilla_ResultExtensions_h