neobytes/src/immer/box.hpp
Alexander Block a370bbfe3d Update immer library to current master (0a718d2d76bab6ebdcf43de943bd6c7d2dbfe2f9) (#2821)
* Update immer library to current master (0a718d2d76bab6ebdcf43de943bd6c7d2dbfe2f9)

* Temporary fix for alignof(std::max_align_t) on MinGW 32bit builds

See https://github.com/arximboldi/immer/issues/78
2019-04-01 14:10:49 +03:00

173 lines
4.6 KiB
C++

//
// 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. */
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. */
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.
bool operator!=(detail::exact_t<const box&> other) const
{ return !(*this == other.value); }
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>
box update(Fn&& fn) const&
{
return std::forward<Fn>(fn)(get());
}
template <typename Fn>
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