// // 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 #include namespace immer { namespace detail { template struct gc_atom_impl; template 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 class box { friend struct detail::gc_atom_impl; friend struct detail::refcount_atom_impl; struct holder : MemoryPolicy::refcount { T value; template holder(Args&&... args) : value{std::forward(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()} {} /*! * Constructs a box holding `T{arg}` */ template >::value && std::is_constructible::value>> box(Arg&& arg) : impl_{detail::make(std::forward(arg))} {} /*! * Constructs a box holding `T{arg1, arg2, args...}` */ template box(Arg1&& arg1, Arg2&& arg2, Args&&... args) : impl_{detail::make(std::forward(arg1), std::forward(arg2), std::forward(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 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 other) const { return !(*this == other.value); } IMMER_NODISCARD bool operator<(detail::exact_t 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 IMMER_NODISCARD box update(Fn&& fn) const& { return std::forward(fn)(get()); } template IMMER_NODISCARD box&& update(Fn&& fn) && { if (impl_->unique()) impl_->value = std::forward(fn)(std::move(impl_->value)); else *this = std::forward(fn)(impl_->value); return std::move(*this); } }; } // namespace immer namespace std { template struct hash> { std::size_t operator()(const immer::box& x) const { return std::hash{}(*x); } }; } // namespace std