From f488d5f9c106fb0b15ddfdd5abf014530f394663 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 27 Oct 2017 18:37:27 +0300 Subject: [PATCH] Use std::function for base::lambda implementation. (cherry picked from commit 101d4f6) --- Telegram/SourceFiles/app.cpp | 2 +- Telegram/SourceFiles/base/lambda.h | 28 +++- Telegram/SourceFiles/base/lambda_guard.h | 33 +++-- Telegram/SourceFiles/base/unique_function.h | 148 ++++++++++++++++++++ Telegram/SourceFiles/mtproto/rpc_sender.h | 128 +++++++++++------ 5 files changed, 279 insertions(+), 60 deletions(-) create mode 100644 Telegram/SourceFiles/base/unique_function.h diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 97a0d71d5..afe9eccce 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -207,7 +207,7 @@ bool loggedOut() { 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 016039352..0e4e90566 100644 --- a/Telegram/SourceFiles/base/lambda.h +++ b/Telegram/SourceFiles/base/lambda.h @@ -23,6 +23,29 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include // std::max_align_t #include +#ifndef CUSTOM_LAMBDA_WRAP + +#include "base/unique_function.h" +#include + +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()) @@ -63,9 +86,6 @@ template 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; @@ -416,3 +436,5 @@ template class lambda final #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 7b38d3d9a..59b01a040 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)...)) {} @@ -44,7 +42,8 @@ template class guard_data { } } - template inline return_type operator()(Args &&... args) { + template ()(std::declval()...))> + inline auto operator()(Args &&... args) { for (int i = 0; i != N; ++i) { if (!_pointers[i]) { return return_type(); @@ -53,7 +52,8 @@ template class guard_data { return _lambda(std::forward(args)...); } - template inline return_type operator()(Args &&... args) const { + template ()(std::declval()...))> + inline auto operator()(Args &&... args) const { for (int i = 0; i != N; ++i) { if (!_pointers[i]) { return return_type(); @@ -73,13 +73,15 @@ template class guard_data { } 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), @@ -103,11 +105,13 @@ template class guard { return *this; } - template inline return_type operator()(Args &&... args) { + template ()(std::declval()...))> + inline decltype(auto) operator()(Args &&... args) { return (*_data)(std::forward(args)...); } - template inline return_type operator()(Args &&... args) const { + template ()(std::declval()...))> + inline decltype(auto) operator()(Args &&... args) const { return (*_data)(std::forward(args)...); } @@ -119,6 +123,10 @@ template class guard { mutable std::unique_ptr> _data; }; +template struct lambda_call_type> { + using type = lambda_call_type_t; +}; + template struct guard_type; template @@ -126,7 +134,7 @@ struct guard_type { using type = typename guard_type::type; }; -template struct guard_type { using type = guard; }; +template struct guard_type { using type = guard>; }; template struct guard_type_helper { static constexpr int N = sizeof...(PointersAndLambda); @@ -135,11 +143,6 @@ template 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..d008a280a --- /dev/null +++ b/Telegram/SourceFiles/base/unique_function.h @@ -0,0 +1,148 @@ +/* +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 + +#include + +#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 ()(std::declval()...)), Return>>> + unique_function(Callable &&other) + : unique_function(std::forward(other), std::is_copy_constructible>{}) {} + + template ()(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 29ca6e3a6..5a73ca1bd 100644 --- a/Telegram/SourceFiles/mtproto/rpc_sender.h +++ b/Telegram/SourceFiles/mtproto/rpc_sender.h @@ -920,7 +920,9 @@ template class RPCDoneHandlerImplementationNo : public RPCDoneHandl 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(); + } } }; @@ -929,41 +931,81 @@ 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 +constexpr bool rpcDone_canCallBare_v = std::is_invocable_v; -template -inline RPCDoneHandlerPtr -rpcDone_lambda_wrap_helper(base::lambda_once lambda) { - return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationBareReq(std::move(lambda))); -} +template +constexpr bool rpcDone_canCallBareReq_v = std::is_invocable_v; -template -inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_once lambda) { - return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationPlain(std::move(lambda))); -} +template constexpr bool rpcDone_canCallNo_v = std::is_invocable_v; -template -inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_once lambda) { - return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationReq(std::move(lambda))); -} +template constexpr bool rpcDone_canCallNoReq_v = std::is_invocable_v; -template inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_once lambda) { - return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationNo(std::move(lambda))); -} +template struct rpcDone_canCallPlain : std::false_type {}; -template inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda_once lambda) { - return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationNoReq(std::move(lambda))); -} +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 RPCDoneHandlerPtr rpcDone(Lambda lambda) { - return rpcDone_lambda_wrap_helper(base::lambda_type(std::move(lambda))); +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 > +RPCDoneHandlerPtr rpcDone(Lambda 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(std::is_same::value, "Unknown method."); + } } template @@ -1002,22 +1044,26 @@ class RPCFailHandlerImplementationNoReq : public RPCFailHandlerImplementation lambda) { - return RPCFailHandlerPtr(new RPCFailHandlerImplementationPlain(std::move(lambda))); -} +template constexpr bool rpcFail_canCallNo_v = std::is_invocable_v; -inline RPCFailHandlerPtr rpcFail_lambda_wrap_helper(base::lambda_once lambda) { - return RPCFailHandlerPtr(new RPCFailHandlerImplementationReq(std::move(lambda))); -} +template constexpr bool rpcFail_canCallNoReq_v = std::is_invocable_v; -inline RPCFailHandlerPtr rpcFail_lambda_wrap_helper(base::lambda_once lambda) { - return RPCFailHandlerPtr(new RPCFailHandlerImplementationNo(std::move(lambda))); -} +template constexpr bool rpcFail_canCallPlain_v = std::is_invocable_v; -inline RPCFailHandlerPtr rpcFail_lambda_wrap_helper(base::lambda_once lambda) { - return RPCFailHandlerPtr(new RPCFailHandlerImplementationNoReq(std::move(lambda))); -} +template +constexpr bool rpcFail_canCallReq_v = std::is_invocable_v; -template RPCFailHandlerPtr rpcFail(Lambda lambda) { - return rpcFail_lambda_wrap_helper(base::lambda_type(std::move(lambda))); +template > +RPCFailHandlerPtr rpcFail(Lambda 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(std::is_same::value, "Unknown method."); + } }