#pragma once #include #include #include #include namespace mbgl { namespace style { /* Manages an ordered collection of elements and their `Immutable`s. The latter is itself stored in an Immutable container. Using immutability at the collection level allows us to short-circuit significant portions of the RenderStyle update logic via a simple pointer equality check, greatly improving performance. Element types are required to have: * An `Impl` inner class type * An `Immutable baseImpl` member * A `std::string getID() const` method */ template class Collection { public: using Impl = typename T::Impl; using WrapperVector = std::vector>; using ImmutableVector = Immutable>>; Collection(); std::size_t size() const; T* get(const std::string&) const; std::vector getWrappers() const; ImmutableVector getImpls() const { return impls; } auto begin() const { return wrappers.begin(); } auto end() const { return wrappers.end(); } void clear(); T* add(std::unique_ptr, const optional& = {}); std::unique_ptr remove(const std::string&); // Must be called whenever an element of the collection is internally mutated. // Typically, each element permits registration of an observer, and the observer // should call this method. void update(const T&); private: std::size_t index(const std::string&) const; WrapperVector wrappers; ImmutableVector impls; }; template Collection::Collection() : impls(makeMutable>>()) { } template std::size_t Collection::size() const { return wrappers.size(); } template std::size_t Collection::index(const std::string& id) const { return std::find_if(wrappers.begin(), wrappers.end(), [&](const auto& e) { return e->getID() == id; }) - wrappers.begin(); } template T* Collection::get(const std::string& id) const { std::size_t i = index(id); return i < size() ? wrappers[i].get() : nullptr; } template std::vector Collection::getWrappers() const { std::vector result; result.reserve(wrappers.size()); for (auto& wrapper : wrappers) { result.push_back(wrapper.get()); } return result; } template void Collection::clear() { mutate(impls, [&] (auto& impls_) { impls_.clear(); }); wrappers.clear(); } template T* Collection::add(std::unique_ptr wrapper, const optional& before) { std::size_t i = before ? index(*before) : size(); mutate(impls, [&] (auto& impls_) { impls_.emplace(impls_.begin() + i, wrapper->baseImpl); }); return wrappers.emplace(wrappers.begin() + i, std::move(wrapper))->get(); } template std::unique_ptr Collection::remove(const std::string& id) { std::size_t i = index(id); if (i >= size()) { return nullptr; } auto source = std::move(wrappers[i]); mutate(impls, [&] (auto& impls_) { impls_.erase(impls_.begin() + i); }); wrappers.erase(wrappers.begin() + i); return source; } template void Collection::update(const T& wrapper) { mutate(impls, [&] (auto& impls_) { impls_.at(this->index(wrapper.getID())) = wrapper.baseImpl; }); } } // namespace style } // namespace mbgl