ResultExtensions.h (14242B)
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/. */ 6 7 /* Extensions to the Result type to enable simpler handling of XPCOM/NSPR 8 * results. */ 9 10 #ifndef mozilla_ResultExtensions_h 11 #define mozilla_ResultExtensions_h 12 13 #include "mozilla/Assertions.h" 14 #include "nscore.h" 15 #include "prtypes.h" 16 #include "mozilla/dom/quota/RemoveParen.h" 17 18 namespace mozilla { 19 20 struct ErrorPropagationTag; 21 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; 27 28 template <typename V, typename E2> 29 friend class Result; 30 31 public: 32 explicit GenericErrorResult(nsresult aErrorValue) : mErrorValue(aErrorValue) { 33 MOZ_ASSERT(NS_FAILED(aErrorValue)); 34 } 35 36 GenericErrorResult(nsresult aErrorValue, const ErrorPropagationTag&) 37 : GenericErrorResult(aErrorValue) {} 38 39 operator nsresult() const { return mErrorValue; } 40 }; 41 42 // Allow MOZ_TRY to handle `PRStatus` values. 43 template <typename E = nsresult> 44 inline Result<Ok, E> ToResult(PRStatus aValue); 45 46 } // namespace mozilla 47 48 #include "mozilla/Result.h" 49 50 namespace mozilla { 51 52 template <typename ResultType> 53 struct ResultTypeTraits; 54 55 template <> 56 struct ResultTypeTraits<nsresult> { 57 static nsresult From(nsresult aValue) { return aValue; } 58 }; 59 60 template <typename E> 61 inline Result<Ok, E> ToResult(nsresult aValue) { 62 if (NS_FAILED(aValue)) { 63 return Err(ResultTypeTraits<E>::From(aValue)); 64 } 65 return Ok(); 66 } 67 68 template <typename E> 69 inline Result<Ok, E> ToResult(PRStatus aValue) { 70 if (aValue == PR_SUCCESS) { 71 return Ok(); 72 } 73 return Err(ResultTypeTraits<E>::From(NS_ERROR_FAILURE)); 74 } 75 76 namespace detail { 77 template <typename R> 78 auto ResultRefAsParam(R& aResult) { 79 return &aResult; 80 } 81 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"); 94 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)); 99 } 100 return res; 101 } 102 103 template <typename T> 104 struct outparam_as_pointer; 105 106 template <typename T> 107 struct outparam_as_pointer<T*> { 108 using type = T*; 109 }; 110 111 template <typename T> 112 struct outparam_as_reference; 113 114 template <typename T> 115 struct outparam_as_reference<T*> { 116 using type = T&; 117 }; 118 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)))); 127 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)...); 142 } 143 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)...); 150 } 151 152 } // namespace detail 153 154 /** 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. 158 * 159 * This can also be used with member functions together with std::men_fn, e.g. 160 * 161 * nsCOMPtr<nsIFile> file = ...; 162 * auto existsOrErr = ToResultInvoke<bool>(std::mem_fn(&nsIFile::Exists), 163 * *file); 164 * 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: 167 * 168 * nsCOMPtr<nsIFile> file = ...; 169 * auto existsOrErr = ToResultInvokeMember(*file, &nsIFile::Exists); 170 */ 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)...); 175 } 176 177 namespace detail { 178 template <typename T> 179 struct tag { 180 using type = T; 181 }; 182 183 template <typename... Ts> 184 struct select_last { 185 using type = typename decltype((tag<Ts>{}, ...))::type; 186 }; 187 188 template <typename... Ts> 189 using select_last_t = typename select_last<Ts...>::type; 190 191 template <> 192 struct select_last<> { 193 using type = void; 194 }; 195 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); 204 }; 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)...)); 211 } 212 } 213 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&; 217 218 template <typename T> 219 auto DerefHelper(T*) -> T&; 220 221 template <template <class> class SmartPtr, typename T, 222 typename = decltype(*std::declval<const SmartPtr<T>>())> 223 auto DerefHelper(const SmartPtr<T>&) -> T&; 224 225 template <typename T> 226 using DerefedType = 227 std::remove_reference_t<decltype(DerefHelper(std::declval<const T&>()))>; 228 } // namespace detail 229 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)...); 238 } 239 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)...); 248 } 249 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)...); 255 } 256 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)...); 263 } 264 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)...); 272 } 273 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)...); 282 } 283 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)...); 293 } 294 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)...); 304 } 305 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)...); 312 } 313 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)...); 320 } 321 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)...); 330 } 331 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)...); 340 } 341 #endif 342 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: 347 // 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__) 354 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: 359 // 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__) 368 369 } // namespace mozilla 370 371 #endif // mozilla_ResultExtensions_h