about summary refs log tree commit diff
path: root/immer/box.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'immer/box.hpp')
-rw-r--r--immer/box.hpp194
1 files changed, 194 insertions, 0 deletions
diff --git a/immer/box.hpp b/immer/box.hpp
new file mode 100644
index 000000000000..0fd2961272ca
--- /dev/null
+++ b/immer/box.hpp
@@ -0,0 +1,194 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/util.hpp>
+#include <immer/memory_policy.hpp>
+
+namespace immer {
+
+namespace detail {
+
+template <typename U, typename MP>
+struct gc_atom_impl;
+
+template <typename U, typename MP>
+struct refcount_atom_impl;
+
+} // namespace detail
+
+/*!
+ * Immutable box for a single value of type `T`.
+ *
+ * The box is always copiable and movable. The `T` copy or move
+ * operations are never called.  Since a box is immutable, copying or
+ * moving just copy the underlying pointers.
+ */
+template <typename T, typename MemoryPolicy = default_memory_policy>
+class box
+{
+    friend struct detail::gc_atom_impl<T, MemoryPolicy>;
+    friend struct detail::refcount_atom_impl<T, MemoryPolicy>;
+
+    struct holder : MemoryPolicy::refcount
+    {
+        T value;
+
+        template <typename... Args>
+        holder(Args&&... args)
+            : value{std::forward<Args>(args)...}
+        {}
+    };
+
+    using heap = typename MemoryPolicy::heap::type;
+
+    holder* impl_ = nullptr;
+
+    box(holder* impl)
+        : impl_{impl}
+    {}
+
+public:
+    using value_type    = T;
+    using memory_policy = MemoryPolicy;
+
+    /*!
+     * Constructs a box holding `T{}`.
+     */
+    box()
+        : impl_{detail::make<heap, holder>()}
+    {}
+
+    /*!
+     * Constructs a box holding `T{arg}`
+     */
+    template <typename Arg,
+              typename Enable = std::enable_if_t<
+                  !std::is_same<box, std::decay_t<Arg>>::value &&
+                  std::is_constructible<T, Arg>::value>>
+    box(Arg&& arg)
+        : impl_{detail::make<heap, holder>(std::forward<Arg>(arg))}
+    {}
+
+    /*!
+     * Constructs a box holding `T{arg1, arg2, args...}`
+     */
+    template <typename Arg1, typename Arg2, typename... Args>
+    box(Arg1&& arg1, Arg2&& arg2, Args&&... args)
+        : impl_{detail::make<heap, holder>(std::forward<Arg1>(arg1),
+                                           std::forward<Arg2>(arg2),
+                                           std::forward<Args>(args)...)}
+    {}
+
+    friend void swap(box& a, box& b)
+    {
+        using std::swap;
+        swap(a.impl_, b.impl_);
+    }
+
+    box(box&& other) { swap(*this, other); }
+    box(const box& other)
+        : impl_(other.impl_)
+    {
+        impl_->inc();
+    }
+    box& operator=(box&& other)
+    {
+        swap(*this, other);
+        return *this;
+    }
+    box& operator=(const box& other)
+    {
+        auto aux = other;
+        swap(*this, aux);
+        return *this;
+    }
+    ~box()
+    {
+        if (impl_ && impl_->dec()) {
+            impl_->~holder();
+            heap::deallocate(sizeof(holder), impl_);
+        }
+    }
+
+    /*! Query the current value. */
+    IMMER_NODISCARD const T& get() const { return impl_->value; }
+
+    /*! Conversion to the boxed type. */
+    operator const T&() const { return get(); }
+
+    /*! Access via dereference */
+    const T& operator*() const { return get(); }
+
+    /*! Access via pointer member access */
+    const T* operator->() const { return &get(); }
+
+    /*! Comparison. */
+    IMMER_NODISCARD bool operator==(detail::exact_t<const box&> other) const
+    {
+        return impl_ == other.value.impl_ || get() == other.value.get();
+    }
+    // Note that the `exact_t` disambiguates comparisons against `T{}`
+    // directly.  In that case we want to use `operator T&` and
+    // compare directly.  We definitely never want to convert a value
+    // to a box (which causes an allocation) just to compare it.
+    IMMER_NODISCARD bool operator!=(detail::exact_t<const box&> other) const
+    {
+        return !(*this == other.value);
+    }
+    IMMER_NODISCARD bool operator<(detail::exact_t<const box&> other) const
+    {
+        return get() < other.value.get();
+    }
+
+    /*!
+     * Returns a new box built by applying the `fn` to the underlying
+     * value.
+     *
+     * @rst
+     *
+     * **Example**
+     *   .. literalinclude:: ../example/box/box.cpp
+     *      :language: c++
+     *      :dedent: 8
+     *      :start-after: update/start
+     *      :end-before:  update/end
+     *
+     * @endrst
+     */
+    template <typename Fn>
+    IMMER_NODISCARD box update(Fn&& fn) const&
+    {
+        return std::forward<Fn>(fn)(get());
+    }
+    template <typename Fn>
+    IMMER_NODISCARD box&& update(Fn&& fn) &&
+    {
+        if (impl_->unique())
+            impl_->value = std::forward<Fn>(fn)(std::move(impl_->value));
+        else
+            *this = std::forward<Fn>(fn)(impl_->value);
+        return std::move(*this);
+    }
+};
+
+} // namespace immer
+
+namespace std {
+
+template <typename T, typename MP>
+struct hash<immer::box<T, MP>>
+{
+    std::size_t operator()(const immer::box<T, MP>& x) const
+    {
+        return std::hash<T>{}(*x);
+    }
+};
+
+} // namespace std