-
Notifications
You must be signed in to change notification settings - Fork 4
/
function.h
291 lines (246 loc) · 9.14 KB
/
function.h
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
//This is <http://www.codeproject.com/Articles/136799/> Lightweight Generic C++ Callbacks (or, Yet Another Delegate Implementation)
//with a number of changes:
//- Callback was renamed to function, and the namespace was removed.
//- BIND_FREE_CB/BIND_MEM_CB were combined to a single bind(), by using the C99 preprocessor's __VA_ARGS__.
//- Instead of the thousand lines of copypasta, the implementations were merged by using some preprocessor macros and having the file include itself.
//- The Arity, ReturnType and ParamNType constants/typedefs were removed.
//- NullCallback was replaced with support for plain NULL, by implicitly casting the NULL to a pointer to a private class (default construction also exists).
//- BoundCallbackFactory and bind_ptr were added. It's useful for legacy C-like code, and some other cases.
//- Made it safe to call an unassigned object. (Unassigned objects are still false.)
//List of libraries that do roughly the same thing:
//http://www.codeproject.com/Articles/7150/ Member Function Pointers and the Fastest Possible C++ Delegates
// rejected because it uses ugly hacks which defeat the optimizer, unknown compilers, and my brain
//http://www.codeproject.com/Articles/11015/ The Impossibly Fast C++ Delegates
// rejected because creation syntax is ugly
//http://www.codeproject.com/Articles/13287/ Fast C++ Delegate
// rejected because it's huge and can allocate
//http://www.codeproject.com/Articles/18886/ A new way to implement Delegate in C++
// rejected because it depends on sizeof in creepy ways and can throw
//http://www.codeproject.com/Articles/136799/ Lightweight Generic C++ Callbacks (or, Yet Another Delegate Implementation)
// chosen because it gives the slimmest function objects - unlike the others, it's just two pointers
#ifndef UTIL_CALLBACK_HPP
#define UTIL_CALLBACK_HPP
#include <stddef.h>
#define UTIL_CALLBACK_HPP_INSIDE
#define bind_free(func) (GetFreeCallbackFactory(func).Bind<func>())
#define bind_ptr(func, ptr) (GetCallbackFactory(func, ptr).Bind<func>(ptr))
#define bind bind_free
#define bind_this(func) bind_ptr(func, this)
template<typename FuncSignature> class function;
#define JOIN2(a,b) a##b
#define JOIN(a,b) JOIN2(a,b)
#define FreeCallbackFactory JOIN(FreeCallbackFactory,COUNT)
#define MemberCallbackFactory JOIN(MemberCallbackFactory,COUNT)
#define ConstMemberCallbackFactory JOIN(ConstMemberCallbackFactory,COUNT)
#define BoundCallbackFactory JOIN(BoundCallbackFactory,COUNT)
#define ARG_TYPES_I(n) JOIN(P,n)
#define ARG_TYPES LOOP(ARG_TYPES_I)
#define ARG_NAMES_I(n) JOIN(a,n)
#define ARG_NAMES LOOP(ARG_NAMES_I)
#define ARG_TYPES_AND_NAMES_I(n) JOIN(P,n) JOIN(a,n)
#define ARG_TYPES_AND_NAMES LOOP(ARG_TYPES_AND_NAMES_I)
#define TYPENAMES_I(n) typename JOIN(P,n)
#define TYPENAMES LOOP(TYPENAMES_I)
#define TYPENAMES2_I(n) typename JOIN(FP,n)
#define TYPENAMES2 LOOP(TYPENAMES2_I)
#define COUNT 0
#define LOOP(macro) /* */
#define C /* */
#include "function.h"
#undef C
#define C ,
#define COUNT 1
#define LOOP(macro) macro(1)
#include "function.h"
#define COUNT 2
#define LOOP(macro) macro(1), macro(2)
#include "function.h"
#define COUNT 3
#define LOOP(macro) macro(1), macro(2), macro(3)
#include "function.h"
#define COUNT 4
#define LOOP(macro) macro(1), macro(2), macro(3), macro(4)
#include "function.h"
#define COUNT 5
#define LOOP(macro) macro(1), macro(2), macro(3), macro(4), macro(5)
#include "function.h"
#define COUNT 6
#define LOOP(macro) macro(1), macro(2), macro(3), macro(4), macro(5), macro(6)
#include "function.h"
#undef C
#undef JOIN2
#undef JOIN
#undef FreeCallbackFactory
#undef MemberCallbackFactory
#undef ConstMemberCallbackFactory
#undef BoundCallbackFactory
#undef ARG_TYPES_I
#undef ARG_TYPES
#undef ARG_NAMES_I
#undef ARG_NAMES
#undef ARG_TYPES_AND_NAMES_I
#undef ARG_TYPES_AND_NAMES
#undef TYPENAMES_I
#undef TYPENAMES
#undef TYPENAMES2_I
#undef TYPENAMES2
#undef UTIL_CALLBACK_HPP_INSIDE
#endif
#ifdef UTIL_CALLBACK_HPP_INSIDE
template<typename R C TYPENAMES>
class function<R (ARG_TYPES)>
{
private:
class null_only;
typedef R (*FuncType)(const void* C ARG_TYPES);
function(FuncType f, const void* o) : func(f), obj(o) {}
FuncType func;
const void* obj;
public:
//to make null objects callable, 'func' must be a valid function
//I can not:
//- use the lowest bits - requires mask at call time, and confuses the optimizer
//- compare it to a static null function, I don't trust the compiler to merge it correctly
//nor can I use NULL/whatever in 'obj', because foreign code can find that argument just as easily as this one can
//solution: set obj=func=EmptyFunction for null functions
//- EmptyFunction doesn't use obj, it can be whatever
//- it is not sensitive to false negatives - even if the address of EmptyFunction changes, obj==func does not
//- it is not sensitive to false positives - EmptyFunction is private and it is impossible for foreign code to know where it is, and luck can not hit it
//- it is sensitive to hostile callers, but if you call bind_ptr(func, (void*)func), you're asking for bugs.
function() : func(EmptyHandler), obj((void*)EmptyHandler) {}
function(const function& rhs) : func(rhs.func), obj(rhs.obj) {}
~function() {}
function(const null_only*) : func(EmptyHandler), obj((void*)EmptyHandler) {}
function& operator=(const function& rhs)
{ obj = rhs.obj; func = rhs.func; return *this; }
inline R operator()(ARG_TYPES_AND_NAMES) const
{
return (*func)(obj C ARG_NAMES);
}
private:
typedef const void* function::*SafeBoolType;
bool isTrue() const
{
return ((void*)func != obj);
}
public:
inline operator SafeBoolType() const
{ return isTrue() ? &function::obj : NULL; }
inline bool operator!() const
{ return !isTrue(); }
private:
static R EmptyHandler(const void* o C ARG_TYPES_AND_NAMES) { return R(); }
template<typename FR C TYPENAMES2>
friend class FreeCallbackFactory;
template<typename FR, class FT C TYPENAMES2>
friend class MemberCallbackFactory;
template<typename FR, class FT C TYPENAMES2>
friend class ConstMemberCallbackFactory;
template<typename FR C TYPENAMES2, typename PTR>
friend class BoundCallbackFactory;
};
template<typename R C TYPENAMES>
void operator==(const function<R (ARG_TYPES)>&,
const function<R (ARG_TYPES)>&);
template<typename R C TYPENAMES>
void operator!=(const function<R (ARG_TYPES)>&,
const function<R (ARG_TYPES)>&);
template<typename R C TYPENAMES>
class FreeCallbackFactory
{
private:
template<R (*Func)(ARG_TYPES)>
static R Wrapper(const void* C ARG_TYPES_AND_NAMES)
{
return (*Func)(ARG_NAMES);
}
public:
template<R (*Func)(ARG_TYPES)>
inline static function<R (ARG_TYPES)> Bind()
{
return function<R (ARG_TYPES)>
(&FreeCallbackFactory::Wrapper<Func>, 0);
}
};
template<typename R C TYPENAMES>
inline FreeCallbackFactory<R C ARG_TYPES>
GetFreeCallbackFactory(R (*)(ARG_TYPES))
{
return FreeCallbackFactory<R C ARG_TYPES>();
}
template<typename R, class T C TYPENAMES>
class MemberCallbackFactory
{
private:
template<R (T::*Func)(ARG_TYPES)>
static R Wrapper(const void* o C ARG_TYPES_AND_NAMES)
{
T* obj = const_cast<T*>(static_cast<const T*>(o));
return (obj->*Func)(ARG_NAMES);
}
public:
template<R (T::*Func)(ARG_TYPES)>
inline static function<R (ARG_TYPES)> Bind(T* o)
{
return function<R (ARG_TYPES)>
(&MemberCallbackFactory::Wrapper<Func>,
static_cast<const void*>(o));
}
};
template<typename R, class T C TYPENAMES>
inline MemberCallbackFactory<R, T C ARG_TYPES>
GetCallbackFactory(R (T::*)(ARG_TYPES), T*)
{
return MemberCallbackFactory<R, T C ARG_TYPES>();
}
template<typename R, class T C TYPENAMES>
class ConstMemberCallbackFactory
{
private:
template<R (T::*Func)(ARG_TYPES) const>
static R Wrapper(const void* o C ARG_TYPES_AND_NAMES)
{
const T* obj = static_cast<const T*>(o);
return (obj->*Func)(ARG_NAMES);
}
public:
template<R (T::*Func)(ARG_TYPES) const>
inline static function<R (ARG_TYPES)> Bind(const T* o)
{
return function<R (ARG_TYPES)>
(&ConstMemberCallbackFactory::Wrapper<Func>,
static_cast<const void*>(o));
}
};
template<typename R, class T C TYPENAMES>
inline ConstMemberCallbackFactory<R, T C ARG_TYPES>
GetCallbackFactory(R (T::*)(ARG_TYPES) const, const T*)
{
return ConstMemberCallbackFactory<R, T C ARG_TYPES>();
}
template<typename R C TYPENAMES, typename PTR>
class BoundCallbackFactory
{
private:
template<R (*Func)(PTR* C ARG_TYPES)>
static R Wrapper(const void* o C ARG_TYPES_AND_NAMES)
{
return (*Func)((PTR*)o C ARG_NAMES);
}
public:
template<R (*Func)(PTR* C ARG_TYPES)>
inline static function<R (ARG_TYPES)> Bind(PTR* o)
{
return function<R (ARG_TYPES)>
(&BoundCallbackFactory::Wrapper<Func>, o);
}
};
template<typename R C TYPENAMES, typename PTR>
inline BoundCallbackFactory<R C ARG_TYPES, PTR>
GetCallbackFactory(R (*)(PTR* C ARG_TYPES), PTR*)
{
return BoundCallbackFactory<R C ARG_TYPES, PTR>();
}
#undef COUNT
#undef LOOP
#endif