/* 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 namespace base { // Guard lambda call by one or many QObject* weak pointers. namespace lambda_internal { template class guard_data { public: template inline guard_data(PointersAndLambda&&... qobjectsAndLambda) : _lambda(init(_pointers, std::forward(qobjectsAndLambda)...)) { } inline guard_data(const guard_data &other) : _lambda(other._lambda) { for (auto i = 0; i != N; ++i) { _pointers[i] = other._pointers[i]; } } 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(); } } return _lambda(std::forward(args)...); } 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(); } } return _lambda(std::forward(args)...); } private: template Lambda init(QPointer *pointers, QObject *qobject, PointersAndLambda&&... qobjectsAndLambda) { *pointers = qobject; return init(++pointers, std::forward(qobjectsAndLambda)...); } Lambda init(QPointer *pointers, Lambda &&lambda) { return std::move(lambda); } QPointer _pointers[N]; mutable Lambda _lambda; }; template struct lambda_call_type> { using type = lambda_call_type_t; }; template class guard { public: 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!"); } inline guard(const guard &other) : _data(std::make_unique>(static_cast &>(*other._data))) { } inline guard(guard &&other) : _data(std::move(other._data)) { } inline guard &operator=(const guard &&other) { _data = std::move(other._data); return *this; } inline guard &operator=(guard &&other) { _data = std::move(other._data); return *this; } template < typename ...Args, typename = decltype(std::declval()(std::declval()...))> inline decltype(auto) operator()(Args&&... args) { return (*_data)(std::forward(args)...); } template < typename ...Args, typename = decltype(std::declval()(std::declval()...))> inline decltype(auto) operator()(Args&&... args) const { return (*_data)(std::forward(args)...); } bool isNull() const { return !_data; } private: mutable std::unique_ptr> _data; }; template struct lambda_call_type> { using type = lambda_call_type_t; }; template struct guard_type; template struct guard_type { using type = typename guard_type::type; }; template struct guard_type { using type = guard>; }; template struct guard_type_helper { static constexpr int N = sizeof...(PointersAndLambda); using type = typename guard_type::type; }; template using guard_t = typename guard_type_helper::type; } // namespace lambda_internal template inline lambda_internal::guard_t lambda_guarded(PointersAndLambda&&... qobjectsAndLambda) { static_assert(sizeof...(PointersAndLambda) > 0, "Lambda should be passed here."); return lambda_internal::guard_t(std::forward(qobjectsAndLambda)...); } } // namespace base