-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTFactory.inl
371 lines (320 loc) · 14.6 KB
/
TFactory.inl
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
///
/// Langulus::Flow
/// Copyright (c) 2017 Dimo Markov <[email protected]>
/// Part of the Langulus framework, see https://langulus.com
///
/// SPDX-License-Identifier: GPL-3.0-or-later
///
#pragma once
#include "TFactory.hpp"
#define TEMPLATE() template<class T, FactoryUsage USAGE>
#define FACTORY() TFactory<T, USAGE>
#if 0
#define VERBOSE_FACTORY(...) Logger::Verbose(__VA_ARGS__)
#else
#define VERBOSE_FACTORY(...) LANGULUS(NOOP)
#endif
namespace Langulus::Flow
{
/// Factory destructor
/// Checks if all elements are referenced exactly once before destruction
/// if safe mode is enabled
TEMPLATE() LANGULUS(INLINED)
FACTORY()::~TFactory() {
static_assert(CT::Producible<T>, "T must have a producer");
// First stage destruction that severs all connections
// If TFactory was on the stack, this will be the first time it's
// called. If it was on the managed heap, it would've been called
// from the Reference routine
Teardown();
}
/// Move-assignment remaps all elements to the new instance owner
/// @attention notice how mFactoryOwner never changes on both sides
/// @param other - the factory to move
TEMPLATE() LANGULUS(INLINED)
auto FACTORY()::operator = (TFactory&& other) noexcept -> TFactory& {
Base::operator = (Forward<Base>(other));
mHashmap = Move(other.mHashmap);
return *this;
}
/// Reset the factory
TEMPLATE() LANGULUS(INLINED)
void FACTORY()::Reset() {
mHashmap.Reset();
Base::Reset();
}
/// Factories can have their own hierarchies going on, and tearing them
/// down to account of circular references is mandatory. Here's an example:
/// The Physics module has a Euclidean::World factory.
/// Each World has a factory for Euclidean::Instances
/// Each Instance refers to its World producer
/// In order to destroy a World, you have to first visit all Instances
/// and sever their connection with the World, so that the proper order
/// of destruction happens. Otherwise the World will be destroyed from
/// the Instance::mProducer handle instead on World factory destruction.
TEMPLATE() LANGULUS(INLINED)
void FACTORY()::Teardown() {
mHashmap.Reset();
for (auto& item : *this) {
item.TeardownInner();
// Propagate Teardown routine
if constexpr (requires { item.Teardown(); })
item.Teardown();
}
}
#if LANGULUS(SAFE)
/// Dump the factory to the log
TEMPLATE()
void FACTORY()::Dump() const {
const auto scope = Logger::SpecialTab("--------- FACTORY DUMP FOR ",
MetaDataOf<TFactory>(), " (", Base::mCount, " of ", Base::mReserved,
" cells used in ", Base::mFrames.GetCount(), " frames): "
);
Count counter = 0;
for (auto& item : *this) {
Logger::Info(counter++, "] ", item, ", ",
item.GetReferences(), " references");
}
}
#endif
/// Find an element with the provided descriptor
/// @param descriptor - the normalized descriptor for the element
/// @return the found element, or nullptr if not found
TEMPLATE() LANGULUS(INLINED)
auto FACTORY()::FindInner(const Many& descriptor) const -> Cell* {
VERBOSE_FACTORY(NameOf<TFactory>(), " seeking for ", descriptor);
const auto hash = descriptor.GetHash();
const auto found = mHashmap.FindIt(hash);
if (found) {
for (auto cell : found.GetValue()) {
if (cell->mData.GetDescriptor() != descriptor)
continue;
return cell;
}
}
return nullptr;
}
/// Create/Destroy element(s) inside the factory
/// @param producer - the producer
/// @param verb - the creation verb
TEMPLATE()
void FACTORY()::Create(auto* producer, Verb& verb) {
static_assert(CT::Related<ProducerOf<T>, decltype(producer)>,
"Producer isn't related to the reflected one");
verb.ForEachDeep(
[&](const Construct& construct) {
// For each construct...
if (not MetaOf<T>()->CastsTo(construct.GetType()))
return;
auto count = static_cast<int>(
::std::floor(construct.GetCharge().mMass * verb.GetMass())
);
CreateInner(producer, verb, count, construct.GetDescriptor());
},
[&](const DMeta& type) {
// For each type...
if (not type or not MetaOf<T>()->CastsTo(type))
return;
auto count = static_cast<int>(
::std::floor(verb.GetMass())
);
CreateInner(producer, verb, count);
}
);
}
/// Create (or reuse) a single element
/// @param producer - the producer
/// @param descriptor - the descriptor
/// @return the new (or reused) instance
TEMPLATE()
auto FACTORY()::CreateOne(auto* producer, const Many& descriptor) -> T* {
static_assert(CT::Related<ProducerOf<T>, decltype(producer)>,
"Producer isn't related to the reflected one");
// Produce amount of compatible constructs
if constexpr (IsUnique) {
// Check if descriptor matches any of the available
const auto found = FindInner(descriptor);
if (found)
return &found->mData;
}
// If reached, nothing was found
// Produce exactly one element with this descriptor
return Produce(producer, descriptor);
}
/// Inner creation/destruction verb
/// @param producer - the producer
/// @param verb - [in/out] the creation/destruction verb
/// @param count - the number of items to create (or destroy if negative)
/// @param descriptor - element descriptor
TEMPLATE()
void FACTORY()::CreateInner(
auto* producer, Verb& verb, int count, const Many& descriptor
) {
static_assert(CT::Related<ProducerOf<T>, decltype(producer)>,
"Producer isn't related to the reflected one");
if (count > 0) {
// Produce amount of compatible constructs
if constexpr (IsUnique) {
// Check if descriptor matches any of the available
const auto found = FindInner(descriptor);
if (found) {
// The unique construct was found, just return it.
// Mass will be ignored, it makes no sense to
// create multiple instances if unique
verb << &found->mData;
return;
}
// If reached, nothing was found
// Produce exactly one element with this descriptor
// Mass will be ignored, it makes no sense to create
// multiple instances if unique
verb << Produce(producer, descriptor);
}
else {
// Satisfy the required count
while (count >= 1) {
auto produced = Produce(producer, descriptor);
verb << produced;
--count;
}
}
}
else if (count < 0) {
// Destroy amount of compatible constructs
if constexpr (IsUnique) {
// Check if descriptor matches any of the available
const auto found = FindInner(descriptor);
if (found) {
// The unique construct was found, destroy it
// Mass is ignored, there should be exactly one
Destroy(found);
return;
}
}
else {
// Destroy the required amount of matching items
do {
const auto found = FindInner(descriptor);
if (not found)
break;
Destroy(found);
++count;
}
while (count < 0);
}
verb.Done();
}
}
/// Select/Deselect element(s) inside the factory
/// @param verb - the selection verb
TEMPLATE()
void FACTORY()::Select(Verb& verb) {
// For each construct or meta compatible with the factory
verb.ForEachDeep(
[&](const Construct& construct) {
// For each construct...
if (not MetaDataOf<T>()->CastsTo(construct.GetType()))
return;
TODO();
},
[&](const DMeta& type) {
// For each type...
if (not type or not MetaDataOf<T>()->CastsTo(type))
return;
TODO();
}
);
}
/// External interface for finding an entry in the factory
/// @param descriptor - descriptor to match exactly
/// @return a valid pointer if element was found
TEMPLATE()
auto FACTORY()::Find(const Many& descriptor) const -> const T* {
const auto found = FindInner(descriptor);
if (found)
return &found->mData;
return nullptr;
}
/// Produce a single T with the given descriptor
/// @param producer - the producer
/// @param descriptor - element descriptor
/// @return the produced instance
TEMPLATE()
auto FACTORY()::Produce(auto* producer, const Many& descriptor) -> T* {
static_assert(CT::Related<ProducerOf<T>, decltype(producer)>,
"Producer isn't related to the reflected one");
// Register entry in the hashmap, for fast search by descriptor
VERBOSE_FACTORY(NameOf<TFactory>(), " producing: ", descriptor);
auto result = Base::NewInner(producer, descriptor);
if (not result)
return nullptr;
const auto hash = result->mData.GetHash();
const auto found = mHashmap.FindIt(hash);
if (found)
found.GetValue() << result;
else
mHashmap.Insert(hash, result);
return &result->mData;
}
/// Destroys an element inside factory
/// @attention assumes item is a valid pointer, owned by the factory
/// @attention item pointer is no longer valid after this call
/// @param item - element to destroy
TEMPLATE()
void FACTORY()::Destroy(Cell* item) {
// Remove from hashmap
const auto hash = item->mData.GetHash();
auto& list = mHashmap[hash];
list.Remove(item);
if (not list)
mHashmap.RemoveKey(hash);
// Destroy the element
Base::Destroy(item);
}
/// Move a produced item
/// @param other - the item to move
template<class T> LANGULUS(INLINED)
ProducedFrom<T>::ProducedFrom(ProducedFrom&& other)
: ProducedFrom {Move(other)} {}
/// Generic construction
/// @param other - intent and element to initialize with
template<class T> template<template<class> class S> LANGULUS(INLINED)
ProducedFrom<T>::ProducedFrom(S<ProducedFrom>&& other)
requires CT::Intent<S<ProducedFrom>>
// mProducer intentionally not overwritten
: mDescriptor {other.Nest(other->mDescriptor)} {}
/// Construct a produced item
/// @param producer - the item's producer
/// @param descriptor - the item's neat descriptor
template<class T> LANGULUS(INLINED)
ProducedFrom<T>::ProducedFrom(T* producer, const Many& descriptor)
: mDescriptor {descriptor}
, mProducer {producer} {}
/// Get the normalized descriptor of the produced item
/// @return the normalized descriptor
template<class T> LANGULUS(INLINED)
auto ProducedFrom<T>::GetDescriptor() const noexcept -> const Many& {
return mDescriptor;
}
/// Get the hash of the normalized descriptor (cached and efficient)
/// @return the hash
template<class T> LANGULUS(INLINED)
Hash ProducedFrom<T>::GetHash() const noexcept {
return mDescriptor.GetHash();
}
/// Return the producer of the item (a.k.a. the owner of the factory)
/// @return a pointer to the producer instance
template<class T> LANGULUS(INLINED)
auto ProducedFrom<T>::GetProducer() const noexcept -> const Ref<T>& {
return mProducer;
}
/// Return the producer of the item (a.k.a. the owner of the factory)
/// @return a pointer to the producer instance
template<class T> LANGULUS(INLINED)
void ProducedFrom<T>::TeardownInner() {
mDescriptor.Reset();
mProducer.Reset();
}
} // namespace Langulus::Flow
#undef TEMPLATE
#undef FACTORY