Permalink
Fetching contributors…
Cannot retrieve contributors at this time
217 lines (156 sloc) 6.44 KB
/*
Copyright 2013 Adobe
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
/**************************************************************************************************/
#ifndef STLAB_COPY_ON_WRITE_HPP
#define STLAB_COPY_ON_WRITE_HPP
/**************************************************************************************************/
#include <atomic>
#include <cassert>
#include <cstddef>
#include <utility>
/**************************************************************************************************/
namespace stlab {
/**************************************************************************************************/
template <typename T> // T models Regular
class copy_on_write {
struct model {
std::atomic<std::size_t> _count{1};
model() = default;
template <class... Args>
explicit model(Args&&... args) : _value(std::forward<Args>(args)...) {}
T _value;
};
model* _self;
template <class U>
using disable_copy = std::enable_if_t<!std::is_same<std::decay_t<U>, copy_on_write>::value>*;
template <typename U>
using disable_copy_assign =
std::enable_if_t<!std::is_same<std::decay_t<U>, copy_on_write>::value, copy_on_write&>;
public:
/* [[deprecated]] */ using value_type = T;
using element_type = T;
copy_on_write() {
static model default_s;
_self = &default_s;
// coverity[useless_call]
++_self->_count;
}
template <class U>
copy_on_write(U&& x, disable_copy<U> = nullptr) : _self(new model(std::forward<U>(x))) {}
template <class U, class V, class... Args>
copy_on_write(U&& x, V&& y, Args&&... args)
: _self(new model(std::forward<U>(x), std::forward<V>(y), std::forward<Args>(args)...)) {}
copy_on_write(const copy_on_write& x) noexcept : _self(x._self) {
assert(_self && "FATAL (sparent) : using a moved copy_on_write object");
// coverity[useless_call]
++_self->_count;
}
copy_on_write(copy_on_write&& x) noexcept : _self(x._self) {
assert(_self && "WARNING (sparent) : using a moved copy_on_write object");
x._self = nullptr;
}
~copy_on_write() {
if (_self && (--_self->_count == 0)) delete _self;
}
auto operator=(const copy_on_write& x) noexcept -> copy_on_write& {
return *this = copy_on_write(x);
}
auto operator=(copy_on_write&& x) noexcept -> copy_on_write& {
auto tmp = std::move(x);
swap(*this, tmp);
return *this;
}
template <class U>
auto operator=(U&& x) -> disable_copy_assign<U> {
if (_self && unique()) {
_self->_value = std::forward<U>(x);
return *this;
}
return *this = copy_on_write(std::forward<U>(x));
}
auto write() -> element_type& {
if (!unique()) *this = copy_on_write(read());
return _self->_value;
}
auto read() const noexcept -> const element_type& {
assert(_self && "FATAL (sparent) : using a moved copy_on_write object");
return _self->_value;
}
operator const element_type&() const noexcept { return read(); }
auto operator*() const noexcept -> const element_type& { return read(); }
auto operator-> () const noexcept -> const element_type* { return &read(); }
bool unique() const noexcept {
assert(_self && "FATAL (sparent) : using a moved copy_on_write object");
return _self->_count == 1;
}
[[deprecated]] bool unique_instance() const noexcept { return unique(); }
bool identity(const copy_on_write& x) const noexcept {
assert((_self && x._self) && "FATAL (sparent) : using a moved copy_on_write object");
return _self == x._self;
}
friend inline void swap(copy_on_write& x, copy_on_write& y) noexcept {
std::swap(x._self, y._self);
}
friend inline bool operator<(const copy_on_write& x, const copy_on_write& y) noexcept {
return !x.identity(y) && (*x < *y);
}
friend inline bool operator<(const copy_on_write& x, const element_type& y) noexcept {
return *x < y;
}
friend inline bool operator<(const element_type& x, const copy_on_write& y) noexcept {
return x < *y;
}
friend inline bool operator>(const copy_on_write& x, const copy_on_write& y) noexcept {
return y < x;
}
friend inline bool operator>(const copy_on_write& x, const element_type& y) noexcept {
return y < x;
}
friend inline bool operator>(const element_type& x, const copy_on_write& y) noexcept {
return y < x;
}
friend inline bool operator<=(const copy_on_write& x, const copy_on_write& y) noexcept {
return !(y < x);
}
friend inline bool operator<=(const copy_on_write& x, const element_type& y) noexcept {
return !(y < x);
}
friend inline bool operator<=(const element_type& x, const copy_on_write& y) noexcept {
return !(y < x);
}
friend inline bool operator>=(const copy_on_write& x, const copy_on_write& y) noexcept {
return !(x < y);
}
friend inline bool operator>=(const copy_on_write& x, const element_type& y) noexcept {
return !(x < y);
}
friend inline bool operator>=(const element_type& x, const copy_on_write& y) noexcept {
return !(x < y);
}
friend inline bool operator==(const copy_on_write& x, const copy_on_write& y) noexcept {
return x.identity(y) || (*x == *y);
}
friend inline bool operator==(const copy_on_write& x, const element_type& y) noexcept {
return *x == y;
}
friend inline bool operator==(const element_type& x, const copy_on_write& y) noexcept {
return x == *y;
}
friend inline bool operator!=(const copy_on_write& x, const copy_on_write& y) noexcept {
return !(x == y);
}
friend inline bool operator!=(const copy_on_write& x, const element_type& y) noexcept {
return !(x == y);
}
friend inline bool operator!=(const element_type& x, const copy_on_write& y) noexcept {
return !(x == y);
}
};
/**************************************************************************************************/
} // namespace stlab
/**************************************************************************************************/
#endif
/**************************************************************************************************/