copy-on-write 1.1.1
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
26
84
97
98/**************************************************************************************************/
99
100#include <atomic>
101#include <cassert>
102#include <cstddef>
103#include <type_traits>
104#include <utility>
105
106/**************************************************************************************************/
107
111namespace stlab {
112
113/**************************************************************************************************/
114
123template <typename T> // T models Regular
125 struct model {
126 std::atomic<std::size_t> _count{1};
127
128 model() noexcept(std::is_nothrow_constructible_v<T>) = default;
129
130 template <class... Args>
131 explicit model(Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args&&...>) :
132 _value(std::forward<Args>(args)...) {}
133
134 T _value;
135 };
136
137 model* _self;
138
139 template <class U>
140 using disable_copy = std::enable_if_t<!std::is_same_v<std::decay_t<U>, copy_on_write>>*;
141
142 template <typename U>
143 using disable_copy_assign =
144 std::enable_if_t<!std::is_same_v<std::decay_t<U>, copy_on_write>, copy_on_write&>;
145
146 // suppress "unreachable code" warning, conditional noexcept seems to throw off MSVC analyzer
147#if defined(_MSC_VER)
148#pragma warning(push)
149#pragma warning(disable : 4702)
150#endif
151 auto default_model() noexcept(std::is_nothrow_constructible_v<T>) -> model* {
152 static model default_s;
153 return &default_s;
154 }
155#if defined(_MSC_VER)
156#pragma warning(pop)
157#endif
158
159public:
165 /* [[deprecated]] */ using value_type = T;
166
170 using element_type = T;
172
178 copy_on_write() noexcept(std::is_nothrow_constructible_v<T>) {
179 _self = default_model();
180
181 // coverity[useless_call]
182 _self->_count.fetch_add(1, std::memory_order_relaxed);
183 }
184
188 template <class U>
189 copy_on_write(U&& x, disable_copy<U> = nullptr) : _self(new model(std::forward<U>(x))) {}
190
195 template <class U, class V, class... Args>
196 copy_on_write(U&& x, V&& y, Args&&... args) :
197 _self(new model(std::forward<U>(x), std::forward<V>(y), std::forward<Args>(args)...)) {}
198
202 copy_on_write(const copy_on_write& x) noexcept : _self(x._self) {
203 assert(_self && "FATAL (sparent) : using a moved copy_on_write object");
204
205 // coverity[useless_call]
206 _self->_count.fetch_add(1, std::memory_order_relaxed);
207 }
208
212 copy_on_write(copy_on_write&& x) noexcept : _self{std::exchange(x._self, nullptr)} {
213 assert(_self && "WARNING (sparent) : using a moved copy_on_write object");
214 }
215
220 assert(!_self || ((_self->_count > 0) && "FATAL (sparent) : double delete"));
221 if (_self && (_self->_count.fetch_sub(1, std::memory_order_release) == 1)) {
222 std::atomic_thread_fence(std::memory_order_acquire);
223 if constexpr (std::is_default_constructible_v<element_type>) {
224 assert(_self != default_model());
225 }
226 delete _self;
227 }
228 }
229
233 auto operator=(const copy_on_write& x) noexcept -> copy_on_write& {
234 // self-assignment is not allowed to disable cert-oop54-cpp warning (and is likely a bug)
235 assert(this != &x && "self-assignment is not allowed");
236 return *this = copy_on_write(x);
237 }
238
242 auto operator=(copy_on_write&& x) noexcept -> copy_on_write& {
243 auto tmp{std::move(x)};
244 swap(*this, tmp);
245 return *this;
246 }
247
252 template <class U>
253 auto operator=(U&& x) -> disable_copy_assign<U> {
254 if (_self && unique()) {
255 _self->_value = std::forward<U>(x);
256 return *this;
257 }
258
259 return *this = copy_on_write(std::forward<U>(x));
260 }
261
264
271 auto write() -> element_type& {
272 if (!unique()) *this = copy_on_write(read());
273
274 return _self->_value;
275 }
276
290 template <class Transform, class Inplace>
291 auto write(Transform transform, Inplace inplace) -> element_type& {
292 static_assert(std::is_invocable_r_v<T, Transform, const T&>,
293 "Transform must be invocable with const T&");
294 static_assert(std::is_invocable_r_v<void, Inplace, T&>,
295 "Inplace must be invocable with T&");
296
297 if (!unique()) {
298 *this = copy_on_write(transform(read()));
299 } else {
300 inplace(_self->_value);
301 }
302
303 return _self->_value;
304 }
305
309 [[nodiscard]] auto read() const noexcept -> const element_type& {
310 assert(_self && "FATAL (sparent) : using a moved copy_on_write object");
311
312 return _self->_value;
313 }
314
318 operator const element_type&() const noexcept { return read(); }
319
323 auto operator*() const noexcept -> const element_type& { return read(); }
324
328 auto operator->() const noexcept -> const element_type* { return &read(); }
329
335 [[nodiscard]] auto unique() const noexcept -> bool {
336 assert(_self && "FATAL (sparent) : using a moved copy_on_write object");
337
338 return _self->_count.load(std::memory_order_acquire) == 1;
339 }
340
345 [[deprecated]] [[nodiscard]] auto unique_instance() const noexcept -> bool { return unique(); }
346
350 [[nodiscard]] auto identity(const copy_on_write& x) const noexcept -> bool {
351 assert((_self && x._self) && "FATAL (sparent) : using a moved copy_on_write object");
352
353 return _self == x._self;
354 }
355
358
365 friend inline void swap(copy_on_write& x, copy_on_write& y) noexcept {
366 std::swap(x._self, y._self);
367 }
368
373 friend inline auto operator<(const copy_on_write& x, const copy_on_write& y) noexcept -> bool {
374 return !x.identity(y) && (*x < *y);
375 }
376
377 friend inline auto operator<(const copy_on_write& x, const element_type& y) noexcept -> bool {
378 return *x < y;
379 }
380
381 friend inline auto operator<(const element_type& x, const copy_on_write& y) noexcept -> bool {
382 return x < *y;
383 }
384
385 friend inline auto operator>(const copy_on_write& x, const copy_on_write& y) noexcept -> bool {
386 return y < x;
387 }
388
389 friend inline auto operator>(const copy_on_write& x, const element_type& y) noexcept -> bool {
390 return y < x;
391 }
392
393 friend inline auto operator>(const element_type& x, const copy_on_write& y) noexcept -> bool {
394 return y < x;
395 }
396
397 friend inline auto operator<=(const copy_on_write& x, const copy_on_write& y) noexcept -> bool {
398 return !(y < x);
399 }
400
401 friend inline auto operator<=(const copy_on_write& x, const element_type& y) noexcept -> bool {
402 return !(y < x);
403 }
404
405 friend inline auto operator<=(const element_type& x, const copy_on_write& y) noexcept -> bool {
406 return !(y < x);
407 }
408
409 friend inline auto operator>=(const copy_on_write& x, const copy_on_write& y) noexcept -> bool {
410 return !(x < y);
411 }
412
413 friend inline auto operator>=(const copy_on_write& x, const element_type& y) noexcept -> bool {
414 return !(x < y);
415 }
416
417 friend inline auto operator>=(const element_type& x, const copy_on_write& y) noexcept -> bool {
418 return !(x < y);
419 }
420
421 friend inline auto operator==(const copy_on_write& x, const copy_on_write& y) noexcept -> bool {
422 return x.identity(y) || (*x == *y);
423 }
424
425 friend inline auto operator==(const copy_on_write& x, const element_type& y) noexcept -> bool {
426 return *x == y;
427 }
428
429 friend inline auto operator==(const element_type& x, const copy_on_write& y) noexcept -> bool {
430 return x == *y;
431 }
432
433 friend inline auto operator!=(const copy_on_write& x, const copy_on_write& y) noexcept -> bool {
434 return !(x == y);
435 }
436
437 friend inline auto operator!=(const copy_on_write& x, const element_type& y) noexcept -> bool {
438 return !(x == y);
439 }
440
441 friend inline auto operator!=(const element_type& x, const copy_on_write& y) noexcept -> bool {
442 return !(x == y);
443 }
444
446};
447/**************************************************************************************************/
448
449} // namespace stlab
450
451/**************************************************************************************************/
452
453#endif
454
455/**************************************************************************************************/
copy_on_write(U &&x, V &&y, Args &&... args)
Constructs a new instance by forwarding multiple arguments to the wrapped value constructor.
Definition copy_on_write.hpp:196
auto operator=(copy_on_write &&x) noexcept -> copy_on_write &
Move assignment operator that takes ownership of the source object's data.
Definition copy_on_write.hpp:242
copy_on_write(U &&x, disable_copy< U >=nullptr)
Constructs a new instance by forwarding arguments to the wrapped value constructor.
Definition copy_on_write.hpp:189
auto operator=(const copy_on_write &x) noexcept -> copy_on_write &
Copy assignment operator that shares the underlying data with the source object.
Definition copy_on_write.hpp:233
~copy_on_write()
Destructor.
Definition copy_on_write.hpp:219
copy_on_write() noexcept(std::is_nothrow_constructible_v< T >)
Default constructs the wrapped value.
Definition copy_on_write.hpp:178
auto operator=(U &&x) -> disable_copy_assign< U >
Assigns a new value to the wrapped object, optimizing for in-place assignment when unique.
Definition copy_on_write.hpp:253
copy_on_write(const copy_on_write &x) noexcept
Copy constructor that shares the underlying data with the source object.
Definition copy_on_write.hpp:202
copy_on_write(copy_on_write &&x) noexcept
Move constructor that takes ownership of the source object's data.
Definition copy_on_write.hpp:212
T element_type
The type of value stored.
Definition copy_on_write.hpp:170
T value_type
Definition copy_on_write.hpp:165
friend void swap(copy_on_write &x, copy_on_write &y) noexcept
Efficiently swaps the contents of two copy_on_write objects.
Definition copy_on_write.hpp:365
friend auto operator<(const element_type &x, const copy_on_write &y) noexcept -> bool
Comparisons can be done with the underlying value or the copy_on_write object.
Definition copy_on_write.hpp:381
friend auto operator!=(const element_type &x, const copy_on_write &y) noexcept -> bool
Comparisons can be done with the underlying value or the copy_on_write object.
Definition copy_on_write.hpp:441
friend auto operator<(const copy_on_write &x, const copy_on_write &y) noexcept -> bool
Comparisons can be done with the underlying value or the copy_on_write object.
Definition copy_on_write.hpp:373
friend auto operator>=(const copy_on_write &x, const copy_on_write &y) noexcept -> bool
Comparisons can be done with the underlying value or the copy_on_write object.
Definition copy_on_write.hpp:409
friend auto operator<=(const element_type &x, const copy_on_write &y) noexcept -> bool
Comparisons can be done with the underlying value or the copy_on_write object.
Definition copy_on_write.hpp:405
friend auto operator!=(const copy_on_write &x, const element_type &y) noexcept -> bool
Comparisons can be done with the underlying value or the copy_on_write object.
Definition copy_on_write.hpp:437
friend auto operator>=(const element_type &x, const copy_on_write &y) noexcept -> bool
Comparisons can be done with the underlying value or the copy_on_write object.
Definition copy_on_write.hpp:417
friend auto operator==(const copy_on_write &x, const copy_on_write &y) noexcept -> bool
Comparisons can be done with the underlying value or the copy_on_write object.
Definition copy_on_write.hpp:421
friend auto operator<(const copy_on_write &x, const element_type &y) noexcept -> bool
Comparisons can be done with the underlying value or the copy_on_write object.
Definition copy_on_write.hpp:377
friend auto operator>=(const copy_on_write &x, const element_type &y) noexcept -> bool
Comparisons can be done with the underlying value or the copy_on_write object.
Definition copy_on_write.hpp:413
friend auto operator<=(const copy_on_write &x, const element_type &y) noexcept -> bool
Comparisons can be done with the underlying value or the copy_on_write object.
Definition copy_on_write.hpp:401
friend auto operator>(const copy_on_write &x, const copy_on_write &y) noexcept -> bool
Comparisons can be done with the underlying value or the copy_on_write object.
Definition copy_on_write.hpp:385
friend auto operator>(const element_type &x, const copy_on_write &y) noexcept -> bool
Comparisons can be done with the underlying value or the copy_on_write object.
Definition copy_on_write.hpp:393
friend auto operator<=(const copy_on_write &x, const copy_on_write &y) noexcept -> bool
Comparisons can be done with the underlying value or the copy_on_write object.
Definition copy_on_write.hpp:397
friend auto operator!=(const copy_on_write &x, const copy_on_write &y) noexcept -> bool
Comparisons can be done with the underlying value or the copy_on_write object.
Definition copy_on_write.hpp:433
friend auto operator==(const copy_on_write &x, const element_type &y) noexcept -> bool
Comparisons can be done with the underlying value or the copy_on_write object.
Definition copy_on_write.hpp:425
friend auto operator==(const element_type &x, const copy_on_write &y) noexcept -> bool
Comparisons can be done with the underlying value or the copy_on_write object.
Definition copy_on_write.hpp:429
friend auto operator>(const copy_on_write &x, const element_type &y) noexcept -> bool
Comparisons can be done with the underlying value or the copy_on_write object.
Definition copy_on_write.hpp:389
auto write(Transform transform, Inplace inplace) -> element_type &
If the object is not unique, the transform is applied to the underlying value to copy it and a refere...
Definition copy_on_write.hpp:291
auto operator*() const noexcept -> const element_type &
Dereference operator that returns a const reference to the underlying value.
Definition copy_on_write.hpp:323
auto identity(const copy_on_write &x) const noexcept -> bool
Returns true if this object and the given object share the same underlying data.
Definition copy_on_write.hpp:350
auto read() const noexcept -> const element_type &
Returns a const reference to the underlying value for read-only access.
Definition copy_on_write.hpp:309
auto unique_instance() const noexcept -> bool
Definition copy_on_write.hpp:345
auto operator->() const noexcept -> const element_type *
Arrow operator that returns a const pointer to the underlying value.
Definition copy_on_write.hpp:328
auto unique() const noexcept -> bool
Returns true if this is the only reference to the underlying object.
Definition copy_on_write.hpp:335
auto write() -> element_type &
Obtains a non-const reference to the underlying value.
Definition copy_on_write.hpp:271
Definition copy_on_write.hpp:111