diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 131816012..2a8997e12 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -208,7 +208,11 @@ namespace { void logOut() { if (auto mtproto = Messenger::Instance().mtp()) { - mtproto->logout(rpcDone(&loggedOut), rpcFail(&loggedOut)); + mtproto->logout(rpcDone([] { + return loggedOut(); + }), rpcFail([] { + return loggedOut(); + })); } else { // We log out because we've forgotten passcode. // So we just start mtproto from scratch. diff --git a/Telegram/SourceFiles/base/lambda.h b/Telegram/SourceFiles/base/lambda.h index 6046bff58..266a1cdf5 100644 --- a/Telegram/SourceFiles/base/lambda.h +++ b/Telegram/SourceFiles/base/lambda.h @@ -22,6 +22,36 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include +#ifndef CUSTOM_LAMBDA_WRAP + +#include +#include "base/unique_function.h" + +namespace base { + +template +using lambda = std::function; + +template +using lambda_once = unique_function; + +namespace lambda_internal { + +template +struct lambda_call_type { + using type = decltype(&Lambda::operator()); +}; + +} // namespace lambda_internal + +template +using lambda_call_type_t + = typename lambda_internal::lambda_call_type::type; + +} // namespace base + +#else // CUSTOM_LAMBDA_WRAP + #ifndef Assert #define LambdaAssertDefined #define Assert(v) ((v) ? ((void)0) : std::abort()) @@ -67,9 +97,6 @@ struct type_helper { template using lambda_type = typename lambda_internal::type_helper>::type; -template -constexpr bool lambda_is_mutable = lambda_internal::type_helper>::is_mutable; - namespace lambda_internal { constexpr auto kFullStorageSize = 32U; @@ -469,3 +496,5 @@ public: #ifdef LambdaUnexpectedDefined #undef Unexpected #endif // LambdaUnexpectedDefined + +#endif // CUSTOM_LAMBDA_WRAP diff --git a/Telegram/SourceFiles/base/lambda_guard.h b/Telegram/SourceFiles/base/lambda_guard.h index c68825d3c..735dfe486 100644 --- a/Telegram/SourceFiles/base/lambda_guard.h +++ b/Telegram/SourceFiles/base/lambda_guard.h @@ -31,8 +31,6 @@ namespace lambda_internal { template class guard_data { public: - using return_type = typename lambda_type::return_type; - template inline guard_data(PointersAndLambda&&... qobjectsAndLambda) : _lambda(init(_pointers, std::forward(qobjectsAndLambda)...)) { } @@ -43,8 +41,10 @@ public: } } - template - inline return_type operator()(Args&&... args) { + template < + typename ...Args, + typename return_type = decltype(std::declval()(std::declval()...))> + inline auto operator()(Args&&... args) { for (int i = 0; i != N; ++i) { if (!_pointers[i]) { return return_type(); @@ -53,8 +53,10 @@ public: return _lambda(std::forward(args)...); } - template - inline return_type operator()(Args&&... args) const { + template < + typename ...Args, + typename return_type = decltype(std::declval()(std::declval()...))> + inline auto operator()(Args&&... args) const { for (int i = 0; i != N; ++i) { if (!_pointers[i]) { return return_type(); @@ -74,15 +76,18 @@ private: } QPointer _pointers[N]; - Lambda _lambda; + mutable Lambda _lambda; }; +template +struct lambda_call_type> { + using type = lambda_call_type_t; +}; + template class guard { public: - using return_type = typename lambda_type::return_type; - template inline guard(Pointer &&qobject, Other &&other, PointersAndLambda&&... qobjectsAndLambda) : _data(std::make_unique>(std::forward(qobject), std::forward(other), std::forward(qobjectsAndLambda)...)) { static_assert(1 + 1 + sizeof...(PointersAndLambda) == N + 1, "Wrong argument count!"); @@ -104,13 +109,17 @@ public: return *this; } - template - inline return_type operator()(Args&&... args) { + template < + typename ...Args, + typename = decltype(std::declval()(std::declval()...))> + inline decltype(auto) operator()(Args&&... args) { return (*_data)(std::forward(args)...); } - template - inline return_type operator()(Args&&... args) const { + template < + typename ...Args, + typename = decltype(std::declval()(std::declval()...))> + inline decltype(auto) operator()(Args&&... args) const { return (*_data)(std::forward(args)...); } @@ -123,6 +132,11 @@ private: }; +template +struct lambda_call_type> { + using type = lambda_call_type_t; +}; + template struct guard_type; @@ -133,7 +147,7 @@ struct guard_type { template struct guard_type { - using type = guard; + using type = guard>; }; template @@ -145,12 +159,6 @@ struct guard_type_helper { template using guard_t = typename guard_type_helper::type; -template -struct type_helper> { - using type = typename type_helper::type; - static constexpr auto is_mutable = type_helper::is_mutable; -}; - } // namespace lambda_internal template diff --git a/Telegram/SourceFiles/base/unique_function.h b/Telegram/SourceFiles/base/unique_function.h new file mode 100644 index 000000000..1bcfe171f --- /dev/null +++ b/Telegram/SourceFiles/base/unique_function.h @@ -0,0 +1,190 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#ifndef Unexpected +#define Unexpected(message) std::abort() +#define UniqueFunctionUnexpected +#endif // Unexpected + +namespace base { +namespace details { + +template +class moveable_callable_wrap { +public: + static_assert( + std::is_move_constructible_v, + "Should be at least moveable."); + + moveable_callable_wrap(Callable &&other) + : _value(std::move(other)) { + } + moveable_callable_wrap &operator=(Callable &&other) { + _value = std::move(other); + return *this; + } + moveable_callable_wrap(moveable_callable_wrap &&other) + : _value(std::move(other._value)) { + } + moveable_callable_wrap( + const moveable_callable_wrap &other) + : _value(fail_construct()) { + } + moveable_callable_wrap &operator=( + moveable_callable_wrap &&other) { + _value = std::move(other._value); + return *this; + } + moveable_callable_wrap &operator=( + const moveable_callable_wrap &other) { + return fail_assign(); + } + + template + decltype(auto) operator()(Args &&...args) const { + return _value(std::forward(args)...); + } + +private: + [[noreturn]] Callable fail_construct() { + Unexpected("Attempt to copy-construct a move-only type."); + } + [[noreturn]] moveable_callable_wrap &fail_assign() { + Unexpected("Attempt to copy-assign a move-only type."); + } + + mutable Callable _value; + +}; + +} // namespace details + +template +class unique_function; + +template +class unique_function final { +public: + unique_function(std::nullptr_t = nullptr) noexcept { + } + unique_function(const unique_function &other) = delete; + unique_function &operator=(const unique_function &other) = delete; + + // Move construct / assign from the same type. + unique_function(unique_function &&other) + : _impl(std::move(other._impl)) { + } + unique_function &operator=(unique_function &&other) { + _impl = std::move(other._impl); + return *this; + } + + template < + typename Callable, + typename = std::enable_if_t< + std::is_convertible_v< + decltype(std::declval()( + std::declval()...)), + Return>>> + unique_function(Callable &&other) + : unique_function( + std::forward(other), + std::is_copy_constructible>{}) { + } + + template < + typename Callable, + typename = std::enable_if_t< + std::is_convertible_v< + decltype(std::declval()( + std::declval()...)), + Return>>> + unique_function &operator=(Callable &&other) { + using Decayed = std::decay_t; + if constexpr (std::is_copy_constructible_v) { + _impl = std::forward(other); + } else if constexpr (std::is_move_constructible_v) { + _impl = details::moveable_callable_wrap( + std::forward(other)); + } else { + static_assert(false_t(other), "Should be moveable."); + } + return *this; + } + + void swap(unique_function &other) { + _impl.swap(other._impl); + } + + template + Return operator()(OtherArgs &&...args) { + return _impl(std::forward(args)...); + } + + explicit operator bool() const { + return _impl.operator bool(); + } + + friend inline bool operator==( + const unique_function &value, + std::nullptr_t) noexcept { + return value._impl == nullptr; + } + friend inline bool operator==( + std::nullptr_t, + const unique_function &value) noexcept { + return value._impl == nullptr; + } + friend inline bool operator!=( + const unique_function &value, + std::nullptr_t) noexcept { + return value._impl != nullptr; + } + friend inline bool operator!=( + std::nullptr_t, + const unique_function &value) noexcept { + return value._impl != nullptr; + } + +private: + template + unique_function(Callable &&other, std::true_type) + : _impl(std::forward(other)) { + } + + template + unique_function(Callable &&other, std::false_type) + : _impl(details::moveable_callable_wrap>( + std::forward(other))) { + } + + std::function _impl; + +}; + +} // namespace base + +#ifdef UniqueFunctionUnexpected +#undef UniqueFunctionUnexpected +#undef Unexpected +#endif // UniqueFunctionUnexpectedb + diff --git a/Telegram/SourceFiles/mtproto/rpc_sender.h b/Telegram/SourceFiles/mtproto/rpc_sender.h index aa914baa1..a6e7a6ff2 100644 --- a/Telegram/SourceFiles/mtproto/rpc_sender.h +++ b/Telegram/SourceFiles/mtproto/rpc_sender.h @@ -20,6 +20,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #pragma once +#include + class RPCError { public: RPCError(const MTPrpcError &error) : _code(error.c_rpc_error().verror_code.v) { @@ -921,7 +923,9 @@ class RPCDoneHandlerImplementationNo : public RPCDoneHandlerImplementation public: using RPCDoneHandlerImplementation::Parent::Parent; void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override { - return this->_handler ? this->_handler() : void(0); + if (this->_handler) { + this->_handler(); + } } }; @@ -931,44 +935,101 @@ class RPCDoneHandlerImplementationNoReq : public RPCDoneHandlerImplementation::Parent::Parent; void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override { - return this->_handler ? this->_handler(requestId) : void(0); + if (this->_handler) { + this->_handler(requestId); + } } }; -template -inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_once lambda) { - return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationBare(std::move(lambda))); -} - -template -inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_once lambda) { - return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationBareReq(std::move(lambda))); -} - -template -inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_once lambda) { - return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationPlain(std::move(lambda))); -} - -template -inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_once lambda) { - return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationReq(std::move(lambda))); -} - -template -inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_once lambda) { - return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationNo(std::move(lambda))); -} - -template -inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_once lambda) { - return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationNoReq(std::move(lambda))); -} +template +constexpr bool rpcDone_canCallBare_v = rpl::details::is_callable_plain_v< + Lambda, const mtpPrime*, const mtpPrime*>; template +constexpr bool rpcDone_canCallBareReq_v = rpl::details::is_callable_plain_v< + Lambda, const mtpPrime*, const mtpPrime*, mtpRequestId>; + +template +constexpr bool rpcDone_canCallNo_v = rpl::details::is_callable_plain_v< + Lambda>; + +template +constexpr bool rpcDone_canCallNoReq_v = rpl::details::is_callable_plain_v< + Lambda, mtpRequestId>; + +template +struct rpcDone_canCallPlain : std::false_type { +}; + +template +struct rpcDone_canCallPlain : std::true_type { + using Arg = T; +}; + +template +struct rpcDone_canCallPlain + : rpcDone_canCallPlain { +}; + +template +constexpr bool rpcDone_canCallPlain_v = rpcDone_canCallPlain::value; + +template +struct rpcDone_canCallReq : std::false_type { +}; + +template +struct rpcDone_canCallReq : std::true_type { + using Arg = T; +}; + +template +struct rpcDone_canCallReq + : rpcDone_canCallReq { +}; + +template +constexpr bool rpcDone_canCallReq_v = rpcDone_canCallReq::value; + +template +struct rpcDone_returnType; + +template +struct rpcDone_returnType { + using type = Return; +}; + +template +struct rpcDone_returnType { + using type = Return; +}; + +template +using rpcDone_returnType_t = typename rpcDone_returnType::type; + +template < + typename Lambda, + typename Function = base::lambda_call_type_t> RPCDoneHandlerPtr rpcDone(Lambda lambda) { - return rpcDone_lambda_wrap_helper(base::lambda_type(std::move(lambda))); + using R = rpcDone_returnType_t; + if constexpr (rpcDone_canCallBare_v) { + return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationBare(std::move(lambda))); + } else if constexpr (rpcDone_canCallBareReq_v) { + return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationBareReq(std::move(lambda))); + } else if constexpr (rpcDone_canCallNo_v) { + return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationNo(std::move(lambda))); + } else if constexpr (rpcDone_canCallNoReq_v) { + return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationNoReq(std::move(lambda))); + } else if constexpr (rpcDone_canCallPlain_v) { + using T = typename rpcDone_canCallPlain::Arg; + return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationPlain(std::move(lambda))); + } else if constexpr (rpcDone_canCallReq_v) { + using T = typename rpcDone_canCallReq::Arg; + return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationReq(std::move(lambda))); + } else { + static_assert(false_t(lambda), "Unknown method."); + } } template @@ -1010,23 +1071,35 @@ public: }; -inline RPCFailHandlerPtr rpcFail_lambda_wrap_helper(base::lambda_once lambda) { - return RPCFailHandlerPtr(new RPCFailHandlerImplementationPlain(std::move(lambda))); -} - -inline RPCFailHandlerPtr rpcFail_lambda_wrap_helper(base::lambda_once lambda) { - return RPCFailHandlerPtr(new RPCFailHandlerImplementationReq(std::move(lambda))); -} - -inline RPCFailHandlerPtr rpcFail_lambda_wrap_helper(base::lambda_once lambda) { - return RPCFailHandlerPtr(new RPCFailHandlerImplementationNo(std::move(lambda))); -} - -inline RPCFailHandlerPtr rpcFail_lambda_wrap_helper(base::lambda_once lambda) { - return RPCFailHandlerPtr(new RPCFailHandlerImplementationNoReq(std::move(lambda))); -} +template +constexpr bool rpcFail_canCallNo_v = rpl::details::is_callable_plain_v< + Lambda>; template +constexpr bool rpcFail_canCallNoReq_v = rpl::details::is_callable_plain_v< + Lambda, mtpRequestId>; + +template +constexpr bool rpcFail_canCallPlain_v = rpl::details::is_callable_plain_v< + Lambda, const RPCError&>; + +template +constexpr bool rpcFail_canCallReq_v = rpl::details::is_callable_plain_v< + Lambda, const RPCError&, mtpRequestId>; + +template < + typename Lambda, + typename Function = base::lambda_call_type_t> RPCFailHandlerPtr rpcFail(Lambda lambda) { - return rpcFail_lambda_wrap_helper(base::lambda_type(std::move(lambda))); + if constexpr (rpcFail_canCallNo_v) { + return RPCFailHandlerPtr(new RPCFailHandlerImplementationNo(std::move(lambda))); + } else if constexpr (rpcFail_canCallNoReq_v) { + return RPCFailHandlerPtr(new RPCFailHandlerImplementationNoReq(std::move(lambda))); + } else if constexpr (rpcFail_canCallPlain_v) { + return RPCFailHandlerPtr(new RPCFailHandlerImplementationPlain(std::move(lambda))); + } else if constexpr (rpcFail_canCallReq_v) { + return RPCFailHandlerPtr(new RPCFailHandlerImplementationReq(std::move(lambda))); + } else { + static_assert(false_t(lambda), "Unknown method."); + } } diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index f43144ab3..8e0eb7de1 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -27,6 +27,7 @@ <(src_loc)/base/timer.h <(src_loc)/base/type_traits.h <(src_loc)/base/unique_any.h +<(src_loc)/base/unique_function.h <(src_loc)/base/variant.h <(src_loc)/base/virtual_method.h <(src_loc)/base/weak_unique_ptr.h