1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164 | /*
This file is part of KDBindings.
SPDX-FileCopyrightText: 2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Leon Matthes <leon.matthes@kdab.com>
SPDX-License-Identifier: MIT
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#pragma once
#include <functional>
#include <type_traits>
#include <utility>
namespace KDBindings {
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<T>() 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<typename T>
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<typename T>
constexpr size_t get_arity()
{
return get_arity(TypeMarker<std::decay_t<T>>{});
}
// 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<typename T>
constexpr size_t get_arity(const T &)
{
return get_arity<T>();
}
// The arity of a function pointer is simply its number of arguments.
template<typename Return, typename... Arguments>
constexpr size_t get_arity(TypeMarker<Return (*)(Arguments...)>)
{
return sizeof...(Arguments);
}
template<typename Return, typename... Arguments>
constexpr size_t get_arity(TypeMarker<Return (*)(Arguments...) noexcept>)
{
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<typename T>
constexpr size_t get_arity(TypeMarker<T>)
{
return get_arity(TypeMarker<decltype(&T::operator())>{}) - 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<typename Return, typename Class, typename... Arguments> \
constexpr size_t get_arity(::KDBindings::Private::TypeMarker<Return (Class::*)(Arguments...) MODIFIERS>) \
{ \
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<typename Return, typename Class, typename... Arguments>
constexpr size_t get_arity(::KDBindings::Private::TypeMarker<Return (Class::*)(Arguments...)>)
{
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<int>
struct placeholder {
};
template<typename Func, typename... Args, std::size_t... Is>
auto bind_first_helper(std::index_sequence<Is...>, Func &&fun, Args... args)
{
return std::bind(std::forward<Func>(fun), std::forward<Args>(args)..., placeholder<Is + 1>{}...);
}
// 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<Func> - 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<Func>. 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<std::conjunction_v<std::negation<std::is_placeholder<Args>>...>>>
auto bind_first(Func &&fun, Args &&...args)
{
return bind_first_helper(std::make_index_sequence<get_arity<Func>() - sizeof...(Args)>{}, std::forward<Func>(fun), std::forward<Args>(args)...);
}
} // namespace Private
} // namespace KDBindings
namespace std {
// This allows a placeholder to be used as a replacement of a std::placeholders.
template<int N>
struct is_placeholder<KDBindings::Private::placeholder<N>>
: integral_constant<int, N> {
};
} // namespace std
|