forked from HeliumProject/Foundation
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Event.h
296 lines (240 loc) · 8.41 KB
/
Event.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
292
293
294
295
296
#pragma once
#include "Platform/Types.h"
#include "Platform/Assert.h"
#include "Foundation/SmartPtr.h"
#include <vector>
namespace Helium
{
//////////////////////////////////////////////////////////////////////////
//
// C++ Delegate/Event System
//
// Classes:
//
// Signature is a template that contains all classes for a given function
// signature. Typedef Signature as a starting point for use in code.
//
// Delegate is an encapsulation of a function that matches the signature.
// Delegate::Function delegates invocation to a standard C function.
// Delegate::Method delegates invocation to a member function.
//
// Event is a set of delegates that are invoked together.
//
// Comments:
//
// The reliance on internally allocated worker (Impl) classes is there to
// allow for experimentation with memory allocation without having to
// refactor tons of code to test changes. Since the allocation is masked
// client code will never call new itself. There is lots of overloading
// and template member functions to support as much efficiency as possible.
// Utilize template member functions over Delegate prototypes to prevent
// needless heap thrashing (creation, comparison, and deletion of internal
// DelegateImpl objects).
//
// Usage:
//
// struct Args {};
//
// void Function( Args args ) {}
//
// class Foo
// {
// void Method( Args args ) {}
// };
//
// // this explicitly instantiates all the classes required for a signature
// typedef Helium::Signature<Args> ExampleSignature;
//
// ExampleSignature::Event g_Event;
// Foo g_Foo;
//
// g_Event.Add( &Function );
// g_Event.Add( &g_Foo, &Foo::Method ) );
// g_Event.Raise( Args () );
// g_Event.Remove( &Function );
// g_Event.Remove( &g_Foo, &Foo::Method ) );
//
// To Do:
//
// * Add support for stl or 'Helium' allocators in place of C++ heap
// * Elminate heap usage in Delegate with some horrific unions:
// http://www.codeproject.com/cpp/fastdelegate2.asp
//
//////////////////////////////////////////////////////////////////////////
//
// This is provided as a placeholder because 'void' is not instantiatable in C++
//
enum Void {};
//
// The different delegate types that are supported, probably won't change very often
//
namespace DelegateTypes
{
enum DelegateType
{
Function,
Method,
};
}
typedef DelegateTypes::DelegateType DelegateType;
//
// Raw Invoke interface (to be hidden by a dervied class via c++ function hiding)
// NOT TYPE SAFE, DANGER!
//
class Invokable
{
public:
virtual void Invoke( void* argument, void* instance = NULL ) const = 0;
};
//
// Delegate encapsulates and a function call of multiple types
//
template< typename ArgsType, template< typename T > class RefCountBaseType = RefCountBase >
class Delegate
{
public:
Delegate();
Delegate( const Delegate& rhs );
template < typename FunctionType >
Delegate( FunctionType function );
template < class ClassType, typename MethodType >
Delegate( ClassType* instance, MethodType method );
template < typename FunctionType >
inline static Delegate Create( FunctionType function );
template < class ClassType, typename MethodType >
inline static Delegate Create( ClassType* instance, MethodType method );
void Clear();
bool Valid() const;
void Set( const Delegate& delegate );
template < typename FunctionType >
void Set( FunctionType function );
template < class ClassType, typename MethodType >
void Set( ClassType* instance, MethodType method );
bool Equals( const Delegate& rhs ) const;
template <typename FunctionType>
bool Equals( FunctionType function ) const;
template <class ClassType, typename MethodType>
bool Equals( const ClassType* instance, MethodType method ) const;
void Invoke( ArgsType parameter ) const;
//
// DelegateImpl implements the guts of Delegate and is heap allocated and reference counted.
// Its defines an interface (pure virtual) that must be implemented by derived classes
//
class DelegateImpl : public RefCountBaseType< DelegateImpl >, public Invokable
{
private:
friend class Delegate;
public:
// Deduce the type of this delegate
virtual DelegateType GetType() const = 0;
// Equality checking (so we don't add the same one twice)
virtual bool Equals( const DelegateImpl* rhs ) const = 0;
// The type-safe entry point for invoking the event handlers this Delegate encapsulates
virtual void Invoke( ArgsType parameter ) const = 0;
};
//
// Function implements Delegate for a static function
//
class Function : public DelegateImpl
{
public:
typedef void (*FunctionType)(ArgsType);
Function( FunctionType function );
virtual DelegateType GetType() const;
virtual bool Equals( const DelegateImpl* rhs ) const;
virtual void Invoke( ArgsType parameter ) const;
virtual void Invoke( void* parameter, void* instance = NULL ) const;
private:
FunctionType m_Function;
friend class Delegate;
};
//
// Method implements Delegate for a member function of an instance of a class or struct
//
template<class ClassType>
class Method : public DelegateImpl
{
public:
typedef void (ClassType::*MethodType)(ArgsType);
Method( ClassType* instance, MethodType method );
virtual DelegateType GetType() const;
virtual bool Equals( const DelegateImpl* rhs ) const;
virtual void Invoke( ArgsType parameter ) const;
virtual void Invoke( void* parameter, void* instance = NULL ) const;
private:
ClassType* m_Instance;
MethodType m_Method;
friend class Delegate;
};
Helium::SmartPtr< DelegateImpl > m_Impl;
};
//
// Event is a collection of delegates that are invoked together
//
template< typename ArgsType, template< typename T > class RefCountBaseType = RefCountBase >
class Event
{
public:
typedef Helium::Delegate< ArgsType, RefCountBaseType > Delegate;
uint32_t Count() const;
bool Valid() const;
void Add( const Delegate& delegate );
template < typename FunctionType >
void AddFunction( FunctionType function );
template < class ClassType, typename MethodType >
void AddMethod( ClassType* instance, MethodType method );
void Remove( const Delegate& delegate );
template < typename FunctionType >
void RemoveFunction( FunctionType function );
template < class ClassType, typename MethodType >
void RemoveMethod( const ClassType* instance, MethodType method );
void Raise( ArgsType parameter );
void RaiseWithEmitter( ArgsType parameter, const Delegate& emitter );
//
// EventImpl implements the guts of Event and is heap allocated and reference counted.
// The choice to make this heap allocated is so that we can handled the "owner" of the
// event being destroyed while the event is raised while at the same time supporting the
// removal of delegates from the event.
//
class EventImpl : public RefCountBaseType< EventImpl >
{
public:
EventImpl();
// Query for count
uint32_t Count() const;
// Compact dead pointers caused by Remove() inside Raise()
void Compact();
// Add the delegate function to the list
void Add( const Delegate& delegate );
template < typename FunctionType >
void AddFunction( FunctionType function );
template < class ClassType, typename MethodType >
void AddMethod( ClassType* instance, MethodType method );
// Remove the delegate function from the list
void Remove( const Delegate& delegate );
template < typename FunctionType >
void RemoveFunction( FunctionType function );
template < class ClassType, typename MethodType >
void RemoveMethod( const ClassType* instance, MethodType method );
// Invoke all of the delegates for this event occurrence. Pays no mind about the return value of the invocation
void Raise( ArgsType parameter, const Delegate& emitter );
private:
std::vector<Delegate> m_Delegates;
uint32_t m_EntryCount;
uint32_t m_EmptySlots;
};
Helium::SmartPtr< EventImpl > m_Impl;
};
//
// Signature intantiates all the template classes necessary for working with a particular signature
//
template< typename ArgsType, template< typename T > class RefCountBaseType = RefCountBase >
class Signature
{
public:
typedef Helium::Delegate< ArgsType, RefCountBaseType > Delegate;
typedef Helium::Event< ArgsType, RefCountBaseType > Event;
};
typedef Helium::Signature<Helium::Void> VoidSignature;
}
#include "Foundation/Event.inl"