/* This file is part of KDBindings. SPDX-FileCopyrightText: 2021-2022 Klarälvdalens Datakonsult AB, a KDAB Group company Author: Leon Matthes SPDX-License-Identifier: MIT Contact KDAB at for commercial licensing options. */ #pragma once #include #include #include namespace KDBindings { /** * The contents of this namespace may only be accessed by the implementation of KDBindings, they * are not part of KDBindings public API and may be altered at any time and provide no guarantees * of any kind when used directly. **/ namespace Private { // ------------------------ get_arity -------------------------- // get_arity is a template function that returns the number of arguments // of (almost) any callable object. // The easiest way is to simply call get_arity() for callable type T. // It needs to be constexpr in order so it can be used in template arguments. // To overload get_arity, it needs a marker type, as C++ doesn't allow partial // function specialization. template struct TypeMarker { constexpr TypeMarker() = default; }; // base implementation of get_arity refers to specialized implementations for each // type of callable object by using the overload for its specialized TypeMarker. template constexpr size_t get_arity() { return get_arity(TypeMarker>{}); } // Syntactic sugar version of get_arity, allows to pass any callable object // to get_arity, instead of having to pass its decltype as a template argument. template constexpr size_t get_arity(const T &) { return get_arity(); } // The arity of a function pointer is simply its number of arguments. template constexpr size_t get_arity(TypeMarker) { return sizeof...(Arguments); } template constexpr size_t get_arity(TypeMarker) { return sizeof...(Arguments); } // The arity of a generic callable object is the arity of its operator() - 1, as the this // pointer is already known for such an object. template constexpr size_t get_arity(TypeMarker) { return get_arity(TypeMarker{}) - 1; } // Macro to help define most combinations of possible member function qualifiers. // Add + 1 to sizeof...(Arguments) here as the "this" pointer is an implicit argument to any member function. #define KDBINDINGS_DEFINE_MEMBER_GET_ARITY(MODIFIERS) \ template \ constexpr size_t get_arity(::KDBindings::Private::TypeMarker) \ { \ return sizeof...(Arguments) + 1; \ } // Define the get_arity version without modifiers without using the macro. // MSVC otherwise complains about a call to the macro with too few arguments template constexpr size_t get_arity(::KDBindings::Private::TypeMarker) { return sizeof...(Arguments) + 1; } KDBINDINGS_DEFINE_MEMBER_GET_ARITY(const) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(&) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(const &) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(&&) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(const &&) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(volatile) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(volatile const) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(volatile &) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(volatile const &) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(volatile &&) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(volatile const &&) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(noexcept) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(const noexcept) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(&noexcept) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(const &noexcept) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(&&noexcept) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(const &&noexcept) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(volatile noexcept) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(volatile const noexcept) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(volatile &noexcept) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(volatile const &noexcept) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(volatile &&noexcept) KDBINDINGS_DEFINE_MEMBER_GET_ARITY(volatile const &&noexcept) // -------------------- placeholder and bind_first --------------------- // Inspired by https://gist.github.com/engelmarkus/fc1678adbed1b630584c90219f77eb48 // A placeholder provides a way to construct something equivalent to a std::placeholders::_N // with N as a template argument. // // Note: As placeholders start at 1, therefore placeholder<0> is NOT a valid placeholder. template struct placeholder { }; template auto bind_first_helper(std::index_sequence, Func &&fun, Args... args) { return std::bind(std::forward(fun), std::forward(args)..., placeholder{}...); } // bind_first binds the first arguments to the callable object (i.e. function) to the values provided by args. // The return value is a new function taking get_arity - sizeof...(Args) many arguments, with the first // sizeof...(Args) arguments bound to the values of args. // This is different to a call with std::bind(fun, args...), as the callable object created by std::bind would // in this case now take zero arguments, whilst bind_first still expects the remaining arguments to be provided // // For now, providing instances of std::placeholders in Args is not allowed, as the implications of this are // unclear if sizeof...(Args) != get_arity. The enable_if_t makes sure none of the Args value is a placeholder. // // In the future, we could provide another overload of this function that allows placeholders, as long as all arguments // are bound. template< typename Func, typename... Args, /*Disallow any placeholder arguments, they would mess with the number and ordering of required and bound arguments, and are, for now, unsupported*/ typename = std::enable_if_t>...>>> auto bind_first(Func &&fun, Args &&...args) { return bind_first_helper(std::make_index_sequence() - sizeof...(Args)>{}, std::forward(fun), std::forward(args)...); } } // namespace Private } // namespace KDBindings namespace std { // This allows a placeholder to be used as a replacement of a std::placeholders. template struct is_placeholder> : integral_constant { }; } // namespace std