/
Autowired.h
412 lines (358 loc) · 13.1 KB
/
Autowired.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
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved.
#pragma once
#include "AutowirableSlot.h"
#include "Decompose.h"
#include "Deferred.h"
#include "GlobalCoreContext.h"
#include MEMORY_HEADER
#include ATOMIC_HEADER
class CoreContext;
class GlobalCoreContext;
/// <summary>
/// Provides a simple way to obtain a reference to the current context
/// </summary>
/// <remarks>
/// Users of this class are encouraged not to hold references for longer than needed. Failing
/// to release a context pointer could prevent resources from being correctly released.
/// </remarks>
class AutoCurrentContext:
public std::shared_ptr<CoreContext>
{
public:
AutoCurrentContext(void);
};
/// <summary>
/// Simple way to obtain a reference to the global context
/// </summary>
class AutoGlobalContext:
public std::shared_ptr<GlobalCoreContext>
{
public:
AutoGlobalContext(void);
};
/// <summary>
/// Provides a simple way to create a dependent context pointer
/// </summary>
/// <remarks>
/// The newly created context will be created using CoreContext::CurrentContext()->Create().
/// </remarks>
template<typename T>
class AutoCreateContextT:
public std::shared_ptr<CoreContext>
{
public:
AutoCreateContextT(void) :
std::shared_ptr<CoreContext>(CoreContext::CurrentContext()->Create<T>())
{}
AutoCreateContextT(AutoInjectable&& inj) :
std::shared_ptr<CoreContext>(CoreContext::CurrentContext()->Create<T>(std::move(inj)))
{}
AutoCreateContextT(std::shared_ptr<CoreContext>& ctxt) :
std::shared_ptr<CoreContext>(ctxt->Create<T>())
{}
AutoCreateContextT(std::shared_ptr<CoreContext>& ctxt, AutoInjectable&& inj) :
std::shared_ptr<CoreContext>(ctxt->Create<T>(std::move(inj)))
{}
};
typedef AutoCreateContextT<void> AutoCreateContext;
/// <summary>
/// An autowired template class that forms the foundation of the context consumer system
/// </summary>
/// <param name="T">The class whose type is to be found. Must be an EXACT match.</param>
/// <remarks>
/// The autowired class offers a quick way to import or create an instance of a specified
/// class in the local context.
///
/// This class may be safely used even when the member in question is an abstract type.
/// </remarks>
template<class T>
class Autowired:
public AutowirableSlot<T>
{
public:
Autowired(const std::shared_ptr<CoreContext>& ctxt = CoreContext::CurrentContext()) :
AutowirableSlot<T>(ctxt),
m_pFirstChild(nullptr)
{
if(ctxt)
ctxt->Autowire(
*static_cast<AnySharedPointerT<T>*>(
static_cast<AnySharedPointer*>(this)
),
*this
);
}
~Autowired(void) {
if(m_pFirstChild == this)
// Tombstoned, nothing to do:
return;
// Need to ensure that nobody tries to autowire us while we are tearing down:
this->CancelAutowiring();
// And now we destroy our deferrable autowiring collection:
std::unique_ptr<DeferrableAutowiring> prior;
for(DeferrableAutowiring* cur = m_pFirstChild; cur; cur = cur->GetFlink())
prior.reset(cur);
}
private:
// The first deferred child known to need registration. This is distinct from the m_pFlink entry in
// the base class, which refers to the _next sibling_; by contrast, this type refers to the _first child_
// which will be the first member registered via NotifyWhenAutowired.
std::atomic<AutowirableSlot<T>*> m_pFirstChild;
public:
operator const std::shared_ptr<T>&(void) const {
return
static_cast<const AnySharedPointerT<T>*>(
static_cast<const AnySharedPointer*>(
this
)
)->slot()->get();
}
operator std::weak_ptr<T>(void) const {
return this->operator const std::shared_ptr<T>&();
}
operator T*(void) const {
return this->operator const std::shared_ptr<T>&().get();
}
/// <summary>
/// Allows a lambda function to be called when this slot is autowired
/// </summary>
/// <remarks>
/// In contrast with CoreContext::NotifyWhenAutowired, the specified lambda will only be
/// called as long as this Autowired slot has not been destroyed. If this slot is destroyed
/// beforehand, the lambda will never be invoked.
/// </remarks>
template<class Fn>
void NotifyWhenAutowired(Fn fn) {
// Trivial initial check:
if(*this) {
fn();
return;
}
// We pass a null shared_ptr, because we do not want this slot to attempt any kind of unregistration when
// it goes out of scope. Instead, we will manage its entire registration lifecycle, and
// retain full ownership over the object until we need to destroy it.
auto newHead = new AutowirableSlotFn<T, Fn>(std::shared_ptr<CoreContext>(), std::forward<Fn>(fn));
// Append to our list in a lock-free way. This is a fairly standard way to do a lock-free append to
// a singly linked list; the only unusual aspect is the use of "this" as a tombstone indicator (IE,
// to signify that the list should no longer be used).
AutowirableSlot<T>* pFirstChild;
do {
// Obtain the current first child, and keep it here so we know what to exchange out with later:
pFirstChild = m_pFirstChild;
// Determine if we've been tombstoned at this piont:
if(pFirstChild == this) {
// Trivially satisfy, and then return. This might look like a leak, but it's not, because we know
// that Finalize is going to destroy the object.
newHead->SatisfyAutowiring(*this);
newHead->Finalize();
return;
}
// Try to set the forward link to the current head, and then update our own flink;
newHead->SetFlink(pFirstChild);
} while(!m_pFirstChild.compare_exchange_weak(pFirstChild, newHead, std::memory_order_acquire));
}
// Base overrides:
DeferrableAutowiring* ReleaseDependentChain(void) override {
// Rip the head off the list, replace it with a tombstone:
auto pFirstChild = m_pFirstChild.exchange(this, std::memory_order_relaxed);
// If we got the tombstone back, we have nothing to return. Otherwise return the element.
return pFirstChild == this ? nullptr : pFirstChild;
}
};
/// <summary>
/// Similar to Autowired, Creates a new instance if this instance isn't autowired
/// </summary>
/// <remarks>
/// This class is simply a convenience class and provides a declarative way to name a required dependency.
///
/// If type T has a static member function called New, the helper's Create routine will attempt call
/// this function instead of the default constructor, even if the default constructor has been supplied,
/// and even if the arity of the New routine is not zero.
///
/// To prevent this behavior, use a name other than New.
/// </remarks>
template<class T>
class AutoRequired:
public std::shared_ptr<T>
{
public:
using std::shared_ptr<T>::operator=;
// !!!!! READ THIS IF YOU ARE GETTING A COMPILER ERROR HERE !!!!!
// If you are getting an error tracked to this line, ensure that class T is totally
// defined at the point where the Autowired instance is constructed. Generally,
// such errors are tracked to missing header files. A common mistake, for instance,
// is to do something like this:
//
// class MyClass;
//
// struct MyStructure {
// Autowired<MyClass> m_member;
// };
//
// At the time m_member is instantiated, MyClass is an incomplete type. So, when the
// compiler tries to instantiate AutowiredCreator::Create (the function you're in right
// now!) it finds that it can't create a new instance of type MyClass because it has
// no idea how to construct it!
//
// This problem can be fixed two ways: You can include the definition of MyClass before
// MyStructure is defined, OR, you can give MyStructure a nontrivial constructor, and
// then ensure that the definition of MyClass is available before the nontrivial
// constructor is defined.
//
// !!!!! READ THIS IF YOU ARE GETTING A COMPILER ERROR HERE !!!!!
AutoRequired(const std::shared_ptr<CoreContext>& ctxt = CoreContext::CurrentContext()):
std::shared_ptr<T>(ctxt->template Inject<T>())
{}
/// <summary>
/// Construct overload, for types which take constructor arguments
/// </summary>
template<class... Args>
AutoRequired(const std::shared_ptr<CoreContext>& ctxt, Args&&... args) :
std::shared_ptr<T>(ctxt->template Inject<T>(std::forward<Args>(args)...))
{}
operator bool(void) const {
return IsAutowired();
}
operator T*(void) const {
return std::shared_ptr<T>::get();
}
bool IsAutowired(void) const {return std::shared_ptr<T>::get() != nullptr;}
};
/// <summary>
/// Similar to Autowired, but doesn't defer creation if types doesn't already exist
/// </summary>
template<class T>
class AutowiredFast:
public std::shared_ptr<T>
{
public:
using std::shared_ptr<T>::operator=;
// !!!!! Read comment in AutoRequired if you get a compiler error here !!!!!
AutowiredFast(const std::shared_ptr<CoreContext>& ctxt = CoreContext::CurrentContext()) {
if (ctxt)
ctxt->FindByTypeRecursive(*this);
}
AutowiredFast(const CoreContext* pCtxt) {
pCtxt->FindByTypeRecursive(*this);
}
operator bool(void) const {
return IsAutowired();
}
operator T*(void) const {
return std::shared_ptr<T>::get();
}
bool IsAutowired(void) const {return std::shared_ptr<T>::get() != nullptr;}
};
/// <summary>
/// Idiom to enable boltable classes
/// </summary>
template<class T>
class AutoEnable
{
public:
// !!!!! Read comment in AutoRequired if you get a compiler error here !!!!!
AutoEnable(std::shared_ptr<CoreContext> ctxt = CoreContext::CurrentContext()) {
ctxt->Enable<T>();
}
};
/// <summary>
/// Convenience class which attempts to inject type T and discards any exceptions
/// </summary>
/// <remarks>
/// Use of this type is functionally equivalent to the following:
///
/// try {
/// AutoCurrentContext()->Inject<T>();
/// catch(...) {
/// AutoCurrentContext()->FilterException();
/// }
/// Autowired<T> foo;
///
/// Users who wish to know whether an exception was thrown may replace uses of AutoDesired with the above
/// and perform their own handling.
/// </remarks>
template<class T>
class AutoDesired:
public Autowired<T>
{
public:
AutoDesired(void) {
try { AutoRequired<T>(); }
catch(...) {
CoreContext::CurrentContext()->FilterException();
}
}
};
/// <summary>
/// Convenience class functionally identical to AutoRequired, but specialized to forward ctor arguments
/// </summary>
template<class T>
class AutoConstruct:
public std::shared_ptr<T>
{
public:
template<class... Args>
AutoConstruct(Args&&... args) :
std::shared_ptr<T>(CoreContext::CurrentContext()->template Inject<T>(std::forward<Args&&>(args)...))
{}
operator bool(void) const { return IsAutowired(); }
operator T*(void) const { return std::shared_ptr<T>::get(); }
bool IsAutowired(void) const { return std::shared_ptr<T>::get() != nullptr; }
};
/// <summary>
/// Convenience class to create an event firer. Also caches the associated JunctionBox
/// </summary>
template<class T>
class AutoFired
{
public:
AutoFired(const std::shared_ptr<CoreContext>& ctxt = CoreContext::CurrentContext()):
m_junctionBox(ctxt->GetJunctionBox<T>())
{}
/// <summary>
/// Utility constructor, used when the receiver is already known
/// </summary>
AutoFired(const std::shared_ptr<JunctionBox<T>>& junctionBox) :
m_junctionBox(junctionBox)
{}
/// <summary>
/// Utility constructor, used to support movement operations
/// </summary>
AutoFired(AutoFired&& rhs):
m_junctionBox(std::move(rhs.m_junctionBox))
{}
private:
std::weak_ptr<JunctionBox<T>> m_junctionBox;
public:
bool HasListeners(void) const {
//TODO: Refactor this so it isn't messy
//check: does it have any direct listeners, or are any appropriate marshalling objects wired into the immediate context?
auto ctxt = CoreContext::CurrentContext();
bool checkval = ctxt->CheckEventOutputStream<T>();
return (checkval || (!m_junctionBox.expired() && m_junctionBox.lock()->HasListeners()) );
}
template<class MemFn>
InvokeRelay<MemFn> operator()(MemFn pfn) const {
static_assert(std::is_same<typename Decompose<MemFn>::type, T>::value, "Cannot invoke an event for an unrelated type");
auto box = m_junctionBox.lock();
if(!box)
// Context has been destroyed
return InvokeRelay<MemFn>();
AutoGlobalContext()->Invoke(&AutowiringEvents::EventFired)(*CoreContext::CurrentContext(),typeid(typename Decompose<MemFn>::type));
return MakeInvokeRelay(box, pfn);
}
template<class MemFn>
InvokeRelay<MemFn> Fire(MemFn pfn) const {
static_assert(!std::is_same<typename Decompose<MemFn>::retType, Deferred>::value, "Cannot Fire an event which is marked Deferred");
return operator()(pfn);
}
template<class MemFn>
InvokeRelay<MemFn> Defer(MemFn pfn) const {
static_assert(std::is_same<typename Decompose<MemFn>::retType, Deferred>::value, "Cannot Defer an event which does not return the Deferred type");
return operator()(pfn);
}
};
// We will also pull in a few utility headers which are reliant upon the declarations in this file
// TODO: Consider moving the declarations in this file into its own header, and using this header
// as a master header for all of Autowiring
#include "AutoPacketFactory.h"