Sound Bakery  v0.1.0
Open-source audio middleware for games
Loading...
Searching...
No Matches
database_ptr.h
1#pragma once
2
3#include "sound_bakery/core/database/database_object.h"
4#include "sound_bakery/pch.h"
5
6namespace SB::Core
7{
8 class DatabaseObject;
9
10 std::weak_ptr<DatabaseObject> SB_API findObject(SB_ID id);
11 bool SB_API objectIdIsChildOfParent(SB_ID childToCheck, SB_ID parent);
12 SB_ID SB_API getParentIdFromId(SB_ID id);
13
21 template <typename TObject>
23 {
24 public:
26 using TIdentifierType = SB_ID;
27 using TObjectPtr = TObject*;
28 using TObjectRef = TObject&;
29 using TObjectWeak = std::weak_ptr<DatabaseObject>;
30 using TObjectShared = std::shared_ptr<TObject>;
31 using TPtrType = std::weak_ptr<DatabaseObject>;
32
33 static_assert(!std::is_pointer<TObject>::value);
34
35 public:
39 DatabasePtr() : m_objectID(), m_objectPtr(), m_null(true) {}
40
41 DatabasePtr(const TThisType& other) = default;
42 DatabasePtr(TThisType&& other) = default;
43
49 DatabasePtr(SB_ID id) : m_objectID(id), m_objectPtr(), m_null(true) {}
50
55 DatabasePtr(const TObjectPtr& object)
56 : m_objectID(static_cast<TIdentifierType>(*object)), m_objectPtr(findObject(id())), m_null(false)
57 {
58 }
59
64 DatabasePtr(std::nullptr_t) : m_objectID(), m_objectPtr(), m_null(true) {}
65
66 ~DatabasePtr() = default;
67
68 public:
73 SB_ID id() const noexcept { return m_objectID; }
74
75 TObjectShared shared() const noexcept
76 {
77 lookup();
78
79 if (m_objectPtr.expired())
80 {
81 return std::shared_ptr<TObject>();
82 }
83 else
84 {
85 return std::static_pointer_cast<TObject>(m_objectPtr.lock());
86 }
87 }
88
89 TObjectWeak weak() const noexcept { return m_objectPtr; }
90
95 TObjectPtr raw() const noexcept { return shared().get(); }
96
97 TObjectPtr lookupRaw() const noexcept
98 {
99 lookup();
100 return raw();
101 }
102
108 bool hasId() const { return m_objectID != TIdentifierType(); }
109
114 bool null() const { return m_null || m_objectPtr.expired(); }
115
121 bool pending() const { return hasId() && null(); }
122
128 bool stale() const { return !m_null && m_objectPtr.expired(); }
129
135 bool valid() const { return hasId() && !null(); }
136
137 public:
142 bool lookup() const
143 {
144 if (pending())
145 {
146 m_objectPtr = findObject(id());
147 m_null = m_objectPtr.expired();
148 }
149 return valid();
150 }
151
155 void reset(TObjectPtr object = nullptr)
156 {
157 m_objectID = object ? static_cast<TIdentifierType>(*object) : TIdentifierType();
158 m_objectPtr.reset();
159 m_null = true;
160
161 lookup();
162 }
163
164 public:
171 TThisType& operator=(TObjectPtr object)
172 {
173 if (raw() != object)
174 {
175 reset(object);
176 }
177 return *this;
178 }
179
180 TThisType& operator=(const TThisType& other)
181 {
182 if (id() != other.id())
183 {
184 m_objectID = other.id();
185 m_objectPtr = other.weak();
186 m_null = other.null();
187 }
188
189 return *this;
190 }
191
192 TThisType& operator=(const TThisType&& other)
193 {
194 if (id() != other.id())
195 {
196 m_objectID = other.id();
197 m_objectPtr = other.weak();
198 m_null = other.null();
199 }
200
201 return *this;
202 }
203
207 operator bool() const { return valid(); }
208
212 bool operator!() const { return !valid(); }
213
218 TObjectPtr operator->() const { return raw(); }
219
220 protected:
221 SB_ID m_objectID;
222 mutable TPtrType m_objectPtr = TPtrType();
223 mutable bool m_null;
224 };
225
236 template <typename T1, typename T2>
237 bool operator==(const DatabasePtr<T1>& lhs, const DatabasePtr<T2>& rhs)
238 {
239 return lhs.id() == rhs.id();
240 }
241
250 template <typename T>
251 bool operator==(const DatabasePtr<T>& lhs, const T* rhs)
252 {
253 return lhs.raw() == rhs;
254 }
255
266 template <typename T1, typename T2>
267 bool operator<(const DatabasePtr<T1>& lhs, const DatabasePtr<T2>& rhs)
268 {
269 return lhs.id() < rhs.id();
270 }
271
276 template <typename TObject>
277 class ChildPtr final : public DatabasePtr<TObject>
278 {
279 public:
281
282 public:
289 ChildPtr() = default;
290
291 ChildPtr(const TThisType& other) : DatabasePtr<TObject>(other), m_ownerID(other.m_ownerID)
292 {
293 // If we don't have an owner, try finding it now
294 // We can't do any other checks because we were empty before this copy
295 // Because we can't do checks, we're just hoping the passed in object is valid
296 if (m_ownerID == 0)
297 {
298 m_ownerID = getParentIdFromId(other.m_objectID);
299 }
300 }
301
302 ChildPtr(TThisType&& other) = default;
303 ~ChildPtr() = default;
304
313 ChildPtr(const DatabaseObject& owner) : DatabasePtr<TObject>(), m_ownerID(owner.getDatabaseID()) {}
314
320 ChildPtr(SB_ID id) : DatabasePtr<TObject>(id), m_ownerID(getParentIdFromId(id)) {}
321
322 TThisType& operator=(typename DatabasePtr<TObject>::TIdentifierType id)
323 {
324 setID(id);
325
326 return *this;
327 }
328
329 TThisType& operator=(typename DatabasePtr<TObject>::TObjectPtr object)
330 {
331 reset(object);
332
333 return *this;
334 }
335
336 TThisType& operator=(const TThisType& other)
337 {
338 if (DatabasePtr<TObject>::id() != other.id())
339 {
340 if (m_ownerID == 0 && DatabasePtr<TObject>::m_objectID != 0)
341 {
342 m_ownerID = getParentIdFromId(DatabasePtr<TObject>::m_objectID);
343 }
344
345 // If we don't have an owner, we don't care about checking children
346 // We can completely copy other
347 if (m_ownerID == 0)
348 {
349 DatabasePtr<TObject>::m_objectID = other.id();
350 DatabasePtr<TObject>::m_objectPtr = other.weak();
351 DatabasePtr<TObject>::m_null = other.null();
352 m_ownerID = other.m_ownerID;
353 }
354 // If owner isn't trying to be changed, we can just check children and update the pointed to ID
355 else if (m_ownerID == other.m_ownerID || other.m_ownerID == 0)
356 {
357 // Do child check
358 if (objectIdIsChildOfParent(other.m_objectID, m_ownerID))
359 {
360 DatabasePtr<TObject>::m_objectID = other.id();
361 DatabasePtr<TObject>::m_objectPtr = other.weak();
362 DatabasePtr<TObject>::m_null = other.null();
363 }
364 }
365 // else: don't allow changing owner IDs once they're set
366 }
367
368 return *this;
369 }
370
371 void setID(typename DatabasePtr<TObject>::TIdentifierType id = 0)
372 {
373 // Fill our parent ID if we didn't have it already
374 if (m_ownerID == 0 && DatabasePtr<TObject>::m_objectID != 0)
375 {
376 m_ownerID = getParentIdFromId(DatabasePtr<TObject>::m_objectID);
377 }
378
379 if (id == 0)
380 {
381 DatabasePtr<TObject>::m_objectID = 0;
382 DatabasePtr<TObject>::m_objectPtr.reset();
383 DatabasePtr<TObject>::m_null = true;
384 }
385 else
386 {
387 if (m_ownerID == 0 || objectIdIsChildOfParent(id, m_ownerID))
388 {
389 DatabasePtr<TObject>::m_objectID = id;
390 DatabasePtr<TObject>::m_objectPtr.reset();
391 DatabasePtr<TObject>::m_null = true;
392 }
393 }
394 }
395
396 void reset(typename DatabasePtr<TObject>::TObjectPtr object = nullptr)
397 {
398 // Fill our parent ID if we didn't have it already
399 if (m_ownerID == 0 && DatabasePtr<TObject>::m_objectID != 0)
400 {
401 m_ownerID = getParentIdFromId(DatabasePtr<TObject>::m_objectID);
402 }
403
404 // Reset pointed to values but retain the owner ID
405 if (object == nullptr)
406 {
407 DatabasePtr<TObject>::m_objectID = 0;
408 DatabasePtr<TObject>::m_objectPtr.reset();
409 DatabasePtr<TObject>::m_null = true;
410 }
411 // Point to new object if it's a child of our owner
412 else
413 {
414 SB_ID newObjectID = static_cast<typename DatabasePtr<TObject>::TIdentifierType>(*object);
415
416 if (m_ownerID == 0 || objectIdIsChildOfParent(newObjectID, m_ownerID))
417 {
418 DatabasePtr<TObject>::m_objectID = newObjectID;
419 DatabasePtr<TObject>::m_objectPtr.reset();
420 DatabasePtr<TObject>::m_null = true;
421 }
422 }
423 }
424
425 private:
426 typename DatabasePtr<TObject>::TIdentifierType m_ownerID = 0;
427 };
428} // namespace SB::Core
429
430namespace std
431{
432 template <typename T>
433 struct hash<SB::Core::DatabasePtr<T>>
434 {
435 size_t operator()(const SB::Core::DatabasePtr<T>& k) const { return hash<SB_ID>{}(k.id()); }
436 };
437
438 template <typename T>
439 struct hash<SB::Core::ChildPtr<T>>
440 {
441 size_t operator()(const SB::Core::ChildPtr<T>& k) const { return hash<SB_ID>{}(k.id()); }
442 };
443} // namespace std
444
445#include <rttr/wrapper_mapper.h>
446
447namespace rttr
448{
449 template <typename T>
450 struct wrapper_mapper<SB::Core::ChildPtr<T>>
451 {
452 using wrapped_type = decltype(SB::Core::ChildPtr<T>(0).id());
454
455 inline static wrapped_type get(const type& obj) { return obj.id(); }
456
457 inline static type create(const wrapped_type& t) { return type(t); }
458
459 template <typename T2>
460 inline static SB::Core::ChildPtr<T2> convert(const type& source, bool& ok)
461 {
462 SB::Core::ChildPtr<T2> convertedLazyPtr(source.id());
463
464 ok = source.hasId() == convertedLazyPtr.hasId();
465
466 return convertedLazyPtr;
467 }
468 };
469
470 template <typename T>
471 struct wrapper_mapper<SB::Core::DatabasePtr<T>>
472 {
473 using wrapped_type = decltype(SB::Core::DatabasePtr<T>().id());
475
476 inline static wrapped_type get(const type& obj) { return obj.id(); }
477
478 inline static type create(const wrapped_type& t) { return type(t); }
479
480 template <typename T2>
481 inline static SB::Core::DatabasePtr<T2> convert(const type& source, bool& ok)
482 {
483 SB::Core::DatabasePtr<T2> convertedLazyPtr(source.id());
484
485 ok = source.hasId() == convertedLazyPtr.hasId();
486
487 return convertedLazyPtr;
488 }
489 };
490
491} // namespace rttr
Syntactic type to define a pointer that must be a child of the owning object.
Definition database_ptr.h:278
ChildPtr(SB_ID id)
Construct a new ChildPtr that points to the ID.
Definition database_ptr.h:320
ChildPtr(const DatabaseObject &owner)
Construct a new Child Ptr object with an owner.
Definition database_ptr.h:313
ChildPtr()=default
Default constructor is exposed for RTTR but not for the user.
Base object type for any object that can exist in the editor/database. Holds an ID and name.
Definition database_object.h:22
Definition database_ptr.h:23
DatabasePtr()
Creates an empty and null LazyPtr.
Definition database_ptr.h:39
bool null() const
Returns true if the object pointer is not set.
Definition database_ptr.h:114
bool stale() const
Returns true if we previously referenced an object that has been destroyed.
Definition database_ptr.h:128
bool lookup() const
Find the live object referenced by the ID and store it.
Definition database_ptr.h:142
void reset(TObjectPtr object=nullptr)
Clear all references.
Definition database_ptr.h:155
bool pending() const
Returns true if we hold an ID but haven't found the live object to point to yet.
Definition database_ptr.h:121
TObjectPtr raw() const noexcept
Get raw pointer of the referenced object.
Definition database_ptr.h:95
bool valid() const
Returns true if we hold an ID and a valid pointer to the object.
Definition database_ptr.h:135
TObjectPtr operator->() const
Access the raw object.
Definition database_ptr.h:218
DatabasePtr(std::nullptr_t)
Create an empty and null LazyPtr.
Definition database_ptr.h:64
bool hasId() const
Returns true if we hold a valid ID and can search for an object at runtime.
Definition database_ptr.h:108
DatabasePtr(SB_ID id)
Creates a LazyPtr that can lookup its object pointer after construction.
Definition database_ptr.h:49
SB_ID id() const noexcept
Get ID of the referenced object.
Definition database_ptr.h:73
DatabasePtr(const TObjectPtr &object)
Create a valid LazyPtr.
Definition database_ptr.h:55
bool operator!() const
Returns true if this LazyPtr is invalid.
Definition database_ptr.h:212
TThisType & operator=(TObjectPtr object)
Assign this LazyPtr to a new object, potentially destroying the current object if we're acting as a U...
Definition database_ptr.h:171