#pragma once #include #include #include #include #include #include template class Collection { public: Collection() = default; virtual ~Collection() = default; DEFAULT_MOVE_NO_COPY(Collection); using Object = Ptr::element_type; using Objects = std::vector; template using OtherObjects = std::vector; Collection & operator=(Objects && other) { objects = std::move(other); ((std::get>(otherObjects).clear()), ...); for (const auto & other : objects) { addOthersPtr(other.get()); } return *this; } const Ptr & operator[](size_t idx) const { return objects[idx]; } template requires(std::is_same_v || (std::is_base_of_v || ...)) [[nodiscard]] auto size() const noexcept { return containerFor().size(); } template auto create(Params &&... params) requires std::is_base_of_v { if constexpr (requires(Ptr ptr) { ptr = std::make_shared(std::forward(params)...); }) { auto obj = std::make_shared(std::forward(params)...); objects.emplace_back(obj); addOthersType(obj.get()); return obj; } else { auto obj = static_cast( objects.emplace_back(std::make_unique(std::forward(params)...)).get()); addOthersType(obj); return obj; } } template T * find() { const auto & srcObjects = containerFor(); if constexpr (std::is_convertible_v::value_type, T *>) { if (srcObjects.empty()) { return nullptr; } return srcObjects.front(); } else if (auto i = std::find_if(srcObjects.begin(), srcObjects.end(), [](auto && o) { return dynamic_cast(std::to_address(o)) != nullptr; }); i != srcObjects.end()) { return static_cast(std::to_address(*i)); } return nullptr; } template auto findOrCreate(Params &&... params) requires std::is_base_of_v { if (auto o = find()) { return o; } return create(std::forward(params)...).get(); } template auto apply(const auto & m, Params &&... params) const { const auto & srcObjects = containerFor(); return apply_internal(srcObjects.begin(), srcObjects.end(), m, std::forward(params)...); } template auto rapply(const auto & m, Params &&... params) const { const auto & srcObjects = containerFor(); return apply_internal(srcObjects.rbegin(), srcObjects.rend(), m, std::forward(params)...); } template auto applyOne(const auto & m, Params &&... params) const { const auto & srcObjects = containerFor(); return applyOne_internal(srcObjects.begin(), srcObjects.end(), m, std::forward(params)...); } template auto rapplyOne(const auto & m, Params &&... params) const { const auto & srcObjects = containerFor(); return applyOne_internal(srcObjects.rbegin(), srcObjects.rend(), m, std::forward(params)...); } template requires std::is_base_of_v auto removeAll() { auto removeAllFrom = [](auto & container) { if constexpr (std::is_base_of_v>) { const auto size = container.size(); container.clear(); return size; } else { return std::erase_if(container, [](auto && objPtr) -> bool { return dynamic_cast(std::to_address(objPtr)); }); } }; (removeAllFrom(std::get>(otherObjects)), ...); return removeAllFrom(objects); } void clear() { ((std::get>(otherObjects).clear()), ...); objects.clear(); } [[nodiscard]] auto begin() const { return objects.begin(); } [[nodiscard]] auto end() const { return objects.end(); } [[nodiscard]] auto rbegin() const { return objects.rbegin(); } [[nodiscard]] auto rend() const { return objects.rend(); } [[nodiscard]] bool empty() const { return objects.empty(); } decltype(auto) emplace(Ptr && ptr) { const auto & object = objects.emplace_back(std::move(ptr)); addOthersPtr(object.get()); return object; } protected: Objects objects; std::tuple...> otherObjects; template void addOthersType(T * obj) { applyToOthersType( [](auto & others, auto ptr) { others.emplace_back(ptr); }, obj); } void addOthersPtr(Object * obj) { applyToOthersPtr( [](auto & others, auto ptr) { others.emplace_back(ptr); }, obj); } template requires(sizeof...(Others) == 0) void applyToOthersType(const auto &, Params...) { } void applyToOthersPtr(const auto &, Object *) requires(sizeof...(Others) == 0) { } template requires(sizeof...(Others) > 0) void applyToOthersType(const auto & func, Params &&... params) { ( [&]() { if constexpr (std::is_convertible_v) { std::invoke( func, std::get>(otherObjects), std::forward(params)...); } }(), ...); } void applyToOthersPtr(const auto & func, Object * obj) requires(sizeof...(Others) > 0) { ( [&]() { if (auto ptr = dynamic_cast(obj)) { std::invoke(func, std::get>(otherObjects), ptr); } }(), ...); } template requires((std::is_base_of_v || ...)) [[nodiscard]] consteval static size_t idx() { size_t typeIdx = 0; auto found = ((++typeIdx && std::is_base_of_v) || ...); return typeIdx - found; } template [[nodiscard]] constexpr const auto & containerFor() const { if constexpr ((std::is_base_of_v || ...)) { return std::get()>(otherObjects); } else { return objects; } } template auto apply_internal(const auto begin, const auto end, const auto & m, Params &&... params) const { if constexpr (std::is_convertible_v) { std::for_each(begin, end, [&m, ¶ms...](auto && op) { std::invoke(m, op, std::forward(params)...); }); return std::distance(begin, end); } else { return std::count_if(begin, end, [&m, ¶ms...](auto && op) { if (auto o = dynamic_cast(std::to_address(op))) { std::invoke(m, o, std::forward(params)...); return true; } return false; }); } } template auto applyOne_internal(const auto begin, const auto end, const auto & m, Params &&... params) const { if constexpr (std::is_convertible_v) { return std::find_if(begin, end, [&m, ¶ms...](auto && op) { return std::invoke(m, op, std::forward(params)...); }); } else { return std::find_if(begin, end, [&m, ¶ms...](auto && op) { if (auto o = dynamic_cast(std::to_address(op))) { return std::invoke(m, o, std::forward(params)...); } return false; }); } } }; template using SharedCollection = Collection, Others...>; template using UniqueCollection = Collection, Others...>;