From c2a5894f2dbe9982830066ab9347b059e6e7d845 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Tue, 25 Apr 2017 18:20:26 -0700 Subject: [core] Immutable Impls --- include/mbgl/util/immutable.hpp | 127 ++++++++++++++++++++++++++++++++++++++++ include/mbgl/util/tileset.hpp | 5 ++ 2 files changed, 132 insertions(+) create mode 100644 include/mbgl/util/immutable.hpp (limited to 'include/mbgl/util') diff --git a/include/mbgl/util/immutable.hpp b/include/mbgl/util/immutable.hpp new file mode 100644 index 0000000000..1d6ff4079a --- /dev/null +++ b/include/mbgl/util/immutable.hpp @@ -0,0 +1,127 @@ +#pragma once + +#include + +namespace mbgl { + +/** + * `Mutable` is a non-nullable uniquely owning reference to a `T`. It can be efficiently converted + * to `Immutable`. + * + * The lifecycle of `Mutable` and `Immutable` is as follows: + * + * 1. Create a `Mutable` using `makeMutable(...)` + * 2. Mutate it freely + * 3. When you're ready to freeze its state and enable safe cross-thread sharing, move assign or + * move construct it to `Immutable` + * + * The reason that `Mutable` exists, rather than simply using a `std::unique_ptr`, is to take advantage + * of the underlying single-allocation optimization provided by `std::make_shared`. + */ +template +class Mutable { +public: + Mutable(Mutable&&) = default; + Mutable& operator=(Mutable&&) = default; + + Mutable(const Mutable&) = delete; + Mutable& operator=(const Mutable&) = delete; + + T* get() { return ptr.get(); } + T* operator->() { return ptr.get(); } + T& operator*() { return *ptr; } + +private: + Mutable(std::shared_ptr&& s) + : ptr(std::move(s)) {} + + std::shared_ptr ptr; + + template friend class Immutable; + template friend Mutable makeMutable(Args&&...); +}; + +template +Mutable makeMutable(Args&&... args) { + return Mutable(std::make_shared(std::forward(args)...)); +} + +/** + * `Immutable` is a non-nullable shared reference to a `const T`. Construction requires + * a transfer of unique ownership from a `Mutable`; once constructed it has the same behavior + * as `std::shared_ptr` but with better indication of intent. + * + * Normally one should not share state between threads because it's difficult to verify the + * absence of read/write data races. `Immutable` provides a guarantee that no writes are + * possible, and instances therefore can be freely transferred and shared between threads. + */ +template +class Immutable { +public: + template + Immutable(Mutable&& s) + : ptr(std::const_pointer_cast(std::move(s.ptr))) {} + + template + Immutable(Immutable&& s) + : ptr(std::move(s.ptr)) {} + + template + Immutable(const Immutable& s) + : ptr(s.ptr) {} + + template + Immutable& operator=(Mutable&& s) { + ptr = std::const_pointer_cast(std::move(s.ptr)); + return *this; + } + + template + Immutable& operator=(Immutable&& s) { + ptr = std::move(s.ptr); + return *this; + } + + template + Immutable& operator=(const Immutable& s) { + ptr = s.ptr; + return *this; + } + + const T* get() const { return ptr.get(); } + const T* operator->() const { return ptr.get(); } + const T& operator*() const { return *ptr; } + + friend bool operator==(const Immutable& lhs, const Immutable& rhs) { + return lhs.ptr == rhs.ptr; + } + + friend bool operator!=(const Immutable& lhs, const Immutable& rhs) { + return lhs.ptr != rhs.ptr; + } + +private: + Immutable(std::shared_ptr&& s) + : ptr(std::move(s)) {} + + std::shared_ptr ptr; + + template friend class Immutable; + template friend class EnableImmutableFromThis; + template friend Immutable staticImmutableCast(const Immutable&); +}; + +template +class EnableImmutableFromThis : public std::enable_shared_from_this { +public: + Immutable immutableFromThis() const { + return Immutable(this->shared_from_this()); + } +}; + +template +Immutable staticImmutableCast(const Immutable& u) { + return Immutable(std::static_pointer_cast(u.ptr)); +} + +} // namespace mbgl diff --git a/include/mbgl/util/tileset.hpp b/include/mbgl/util/tileset.hpp index 1f28a5039a..1256e9fe96 100644 --- a/include/mbgl/util/tileset.hpp +++ b/include/mbgl/util/tileset.hpp @@ -18,6 +18,11 @@ public: Scheme scheme = Scheme::XYZ; // TileJSON also includes center, zoom, and bounds, but they are not used by mbgl. + + friend bool operator==(const Tileset& lhs, const Tileset& rhs) { + return std::tie(lhs.tiles, lhs.zoomRange, lhs.attribution, lhs.scheme) + == std::tie(rhs.tiles, rhs.zoomRange, rhs.attribution, rhs.scheme); + } }; } // namespace mbgl -- cgit v1.2.1