/* -*- 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" namespace mozilla { // Allow nsresult errors to automatically convert to nsresult values, so MOZ_TRY // can be used in XPCOM methods with Result results. template <> class MOZ_MUST_USE_TYPE GenericErrorResult { nsresult mErrorValue; template friend class Result; public: explicit GenericErrorResult(nsresult aErrorValue) : mErrorValue(aErrorValue) { MOZ_ASSERT(NS_FAILED(aErrorValue)); } operator nsresult() const { return mErrorValue; } }; // Allow MOZ_TRY to handle `PRStatus` values. inline Result ToResult(PRStatus aValue); } // namespace mozilla #include "mozilla/Result.h" namespace mozilla { inline Result ToResult(nsresult aValue) { if (NS_FAILED(aValue)) { return Err(aValue); } return Ok(); } inline Result ToResult(PRStatus aValue) { if (aValue == PR_SUCCESS) { return Ok(); } return Err(NS_ERROR_FAILURE); } namespace detail { template auto ResultRefAsParam(R& aResult) { return &aResult; } template Result ToResultInvokeInternal(const Func& aFunc, const RArgMapper& aRArgMapper, Args&&... aArgs) { R res; nsresult rv = aFunc(std::forward(aArgs)..., aRArgMapper(res)); if (NS_FAILED(rv)) { return Err(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(NS_OK)); // 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 overload, which * has the additional benefit of enabling the deduction of the success result * type: * * nsCOMPtr file = ...; * auto existsOrErr = ToResultInvoke(*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 ToResultInvokeMemberFunction(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>, decltype(lambda)>( lambda); } else { // No output parameter present, return a Result return mozilla::ToResult((aObj.*aFunc)(std::forward(aArgs)...)); } } } // namespace detail template auto ToResultInvoke(T& aObj, nsresult (U::*aFunc)(XArgs...), Args&&... aArgs) { return detail::ToResultInvokeMemberFunction>( aObj, aFunc, std::forward(aArgs)...); } template auto ToResultInvoke(const T& aObj, nsresult (U::*aFunc)(XArgs...) const, Args&&... aArgs) { return detail::ToResultInvokeMemberFunction>( aObj, aFunc, std::forward(aArgs)...); } template auto ToResultInvoke(T* const aObj, nsresult (U::*aFunc)(XArgs...), Args&&... aArgs) { return ToResultInvoke(*aObj, aFunc, std::forward(aArgs)...); } template auto ToResultInvoke(const T* const aObj, nsresult (U::*aFunc)(XArgs...) const, Args&&... aArgs) { return ToResultInvoke(*aObj, aFunc, std::forward(aArgs)...); } #if defined(XP_WIN) && !defined(_WIN64) template auto ToResultInvoke(T& aObj, nsresult (__stdcall U::*aFunc)(XArgs...), Args&&... aArgs) { return detail::ToResultInvokeMemberFunction>( aObj, aFunc, std::forward(aArgs)...); } template auto ToResultInvoke(const T& aObj, nsresult (__stdcall U::*aFunc)(XArgs...) const, Args&&... aArgs) { return detail::ToResultInvokeMemberFunction>( aObj, aFunc, std::forward(aArgs)...); } template auto ToResultInvoke(T* const aObj, nsresult (__stdcall U::*aFunc)(XArgs...), Args&&... aArgs) { return ToResultInvoke(*aObj, aFunc, std::forward(aArgs)...); } template auto ToResultInvoke(const T* const aObj, nsresult (__stdcall U::*aFunc)(XArgs...) const, Args&&... aArgs) { return ToResultInvoke(*aObj, aFunc, std::forward(aArgs)...); } #endif } // namespace mozilla #endif // mozilla_ResultExtensions_h