/* 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 "base/build_config.h" #include namespace rpl { namespace details { template const Arg &const_ref_val() noexcept; template Arg &lvalue_ref_val() noexcept; using false_t = char; struct true_t { false_t data[2]; }; static_assert(sizeof(false_t) != sizeof(true_t), "I can't work :("); template < typename Method, typename ...Args, typename = decltype(std::declval()( std::declval()...))> true_t test_callable_plain(Method &&, Args &&...) noexcept; false_t test_callable_plain(...) noexcept; template struct is_callable_plain : std::bool_constant<( sizeof(test_callable_plain( std::declval(), std::declval()... )) == sizeof(true_t))> { }; template constexpr bool is_callable_plain_v = is_callable_plain::value; template < typename Method, typename ...Types, typename = decltype(std::declval()( std::declval()...))> true_t test_callable_tuple( Method &&, std::tuple &&) noexcept; template < typename Method, typename ...Types, typename = decltype(std::declval()( const_ref_val()...))> true_t test_callable_tuple( Method &&, const std::tuple &) noexcept; false_t test_callable_tuple(...) noexcept; template constexpr bool is_callable_tuple_v = (sizeof(test_callable_tuple( std::declval(), std::declval())) == sizeof(true_t)); template struct is_callable_tuple : std::bool_constant< is_callable_tuple_v> { }; template struct is_callable; template struct is_callable : std::bool_constant< is_callable_plain_v> { }; template struct is_callable : std::bool_constant< is_callable_plain_v || is_callable_tuple_v || is_callable_plain_v> { }; template constexpr bool is_callable_v = is_callable::value; template inline decltype(auto) callable_invoke(Method &&method, Arg &&arg) { if constexpr (is_callable_plain_v) { return std::forward(method)(std::forward(arg)); } else if constexpr (is_callable_tuple_v) { return std::apply( std::forward(method), std::forward(arg)); } else if constexpr (is_callable_v) { return std::forward(method)(); } else { static_assert(false_(method, arg), "Bad callable_invoke() call."); } } template using callable_result = decltype(callable_invoke( std::declval(), std::declval())); template < typename Method, typename Arg, typename = decltype(std::declval()( const_ref_val>()))> true_t test_allows_const_ref(Method &&, Arg &&) noexcept; false_t test_allows_const_ref(...) noexcept; template constexpr bool allows_const_ref_v = (sizeof(test_allows_const_ref( std::declval(), std::declval())) == sizeof(true_t)); template inline decltype(auto) const_ref_call_invoke( Method &&method, const Arg &arg) { if constexpr (allows_const_ref_v) { return callable_invoke(std::forward(method), arg); } else { auto copy = arg; return callable_invoke( std::forward(method), std::move(copy)); } } } // namespace details } // namespace rpl