/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* Extensions to the Result type to enable simpler handling of XPCOM/NSPR * results. */ #ifndef mozilla_ResultExtensions_h #define mozilla_ResultExtensions_h #include "mozilla/Assertions.h" #include "nscore.h" #include "prtypes.h" #include "mozilla/dom/quota/RemoveParen.h" namespace mozilla { struct ErrorPropagationTag; // Allow nsresult errors to automatically convert to nsresult values, so MOZ_TRY // can be used in XPCOM methods with Result results. template <> class [[nodiscard]] GenericErrorResult { nsresult mErrorValue; template friend class Result; public: explicit GenericErrorResult(nsresult aErrorValue) : mErrorValue(aErrorValue) { MOZ_ASSERT(NS_FAILED(aErrorValue)); } GenericErrorResult(nsresult aErrorValue, const ErrorPropagationTag&) : GenericErrorResult(aErrorValue) {} operator nsresult() const { return mErrorValue; } }; // Allow MOZ_TRY to handle `PRStatus` values. template inline Result ToResult(PRStatus aValue); } // namespace mozilla #include "mozilla/Result.h" namespace mozilla { template struct ResultTypeTraits; template <> struct ResultTypeTraits { static nsresult From(nsresult aValue) { return aValue; } }; template inline Result ToResult(nsresult aValue) { if (NS_FAILED(aValue)) { return Err(ResultTypeTraits::From(aValue)); } return Ok(); } template inline Result ToResult(PRStatus aValue) { if (aValue == PR_SUCCESS) { return Ok(); } return Err(ResultTypeTraits::From(NS_ERROR_FAILURE)); } namespace detail { template auto ResultRefAsParam(R& aResult) { return &aResult; } template Result ToResultInvokeInternal(const Func& aFunc, const RArgMapper& aRArgMapper, Args&&... aArgs) { // XXX Thereotically, if R is a pointer to a non-refcounted type, this might // be a non-owning pointer, but unless we find a case where this actually is // relevant, it's safe to forbid any raw pointer result. static_assert( !std::is_pointer_v, "Raw pointer results are not supported, please specify a smart pointer " "result type explicitly, so that getter_AddRefs is used"); R res; nsresult rv = aFunc(std::forward(aArgs)..., aRArgMapper(res)); if (NS_FAILED(rv)) { return Err(ResultTypeTraits::From(rv)); } return res; } template struct outparam_as_pointer; template struct outparam_as_pointer { using type = T*; }; template struct outparam_as_reference; template struct outparam_as_reference { using type = T&; }; template typename RArg, typename Func, typename... Args> using to_result_retval_t = decltype(std::declval()( std::declval()..., std::declval()))>::type>()), Result(Err(ResultTypeTraits::From(NS_ERROR_FAILURE)))); // There are two ToResultInvokeSelector overloads, which cover the cases of a) a // pointer-typed output parameter, and b) a reference-typed output parameter, // using to_result_retval_t in connection with outparam_as_pointer and // outparam_as_reference type traits. These type traits may be specialized for // types other than raw pointers to allow calling functions with argument types // that implicitly convert/bind to a raw pointer/reference. The overload that is // used is selected by expression SFINAE: the decltype expression in // to_result_retval_t is only valid in either case. template auto ToResultInvokeSelector(const Func& aFunc, Args&&... aArgs) -> to_result_retval_t { return ToResultInvokeInternal( aFunc, [](R& res) -> decltype(auto) { return ResultRefAsParam(res); }, std::forward(aArgs)...); } template auto ToResultInvokeSelector(const Func& aFunc, Args&&... aArgs) -> to_result_retval_t { return ToResultInvokeInternal( aFunc, [](R& res) -> decltype(auto) { return *ResultRefAsParam(res); }, std::forward(aArgs)...); } } // namespace detail /** * Adapts a function with a nsresult error type and an R* output parameter as * the last parameter to a function returning a mozilla::Result * object. * * This can also be used with member functions together with std::men_fn, e.g. * * nsCOMPtr file = ...; * auto existsOrErr = ToResultInvoke(std::mem_fn(&nsIFile::Exists), * *file); * * but it is more convenient to use the member function version, which has the * additional benefit of enabling the deduction of the success result type: * * nsCOMPtr file = ...; * auto existsOrErr = ToResultInvokeMember(*file, &nsIFile::Exists); */ template Result ToResultInvoke(const Func& aFunc, Args&&... aArgs) { return detail::ToResultInvokeSelector( aFunc, std::forward(aArgs)...); } namespace detail { template struct tag { using type = T; }; template struct select_last { using type = typename decltype((tag{}, ...))::type; }; template using select_last_t = typename select_last::type; template <> struct select_last<> { using type = void; }; template auto ToResultInvokeMemberInternal(T& aObj, const Func& aFunc, Args&&... aArgs) { if constexpr (std::is_pointer_v || (std::is_lvalue_reference_v && !std::is_const_v>)) { auto lambda = [&](RArg res) { return (aObj.*aFunc)(std::forward(aArgs)..., res); }; return detail::ToResultInvokeSelector< std::remove_reference_t>, E, decltype(lambda)>(lambda); } else { // No output parameter present, return a Result return mozilla::ToResult((aObj.*aFunc)(std::forward(aArgs)...)); } } // For use in MOZ_TO_RESULT_INVOKE_MEMBER/MOZ_TO_RESULT_INVOKE_MEMBER_TYPED. template auto DerefHelper(const T&) -> T&; template auto DerefHelper(T*) -> T&; template