46 static std::shared_ptr<C>
Lock(
const std::shared_ptr<C>& ptr) {
55 static bool IsExpired(
const std::shared_ptr<C>& ptr) {
56 return ptr ==
nullptr;
64 static std::shared_ptr<C>
Store(
const std::shared_ptr<C>& ptr) {
81 static std::shared_ptr<C>
Lock(
const std::weak_ptr<C>& ptr) {
90 static bool IsExpired(
const std::weak_ptr<C>& ptr) {
99 static std::weak_ptr<C>
Store(
const std::shared_ptr<C>& ptr) {
114template <
typename C = Part,
typename StoredPtr = std::shared_ptr<C>>
class PartList :
public Part {
116 static_assert(std::is_same<StoredPtr, std::shared_ptr<C>>::value ||
117 std::is_same<StoredPtr, std::weak_ptr<C>>::value,
118 "StoredPtr must be std::shared_ptr<C> or std::weak_ptr<C>");
124 explicit PartList(
const size_t initialAllocation = 0);
132 bool Has(std::shared_ptr<C> part)
const;
139 template <
typename T>
bool Has()
const;
146 bool Has(
const uint64_t
id)
const;
161 return mMutationVersion;
169 std::shared_ptr<C>
Get(
const uint64_t
id)
const;
172 std::shared_ptr<std::vector<std::shared_ptr<C>>>
Get()
const;
179 template <
typename T> std::shared_ptr<std::vector<std::shared_ptr<T>>>
Get()
const;
186 std::shared_ptr<std::vector<std::shared_ptr<C>>>
Get(
const std::vector<uint64_t>& ids)
const;
193 template <
typename T> std::shared_ptr<T>
GetFirst()
const;
264 template <
typename P = StoredPtr>
265 typename std::enable_if<std::is_same<P, std::shared_ptr<C>>::value, std::vector<P>&>::type
GetList();
272 template <
typename P = StoredPtr>
273 typename std::enable_if<std::is_same<P, std::shared_ptr<C>>::value,
const std::vector<P>&>::type
GetList()
const;
275#ifdef COMPONENT_THREAD_SAFE
290 virtual bool CanAdd(std::shared_ptr<C> part);
309 virtual void Added(std::shared_ptr<C> part,
const bool forced);
316 virtual void Removed(std::shared_ptr<C> part,
const bool forced);
321 std::shared_ptr<C> LockPtr(
const StoredPtr& ptr)
const;
322 StoredPtr StorePtr(
const std::shared_ptr<C>& ptr)
const;
323 bool IsExpiredPtr(
const StoredPtr& ptr)
const;
325 bool ContainsIdUnlocked(
const uint64_t
id)
const;
326 bool EraseByIdUnlocked(
const uint64_t
id, std::shared_ptr<C>* removedPart =
nullptr);
328 std::vector<StoredPtr> mList;
329 uint64_t mMutationVersion = 0;
330#ifdef COMPONENT_THREAD_SAFE
331 mutable std::recursive_mutex mMutex;
337template <
typename C,
typename StoredPtr>
340#ifdef COMPONENT_THREAD_SAFE
345 mList.reserve(initialAllocation);
348template <
typename C,
typename StoredPtr>
350 return PtrTraits::Lock(ptr);
353template <
typename C,
typename StoredPtr>
354StoredPtr PartList<C, StoredPtr>::StorePtr(
const std::shared_ptr<C>& ptr)
const {
355 return PtrTraits::Store(ptr);
358template <
typename C,
typename StoredPtr>
bool PartList<C, StoredPtr>::IsExpiredPtr(
const StoredPtr& ptr)
const {
359 return PtrTraits::IsExpired(ptr);
362template <
typename C,
typename StoredPtr>
void PartList<C, StoredPtr>::PruneExpired() {
363 const auto oldSize = mList.size();
364 mList.erase(std::remove_if(mList.begin(), mList.end(), [
this](
const StoredPtr& ptr) { return IsExpiredPtr(ptr); }),
366 if (mList.size() != oldSize) {
371template <
typename C,
typename StoredPtr>
bool PartList<C, StoredPtr>::ContainsIdUnlocked(
const uint64_t
id)
const {
372 return std::find_if(mList.begin(), mList.end(), [
this,
id](
const StoredPtr& item) {
373 auto locked = LockPtr(item);
374 return locked && locked->GetId() == id;
378template <
typename C,
typename StoredPtr>
379bool PartList<C, StoredPtr>::EraseByIdUnlocked(
const uint64_t
id, std::shared_ptr<C>* removedPart) {
380 auto it = std::find_if(mList.begin(), mList.end(), [
this,
id](
const StoredPtr& item) {
381 auto locked = LockPtr(item);
382 return locked && locked->GetId() == id;
385 if (it == mList.end()) {
389 if (removedPart !=
nullptr) {
390 *removedPart = LockPtr(*it);
398template <
typename C,
typename StoredPtr>
404 const bool canAdd = CanAdd(part);
405 if (!canAdd && !force) {
409 const bool forced = !canAdd && force;
410 const uint64_t
id = part->GetId();
413#ifdef COMPONENT_THREAD_SAFE
414 const std::lock_guard<std::recursive_mutex> lock(mMutex);
417 if (ContainsIdUnlocked(
id)) {
420 mList.push_back(StorePtr(part));
424 bool shouldRunHooks =
false;
426#ifdef COMPONENT_THREAD_SAFE
427 const std::lock_guard<std::recursive_mutex> lock(mMutex);
429 shouldRunHooks = ContainsIdUnlocked(
id);
432 if (!shouldRunHooks) {
438 part->OnAdded(forced);
440#ifdef COMPONENT_THREAD_SAFE
441 const std::lock_guard<std::recursive_mutex> lock(mMutex);
444 EraseByIdUnlocked(
id);
451template <
typename C,
typename StoredPtr>
458 for (
const auto& part : parts) {
460 if (
static_cast<int32_t
>(r) >
static_cast<int32_t
>(result)) {
463 static_cast<int32_t
>(r) <
static_cast<int32_t
>(result)) {
470template <
typename C,
typename StoredPtr>
476 const bool canRemove = CanRemove(part);
477 if (!canRemove && !force) {
481 const bool forced = !canRemove && force;
482 const uint64_t
id = part->GetId();
483 std::shared_ptr<C> removedPart =
nullptr;
486#ifdef COMPONENT_THREAD_SAFE
487 const std::lock_guard<std::recursive_mutex> lock(mMutex);
490 if (!EraseByIdUnlocked(
id, &removedPart)) {
499 bool shouldRunHooks =
false;
501#ifdef COMPONENT_THREAD_SAFE
502 const std::lock_guard<std::recursive_mutex> lock(mMutex);
504 shouldRunHooks = !ContainsIdUnlocked(
id);
507 if (!shouldRunHooks) {
512 Removed(removedPart, forced);
513 removedPart->OnRemoved(forced);
515#ifdef COMPONENT_THREAD_SAFE
516 const std::lock_guard<std::recursive_mutex> lock(mMutex);
519 if (!ContainsIdUnlocked(
id)) {
520 mList.push_back(StorePtr(removedPart));
529template <
typename C,
typename StoredPtr>
531 std::shared_ptr<C> candidate =
nullptr;
533#ifdef COMPONENT_THREAD_SAFE
534 const std::lock_guard<std::recursive_mutex> lock(mMutex);
537 if (!ContainsIdUnlocked(
id)) {
540 auto found = std::find_if(mList.begin(), mList.end(), [
this,
id](
const StoredPtr& item) {
541 auto locked = LockPtr(item);
542 return locked && locked->GetId() == id;
544 candidate = found != mList.end() ? LockPtr(*found) :
nullptr;
551 const bool canRemove = CanRemove(candidate);
552 if (!canRemove && !force) {
556 const bool forced = !canRemove && force;
557 std::shared_ptr<C> removedPart =
nullptr;
560#ifdef COMPONENT_THREAD_SAFE
561 const std::lock_guard<std::recursive_mutex> lock(mMutex);
564 if (!EraseByIdUnlocked(
id, &removedPart)) {
573 bool shouldRunHooks =
false;
575#ifdef COMPONENT_THREAD_SAFE
576 const std::lock_guard<std::recursive_mutex> lock(mMutex);
578 shouldRunHooks = !ContainsIdUnlocked(
id);
581 if (!shouldRunHooks) {
586 Removed(removedPart, forced);
587 removedPart->OnRemoved(forced);
589#ifdef COMPONENT_THREAD_SAFE
590 const std::lock_guard<std::recursive_mutex> lock(mMutex);
593 if (!ContainsIdUnlocked(
id)) {
594 mList.push_back(StorePtr(removedPart));
604 std::vector<std::shared_ptr<C>> snapshot;
606#ifdef COMPONENT_THREAD_SAFE
607 const std::lock_guard<std::recursive_mutex> lock(mMutex);
614 snapshot.reserve(mList.size());
615 for (
const auto& item : mList) {
616 auto locked = LockPtr(item);
618 snapshot.push_back(locked);
624 for (
const auto& part : snapshot) {
626 if (
static_cast<int32_t
>(r) >
static_cast<int32_t
>(result)) {
629 static_cast<int32_t
>(r) <
static_cast<int32_t
>(result)) {
636template <
typename C,
typename StoredPtr>
643 for (
const auto& part : parts) {
645 if (
static_cast<int32_t
>(r) >
static_cast<int32_t
>(result)) {
648 static_cast<int32_t
>(r) <
static_cast<int32_t
>(result)) {
655template <
typename C,
typename StoredPtr>
658 std::vector<std::shared_ptr<C>> snapshot;
660#ifdef COMPONENT_THREAD_SAFE
661 const std::lock_guard<std::recursive_mutex> lock(mMutex);
665 snapshot.reserve(mList.size());
666 for (
const auto& item : mList) {
667 auto locked = LockPtr(item);
668 if (locked && std::dynamic_pointer_cast<T>(locked) !=
nullptr) {
669 snapshot.push_back(locked);
674 if (snapshot.empty()) {
679 for (
const auto& part : snapshot) {
681 if (
static_cast<int32_t
>(r) >
static_cast<int32_t
>(result)) {
684 static_cast<int32_t
>(r) <
static_cast<int32_t
>(result)) {
691template <
typename C,
typename StoredPtr>
697 std::vector<std::shared_ptr<C>> snapshot;
699#ifdef COMPONENT_THREAD_SAFE
700 const std::lock_guard<std::recursive_mutex> lock(mMutex);
704 snapshot.reserve(mList.size());
705 for (
const auto& item : mList) {
706 auto locked = LockPtr(item);
707 if (locked && std::find(ids.begin(), ids.end(), locked->GetId()) != ids.end()) {
708 snapshot.push_back(locked);
713 if (snapshot.empty()) {
718 for (
const auto& part : snapshot) {
720 if (
static_cast<int32_t
>(r) >
static_cast<int32_t
>(result)) {
723 static_cast<int32_t
>(r) <
static_cast<int32_t
>(result)) {
730template <
typename C,
typename StoredPtr>
732typename std::enable_if<std::is_same<P, std::shared_ptr<C>>::value, std::vector<P>&>::type
737template <
typename C,
typename StoredPtr>
739typename std::enable_if<std::is_same<P, std::shared_ptr<C>>::value,
const std::vector<P>&>::type
744#ifdef COMPONENT_THREAD_SAFE
758template <
typename C,
typename StoredPtr>
762template <
typename C,
typename StoredPtr>
770#ifdef COMPONENT_THREAD_SAFE
771 const std::lock_guard<std::recursive_mutex> lock(mMutex);
774 return ContainsIdUnlocked(part->GetId());
778#ifdef COMPONENT_THREAD_SAFE
779 const std::lock_guard<std::recursive_mutex> lock(mMutex);
782 return std::find_if(mList.begin(), mList.end(), [
this](
const StoredPtr& item) {
783 auto locked = LockPtr(item);
784 return locked && std::dynamic_pointer_cast<T>(locked) != nullptr;
789#ifdef COMPONENT_THREAD_SAFE
790 const std::lock_guard<std::recursive_mutex> lock(mMutex);
793 return ContainsIdUnlocked(
id);
797#ifdef COMPONENT_THREAD_SAFE
798 const std::lock_guard<std::recursive_mutex> lock(mMutex);
801 return !mList.empty();
805#ifdef COMPONENT_THREAD_SAFE
806 const std::lock_guard<std::recursive_mutex> lock(mMutex);
813#ifdef COMPONENT_THREAD_SAFE
814 const std::lock_guard<std::recursive_mutex> lock(mMutex);
817 auto it = std::find_if(mList.begin(), mList.end(), [
this,
id](
const StoredPtr& item) {
818 auto locked = LockPtr(item);
819 return locked && locked->GetId() == id;
821 return it != mList.end() ? LockPtr(*it) :
nullptr;
824template <
typename C,
typename StoredPtr>
826#ifdef COMPONENT_THREAD_SAFE
827 const std::lock_guard<std::recursive_mutex> lock(mMutex);
830 auto result = std::make_shared<std::vector<std::shared_ptr<C>>>();
831 result->reserve(mList.size());
832 for (
const auto& item : mList) {
833 auto locked = LockPtr(item);
835 result->push_back(locked);
841template <
typename C,
typename StoredPtr>
844#ifdef COMPONENT_THREAD_SAFE
845 const std::lock_guard<std::recursive_mutex> lock(mMutex);
848 auto result = std::make_shared<std::vector<std::shared_ptr<T>>>();
849 for (
const auto& item : mList) {
850 auto locked = LockPtr(item);
854 auto typed = std::dynamic_pointer_cast<T>(locked);
856 result->push_back(typed);
862template <
typename C,
typename StoredPtr>
865#ifdef COMPONENT_THREAD_SAFE
866 const std::lock_guard<std::recursive_mutex> lock(mMutex);
869 for (
const auto& item : mList) {
870 auto locked = LockPtr(item);
874 auto typed = std::dynamic_pointer_cast<T>(locked);
882template <
typename C,
typename StoredPtr>
884#ifdef COMPONENT_THREAD_SAFE
885 const std::lock_guard<std::recursive_mutex> lock(mMutex);
888 auto result = std::make_shared<std::vector<std::shared_ptr<C>>>();
889 for (
const auto& item : mList) {
890 auto locked = LockPtr(item);
894 if (std::find(ids.begin(), ids.end(), locked->GetId()) != ids.end()) {
895 result->push_back(locked);
A thread-safe ordered list of Parts.
Definition PartList.h:114
PartList(const size_t initialAllocation=0)
Constructs a PartList, optionally pre-allocating storage.
Definition PartList.h:338
ListReturnCode Remove(std::shared_ptr< C > part, const bool force=false)
Removes a specific Part from the list.
Definition PartList.h:471
ListReturnCode Remove(const bool force=false)
Removes all Parts that can be dynamic_cast to type T.
Definition PartList.h:657
bool Has() const
Checks whether any Part of type T is in the list.
Definition PartList.h:777
ListReturnCode Remove(const uint64_t id, const bool force=false)
Removes a Part by its unique ID.
Definition PartList.h:530
std::recursive_mutex & GetMutex() const
Returns the internal recursive mutex used to guard list access.
Definition PartList.h:745
std::shared_ptr< std::vector< std::shared_ptr< C > > > Get(const std::vector< uint64_t > &ids) const
Returns all Parts whose IDs appear in the given vector.
Definition PartList.h:883
size_t GetCount() const
Returns the number of currently live Parts in the list.
Definition PartList.h:804
bool Has() const
Checks whether the list contains any Parts at all.
Definition PartList.h:796
std::shared_ptr< std::vector< std::shared_ptr< C > > > Get() const
Returns a snapshot (copy) of all currently live Parts in the list.
Definition PartList.h:825
ListReturnCode Remove(const std::vector< uint64_t > &ids, const bool force=false)
Removes all Parts whose IDs appear in the given vector.
Definition PartList.h:692
virtual void Added(std::shared_ptr< C > part, const bool forced)
Notification hook called after a Part has been added.
Definition PartList.h:759
std::shared_ptr< std::vector< std::shared_ptr< T > > > Get() const
Returns all Parts that can be dynamic_cast to type T.
Definition PartList.h:843
ListReturnCode Add(const std::vector< std::shared_ptr< C > > &parts, const bool force=false)
Adds multiple Parts to the list.
Definition PartList.h:452
virtual void Removed(std::shared_ptr< C > part, const bool forced)
Notification hook called after a Part has been removed.
Definition PartList.h:763
uint64_t GetMutationVersion() const
Returns a monotonically increasing counter that increments on every add or remove.
Definition PartList.h:160
bool Has(std::shared_ptr< C > part) const
Checks whether a specific Part is in the list.
Definition PartList.h:766
ListReturnCode Remove(const std::vector< std::shared_ptr< C > > &parts, const bool force=false)
Removes multiple Parts from the list.
Definition PartList.h:637
virtual ~PartList()=default
ListReturnCode Remove(const bool force=false)
Removes all Parts from the list.
Definition PartList.h:603
virtual bool CanAdd(std::shared_ptr< C > part)
Permission hook called before adding a Part. Override to deny.
Definition PartList.h:750
ListReturnCode Add(std::shared_ptr< C > part, const bool force=false)
Adds a Part to the list if not already present.
Definition PartList.h:399
virtual bool CanRemove(std::shared_ptr< C > part)
Permission hook called before removing a Part. Override to deny.
Definition PartList.h:754
std::enable_if< std::is_same< P, std::shared_ptr< C > >::value, conststd::vector< P > & >::type GetList() const
Direct const access to the underlying vector for strong-storage lists.
Definition PartList.h:740
std::enable_if< std::is_same< P, std::shared_ptr< C > >::value, std::vector< P > & >::type GetList()
Direct access to the underlying vector for strong-storage lists.
Definition PartList.h:733
std::shared_ptr< C > Get(const uint64_t id) const
Retrieves a Part by its unique ID.
Definition PartList.h:812
std::shared_ptr< T > GetFirst() const
Returns the first Part that can be dynamic_cast to T.
Definition PartList.h:864
bool Has(const uint64_t id) const
Checks whether a Part with the given ID is in the list.
Definition PartList.h:788
Base class for all identifiable objects in the component system.
Definition Part.h:27
friend class PartList
Definition Part.h:86
Core namespace for the libultraship engine framework.
Definition gfx_direct3d_common.h:14
ListReturnCode
Return codes for PartList add/remove operations.
Definition PartList.h:22
@ ForcedSuccess
Operation succeeded via force override.
@ NotFound
The specified Part was not found.
@ Success
Operation succeeded normally.
@ NoItemsProvided
The input collection was empty.
@ NotPermitted
Operation blocked by a permission check.
@ Failed
General failure (e.g. null pointer).
@ Duplicate
Part already present; list unchanged.
static std::shared_ptr< C > Store(const std::shared_ptr< C > &ptr)
Converts an API pointer into strong stored form.
Definition PartList.h:64
static bool IsExpired(const std::shared_ptr< C > &ptr)
Indicates whether the stored pointer is considered expired.
Definition PartList.h:55
static std::shared_ptr< C > Lock(const std::shared_ptr< C > &ptr)
Returns the stored pointer unchanged.
Definition PartList.h:46
static std::weak_ptr< C > Store(const std::shared_ptr< C > &ptr)
Converts an API pointer into weak stored form.
Definition PartList.h:99
static bool IsExpired(const std::weak_ptr< C > &ptr)
Indicates whether the weak pointer is expired.
Definition PartList.h:90
static std::shared_ptr< C > Lock(const std::weak_ptr< C > &ptr)
Locks a weak pointer.
Definition PartList.h:81