copy_on_write.hpp (6595B)
1 /* 2 Copyright 2013 Adobe 3 Distributed under the Boost Software License, Version 1.0. 4 (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 */ 6 /**************************************************************************************************/ 7 8 #ifndef STLAB_COPY_ON_WRITE_HPP 9 #define STLAB_COPY_ON_WRITE_HPP 10 11 /**************************************************************************************************/ 12 13 #include <atomic> 14 #include <cassert> 15 #include <cstddef> 16 #include <utility> 17 18 /**************************************************************************************************/ 19 20 namespace stlab { 21 22 /**************************************************************************************************/ 23 24 template <typename T> // T models Regular 25 class copy_on_write { 26 struct model { 27 std::atomic<std::size_t> _count{1}; 28 29 model() = default; 30 31 template <class... Args> 32 explicit model(Args&&... args) : _value(std::forward<Args>(args)...) {} 33 34 T _value; 35 }; 36 37 model* _self; 38 39 template <class U> 40 using disable_copy = std::enable_if_t<!std::is_same<std::decay_t<U>, copy_on_write>::value>*; 41 42 template <typename U> 43 using disable_copy_assign = 44 std::enable_if_t<!std::is_same<std::decay_t<U>, copy_on_write>::value, copy_on_write&>; 45 46 public: 47 /* [[deprecated]] */ using value_type = T; 48 49 using element_type = T; 50 51 copy_on_write() { 52 static model default_s; 53 _self = &default_s; 54 55 // coverity[useless_call] 56 ++_self->_count; 57 } 58 59 template <class U> 60 copy_on_write(U&& x, disable_copy<U> = nullptr) : _self(new model(std::forward<U>(x))) {} 61 62 template <class U, class V, class... Args> 63 copy_on_write(U&& x, V&& y, Args&&... args) 64 : _self(new model(std::forward<U>(x), std::forward<V>(y), std::forward<Args>(args)...)) {} 65 66 copy_on_write(const copy_on_write& x) noexcept : _self(x._self) { 67 assert(_self && "FATAL (sparent) : using a moved copy_on_write object"); 68 69 // coverity[useless_call] 70 ++_self->_count; 71 } 72 copy_on_write(copy_on_write&& x) noexcept : _self(x._self) { 73 assert(_self && "WARNING (sparent) : using a moved copy_on_write object"); 74 x._self = nullptr; 75 } 76 77 ~copy_on_write() { 78 if (_self && (--_self->_count == 0)) delete _self; 79 } 80 81 auto operator=(const copy_on_write& x) noexcept -> copy_on_write& { 82 return *this = copy_on_write(x); 83 } 84 85 auto operator=(copy_on_write&& x) noexcept -> copy_on_write& { 86 auto tmp = std::move(x); 87 swap(*this, tmp); 88 return *this; 89 } 90 91 template <class U> 92 auto operator=(U&& x) -> disable_copy_assign<U> { 93 if (_self && unique()) { 94 _self->_value = std::forward<U>(x); 95 return *this; 96 } 97 98 return *this = copy_on_write(std::forward<U>(x)); 99 } 100 101 auto write() -> element_type& { 102 if (!unique()) *this = copy_on_write(read()); 103 104 return _self->_value; 105 } 106 107 auto read() const noexcept -> const element_type& { 108 assert(_self && "FATAL (sparent) : using a moved copy_on_write object"); 109 110 return _self->_value; 111 } 112 113 operator const element_type&() const noexcept { return read(); } 114 115 auto operator*() const noexcept -> const element_type& { return read(); } 116 117 auto operator-> () const noexcept -> const element_type* { return &read(); } 118 119 bool unique() const noexcept { 120 assert(_self && "FATAL (sparent) : using a moved copy_on_write object"); 121 122 return _self->_count == 1; 123 } 124 [[deprecated]] bool unique_instance() const noexcept { return unique(); } 125 126 bool identity(const copy_on_write& x) const noexcept { 127 assert((_self && x._self) && "FATAL (sparent) : using a moved copy_on_write object"); 128 129 return _self == x._self; 130 } 131 132 friend inline void swap(copy_on_write& x, copy_on_write& y) noexcept { 133 std::swap(x._self, y._self); 134 } 135 136 friend inline bool operator<(const copy_on_write& x, const copy_on_write& y) noexcept { 137 return !x.identity(y) && (*x < *y); 138 } 139 140 friend inline bool operator<(const copy_on_write& x, const element_type& y) noexcept { 141 return *x < y; 142 } 143 144 friend inline bool operator<(const element_type& x, const copy_on_write& y) noexcept { 145 return x < *y; 146 } 147 148 friend inline bool operator>(const copy_on_write& x, const copy_on_write& y) noexcept { 149 return y < x; 150 } 151 152 friend inline bool operator>(const copy_on_write& x, const element_type& y) noexcept { 153 return y < x; 154 } 155 156 friend inline bool operator>(const element_type& x, const copy_on_write& y) noexcept { 157 return y < x; 158 } 159 160 friend inline bool operator<=(const copy_on_write& x, const copy_on_write& y) noexcept { 161 return !(y < x); 162 } 163 164 friend inline bool operator<=(const copy_on_write& x, const element_type& y) noexcept { 165 return !(y < x); 166 } 167 168 friend inline bool operator<=(const element_type& x, const copy_on_write& y) noexcept { 169 return !(y < x); 170 } 171 172 friend inline bool operator>=(const copy_on_write& x, const copy_on_write& y) noexcept { 173 return !(x < y); 174 } 175 176 friend inline bool operator>=(const copy_on_write& x, const element_type& y) noexcept { 177 return !(x < y); 178 } 179 180 friend inline bool operator>=(const element_type& x, const copy_on_write& y) noexcept { 181 return !(x < y); 182 } 183 184 friend inline bool operator==(const copy_on_write& x, const copy_on_write& y) noexcept { 185 return x.identity(y) || (*x == *y); 186 } 187 188 friend inline bool operator==(const copy_on_write& x, const element_type& y) noexcept { 189 return *x == y; 190 } 191 192 friend inline bool operator==(const element_type& x, const copy_on_write& y) noexcept { 193 return x == *y; 194 } 195 196 friend inline bool operator!=(const copy_on_write& x, const copy_on_write& y) noexcept { 197 return !(x == y); 198 } 199 200 friend inline bool operator!=(const copy_on_write& x, const element_type& y) noexcept { 201 return !(x == y); 202 } 203 204 friend inline bool operator!=(const element_type& x, const copy_on_write& y) noexcept { 205 return !(x == y); 206 } 207 }; 208 /**************************************************************************************************/ 209 210 } // namespace stlab 211 212 /**************************************************************************************************/ 213 214 #endif 215 216 /**************************************************************************************************/