tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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