stlab::copy_on_write 1.0.3
Copy-on-write wrapper for any type
Loading...
Searching...
No Matches
copy_on_write.hpp
Go to the documentation of this file.
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
16
17#ifndef STLAB_COPY_ON_WRITE_HPP
18#define STLAB_COPY_ON_WRITE_HPP
19
20/**************************************************************************************************/
21
64
77
78/**************************************************************************************************/
79
80#include <atomic>
81#include <cassert>
82#include <cstddef>
83#include <type_traits>
84#include <utility>
85
86/**************************************************************************************************/
87
88namespace stlab {
89
90/**************************************************************************************************/
91
100template <typename T> // T models Regular
102 struct model {
103 std::atomic<std::size_t> _count{1};
104
105 model() noexcept(std::is_nothrow_constructible_v<T>) = default;
106
107 template <class... Args>
108 explicit model(Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args&&...>)
109 : _value(std::forward<Args>(args)...) {}
110
111 T _value;
112 };
113
114 model* _self;
115
116 template <class U>
117 using disable_copy = std::enable_if_t<!std::is_same_v<std::decay_t<U>, copy_on_write>>*;
118
119 template <typename U>
120 using disable_copy_assign =
121 std::enable_if_t<!std::is_same_v<std::decay_t<U>, copy_on_write>, copy_on_write&>;
122
123 auto default_model() noexcept(std::is_nothrow_constructible_v<T>) -> model* {
124 static model default_s;
125 return &default_s;
126 }
127
128public:
132 /* [[deprecated]] */ using value_type = T;
133
137 using element_type = T;
138
142 copy_on_write() noexcept(std::is_nothrow_constructible_v<T>) {
143 _self = default_model();
144
145 // coverity[useless_call]
146 _self->_count.fetch_add(1, std::memory_order_relaxed);
147 }
148
152 template <class U>
153 copy_on_write(U&& x, disable_copy<U> = nullptr) : _self(new model(std::forward<U>(x))) {}
154
158 template <class U, class V, class... Args>
159 copy_on_write(U&& x, V&& y, Args&&... args)
160 : _self(new model(std::forward<U>(x), std::forward<V>(y), std::forward<Args>(args)...)) {}
161
165 copy_on_write(const copy_on_write& x) noexcept : _self(x._self) {
166 assert(_self && "FATAL (sparent) : using a moved copy_on_write object");
167
168 // coverity[useless_call]
169 _self->_count.fetch_add(1, std::memory_order_relaxed);
170 }
171
175 copy_on_write(copy_on_write&& x) noexcept : _self{std::exchange(x._self, nullptr)} {
176 assert(_self && "WARNING (sparent) : using a moved copy_on_write object");
177 }
178
179 ~copy_on_write() {
180 assert(!_self || ((_self->_count > 0) && "FATAL (sparent) : double delete"));
181 if (_self && (_self->_count.fetch_sub(1, std::memory_order_release) == 1)) {
182 std::atomic_thread_fence(std::memory_order_acquire);
183 if constexpr (std::is_default_constructible_v<element_type>) {
184 assert(_self != default_model());
185 }
186 delete _self;
187 }
188 }
189
193 auto operator=(const copy_on_write& x) noexcept -> copy_on_write& {
194 // self-assignment is not allowed to disable cert-oop54-cpp warning (and is likely a bug)
195 assert(this != &x && "self-assignment is not allowed");
196 return *this = copy_on_write(x);
197 }
198
202 auto operator=(copy_on_write&& x) noexcept -> copy_on_write& {
203 auto tmp{std::move(x)};
204 swap(*this, tmp);
205 return *this;
206 }
207
211 template <class U>
212 auto operator=(U&& x) -> disable_copy_assign<U> {
213 if (_self && unique()) {
214 _self->_value = std::forward<U>(x);
215 return *this;
216 }
217
218 return *this = copy_on_write(std::forward<U>(x));
219 }
220
227 auto write() -> element_type& {
228 if (!unique()) *this = copy_on_write(read());
229
230 return _self->_value;
231 }
232
245 template <class Transform, class Inplace>
246 auto write(Transform transform, Inplace inplace) -> element_type& {
247 static_assert(std::is_invocable_r_v<T, Transform, const T&>,
248 "Transform must be invocable with const T&");
249 static_assert(std::is_invocable_r_v<void, Inplace, T&>,
250 "Inplace must be invocable with T&");
251
252 if (!unique()) {
253 *this = copy_on_write(transform(read()));
254 } else {
255 inplace(_self->_value);
256 }
257
258 return _self->_value;
259 }
260
264 [[nodiscard]] auto read() const noexcept -> const element_type& {
265 assert(_self && "FATAL (sparent) : using a moved copy_on_write object");
266
267 return _self->_value;
268 }
269
273 operator const element_type&() const noexcept { return read(); }
274
278 auto operator*() const noexcept -> const element_type& { return read(); }
279
283 auto operator->() const noexcept -> const element_type* { return &read(); }
284
290 [[nodiscard]] auto unique() const noexcept -> bool {
291 assert(_self && "FATAL (sparent) : using a moved copy_on_write object");
292
293 return _self->_count.load(std::memory_order_acquire) == 1;
294 }
295
300 [[deprecated]] [[nodiscard]] auto unique_instance() const noexcept -> bool { return unique(); }
301
305 [[nodiscard]] auto identity(const copy_on_write& x) const noexcept -> bool {
306 assert((_self && x._self) && "FATAL (sparent) : using a moved copy_on_write object");
307
308 return _self == x._self;
309 }
310
314 friend inline void swap(copy_on_write& x, copy_on_write& y) noexcept {
315 std::swap(x._self, y._self);
316 }
317
322 friend inline auto operator<(const copy_on_write& x, const copy_on_write& y) noexcept -> bool {
323 return !x.identity(y) && (*x < *y);
324 }
325
326 friend inline auto operator<(const copy_on_write& x, const element_type& y) noexcept -> bool {
327 return *x < y;
328 }
329
330 friend inline auto operator<(const element_type& x, const copy_on_write& y) noexcept -> bool {
331 return x < *y;
332 }
333
334 friend inline auto operator>(const copy_on_write& x, const copy_on_write& y) noexcept -> bool {
335 return y < x;
336 }
337
338 friend inline auto operator>(const copy_on_write& x, const element_type& y) noexcept -> bool {
339 return y < x;
340 }
341
342 friend inline auto operator>(const element_type& x, const copy_on_write& y) noexcept -> bool {
343 return y < x;
344 }
345
346 friend inline auto operator<=(const copy_on_write& x, const copy_on_write& y) noexcept -> bool {
347 return !(y < x);
348 }
349
350 friend inline auto operator<=(const copy_on_write& x, const element_type& y) noexcept -> bool {
351 return !(y < x);
352 }
353
354 friend inline auto operator<=(const element_type& x, const copy_on_write& y) noexcept -> bool {
355 return !(y < x);
356 }
357
358 friend inline auto operator>=(const copy_on_write& x, const copy_on_write& y) noexcept -> bool {
359 return !(x < y);
360 }
361
362 friend inline auto operator>=(const copy_on_write& x, const element_type& y) noexcept -> bool {
363 return !(x < y);
364 }
365
366 friend inline auto operator>=(const element_type& x, const copy_on_write& y) noexcept -> bool {
367 return !(x < y);
368 }
369
370 friend inline auto operator==(const copy_on_write& x, const copy_on_write& y) noexcept -> bool {
371 return x.identity(y) || (*x == *y);
372 }
373
374 friend inline auto operator==(const copy_on_write& x, const element_type& y) noexcept -> bool {
375 return *x == y;
376 }
377
378 friend inline auto operator==(const element_type& x, const copy_on_write& y) noexcept -> bool {
379 return x == *y;
380 }
381
382 friend inline auto operator!=(const copy_on_write& x, const copy_on_write& y) noexcept -> bool {
383 return !(x == y);
384 }
385
386 friend inline auto operator!=(const copy_on_write& x, const element_type& y) noexcept -> bool {
387 return !(x == y);
388 }
389
390 friend inline auto operator!=(const element_type& x, const copy_on_write& y) noexcept -> bool {
391 return !(x == y);
392 }
393
394};
395/**************************************************************************************************/
396
397} // namespace stlab
398
399/**************************************************************************************************/
400
401#endif
402
403/**************************************************************************************************/
friend void swap(copy_on_write &x, copy_on_write &y) noexcept
friend auto operator<(const element_type &x, const copy_on_write &y) noexcept -> bool
copy_on_write(U &&x, V &&y, Args &&... args)
auto operator=(copy_on_write &&x) noexcept -> copy_on_write &
auto write(Transform transform, Inplace inplace) -> element_type &
friend auto operator!=(const element_type &x, const copy_on_write &y) noexcept -> bool
copy_on_write(U &&x, disable_copy< U >=nullptr)
friend auto operator<(const copy_on_write &x, const copy_on_write &y) noexcept -> bool
auto operator=(const copy_on_write &x) noexcept -> copy_on_write &
friend auto operator>=(const copy_on_write &x, const copy_on_write &y) noexcept -> bool
auto operator*() const noexcept -> const element_type &
auto identity(const copy_on_write &x) const noexcept -> bool
friend auto operator<=(const element_type &x, const copy_on_write &y) noexcept -> bool
auto read() const noexcept -> const element_type &
friend auto operator!=(const copy_on_write &x, const element_type &y) noexcept -> bool
friend auto operator>=(const element_type &x, const copy_on_write &y) noexcept -> bool
friend auto operator==(const copy_on_write &x, const copy_on_write &y) noexcept -> bool
friend auto operator<(const copy_on_write &x, const element_type &y) noexcept -> bool
auto unique_instance() const noexcept -> bool
friend auto operator>=(const copy_on_write &x, const element_type &y) noexcept -> bool
auto operator->() const noexcept -> const element_type *
friend auto operator<=(const copy_on_write &x, const element_type &y) noexcept -> bool
copy_on_write() noexcept(std::is_nothrow_constructible_v< T >)
auto unique() const noexcept -> bool
auto write() -> element_type &
friend auto operator>(const copy_on_write &x, const copy_on_write &y) noexcept -> bool
friend auto operator>(const element_type &x, const copy_on_write &y) noexcept -> bool
friend auto operator<=(const copy_on_write &x, const copy_on_write &y) noexcept -> bool
auto operator=(U &&x) -> disable_copy_assign< U >
friend auto operator!=(const copy_on_write &x, const copy_on_write &y) noexcept -> bool
copy_on_write(const copy_on_write &x) noexcept
friend auto operator==(const copy_on_write &x, const element_type &y) noexcept -> bool
friend auto operator==(const element_type &x, const copy_on_write &y) noexcept -> bool
friend auto operator>(const copy_on_write &x, const element_type &y) noexcept -> bool
copy_on_write(copy_on_write &&x) noexcept