stlab-copy-on-write 1.0.4
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
91namespace stlab {
92
93/**************************************************************************************************/
94
103template <typename T> // T models Regular
105 struct model {
106 std::atomic<std::size_t> _count{1};
107
108 model() noexcept(std::is_nothrow_constructible_v<T>) = default;
109
110 template <class... Args>
111 explicit model(Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args&&...>) :
112 _value(std::forward<Args>(args)...) {}
113
114 T _value;
115 };
116
117 model* _self;
118
119 template <class U>
120 using disable_copy = std::enable_if_t<!std::is_same_v<std::decay_t<U>, copy_on_write>>*;
121
122 template <typename U>
123 using disable_copy_assign =
124 std::enable_if_t<!std::is_same_v<std::decay_t<U>, copy_on_write>, copy_on_write&>;
125
126 auto default_model() noexcept(std::is_nothrow_constructible_v<T>) -> model* {
127 static model default_s;
128 return &default_s;
129 }
130
131public:
135 /* [[deprecated]] */ using value_type = T;
136
140 using element_type = T;
141
145 copy_on_write() noexcept(std::is_nothrow_constructible_v<T>) {
146 _self = default_model();
147
148 // coverity[useless_call]
149 _self->_count.fetch_add(1, std::memory_order_relaxed);
150 }
151
155 template <class U>
156 copy_on_write(U&& x, disable_copy<U> = nullptr) : _self(new model(std::forward<U>(x))) {}
157
161 template <class U, class V, class... Args>
162 copy_on_write(U&& x, V&& y, Args&&... args) :
163 _self(new model(std::forward<U>(x), std::forward<V>(y), std::forward<Args>(args)...)) {}
164
168 copy_on_write(const copy_on_write& x) noexcept : _self(x._self) {
169 assert(_self && "FATAL (sparent) : using a moved copy_on_write object");
170
171 // coverity[useless_call]
172 _self->_count.fetch_add(1, std::memory_order_relaxed);
173 }
174
178 copy_on_write(copy_on_write&& x) noexcept : _self{std::exchange(x._self, nullptr)} {
179 assert(_self && "WARNING (sparent) : using a moved copy_on_write object");
180 }
181
182 ~copy_on_write() {
183 assert(!_self || ((_self->_count > 0) && "FATAL (sparent) : double delete"));
184 if (_self && (_self->_count.fetch_sub(1, std::memory_order_release) == 1)) {
185 std::atomic_thread_fence(std::memory_order_acquire);
186 if constexpr (std::is_default_constructible_v<element_type>) {
187 assert(_self != default_model());
188 }
189 delete _self;
190 }
191 }
192
196 auto operator=(const copy_on_write& x) noexcept -> copy_on_write& {
197 // self-assignment is not allowed to disable cert-oop54-cpp warning (and is likely a bug)
198 assert(this != &x && "self-assignment is not allowed");
199 return *this = copy_on_write(x);
200 }
201
205 auto operator=(copy_on_write&& x) noexcept -> copy_on_write& {
206 auto tmp{std::move(x)};
207 swap(*this, tmp);
208 return *this;
209 }
210
214 template <class U>
215 auto operator=(U&& x) -> disable_copy_assign<U> {
216 if (_self && unique()) {
217 _self->_value = std::forward<U>(x);
218 return *this;
219 }
220
221 return *this = copy_on_write(std::forward<U>(x));
222 }
223
230 auto write() -> element_type& {
231 if (!unique()) *this = copy_on_write(read());
232
233 return _self->_value;
234 }
235
248 template <class Transform, class Inplace>
249 auto write(Transform transform, Inplace inplace) -> element_type& {
250 static_assert(std::is_invocable_r_v<T, Transform, const T&>,
251 "Transform must be invocable with const T&");
252 static_assert(std::is_invocable_r_v<void, Inplace, T&>,
253 "Inplace must be invocable with T&");
254
255 if (!unique()) {
256 *this = copy_on_write(transform(read()));
257 } else {
258 inplace(_self->_value);
259 }
260
261 return _self->_value;
262 }
263
267 [[nodiscard]] auto read() const noexcept -> const element_type& {
268 assert(_self && "FATAL (sparent) : using a moved copy_on_write object");
269
270 return _self->_value;
271 }
272
276 operator const element_type&() const noexcept { return read(); }
277
281 auto operator*() const noexcept -> const element_type& { return read(); }
282
286 auto operator->() const noexcept -> const element_type* { return &read(); }
287
293 [[nodiscard]] auto unique() const noexcept -> bool {
294 assert(_self && "FATAL (sparent) : using a moved copy_on_write object");
295
296 return _self->_count.load(std::memory_order_acquire) == 1;
297 }
298
303 [[deprecated]] [[nodiscard]] auto unique_instance() const noexcept -> bool { return unique(); }
304
308 [[nodiscard]] auto identity(const copy_on_write& x) const noexcept -> bool {
309 assert((_self && x._self) && "FATAL (sparent) : using a moved copy_on_write object");
310
311 return _self == x._self;
312 }
313
317 friend inline void swap(copy_on_write& x, copy_on_write& y) noexcept {
318 std::swap(x._self, y._self);
319 }
320
325 friend inline auto operator<(const copy_on_write& x, const copy_on_write& y) noexcept -> bool {
326 return !x.identity(y) && (*x < *y);
327 }
328
329 friend inline auto operator<(const copy_on_write& x, const element_type& y) noexcept -> bool {
330 return *x < y;
331 }
332
333 friend inline auto operator<(const element_type& x, const copy_on_write& y) noexcept -> bool {
334 return x < *y;
335 }
336
337 friend inline auto operator>(const copy_on_write& x, const copy_on_write& y) noexcept -> bool {
338 return y < x;
339 }
340
341 friend inline auto operator>(const copy_on_write& x, const element_type& y) noexcept -> bool {
342 return y < x;
343 }
344
345 friend inline auto operator>(const element_type& x, const copy_on_write& y) noexcept -> bool {
346 return y < x;
347 }
348
349 friend inline auto operator<=(const copy_on_write& x, const copy_on_write& y) noexcept -> bool {
350 return !(y < x);
351 }
352
353 friend inline auto operator<=(const copy_on_write& x, const element_type& y) noexcept -> bool {
354 return !(y < x);
355 }
356
357 friend inline auto operator<=(const element_type& x, const copy_on_write& y) noexcept -> bool {
358 return !(y < x);
359 }
360
361 friend inline auto operator>=(const copy_on_write& x, const copy_on_write& y) noexcept -> bool {
362 return !(x < y);
363 }
364
365 friend inline auto operator>=(const copy_on_write& x, const element_type& y) noexcept -> bool {
366 return !(x < y);
367 }
368
369 friend inline auto operator>=(const element_type& x, const copy_on_write& y) noexcept -> bool {
370 return !(x < y);
371 }
372
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 !(x == y);
387 }
388
389 friend inline auto operator!=(const copy_on_write& x, const element_type& y) noexcept -> bool {
390 return !(x == y);
391 }
392
393 friend inline auto operator!=(const element_type& x, const copy_on_write& y) noexcept -> bool {
394 return !(x == y);
395 }
396
397};
398/**************************************************************************************************/
399
400} // namespace stlab
401
402/**************************************************************************************************/
403
404#endif
405
406/**************************************************************************************************/
T element_type
Definition copy_on_write.hpp:140
friend void swap(copy_on_write &x, copy_on_write &y) noexcept
Definition copy_on_write.hpp:317
friend auto operator<(const element_type &x, const copy_on_write &y) noexcept -> bool
Definition copy_on_write.hpp:333
copy_on_write(U &&x, V &&y, Args &&... args)
Definition copy_on_write.hpp:162
auto operator=(copy_on_write &&x) noexcept -> copy_on_write &
Definition copy_on_write.hpp:205
auto write(Transform transform, Inplace inplace) -> element_type &
Definition copy_on_write.hpp:249
friend auto operator!=(const element_type &x, const copy_on_write &y) noexcept -> bool
Definition copy_on_write.hpp:393
copy_on_write(U &&x, disable_copy< U >=nullptr)
Definition copy_on_write.hpp:156
friend auto operator<(const copy_on_write &x, const copy_on_write &y) noexcept -> bool
Definition copy_on_write.hpp:325
T value_type
Definition copy_on_write.hpp:135
auto operator=(const copy_on_write &x) noexcept -> copy_on_write &
Definition copy_on_write.hpp:196
friend auto operator>=(const copy_on_write &x, const copy_on_write &y) noexcept -> bool
Definition copy_on_write.hpp:361
auto operator*() const noexcept -> const element_type &
Definition copy_on_write.hpp:281
auto identity(const copy_on_write &x) const noexcept -> bool
Definition copy_on_write.hpp:308
friend auto operator<=(const element_type &x, const copy_on_write &y) noexcept -> bool
Definition copy_on_write.hpp:357
auto read() const noexcept -> const element_type &
Definition copy_on_write.hpp:267
friend auto operator!=(const copy_on_write &x, const element_type &y) noexcept -> bool
Definition copy_on_write.hpp:389
friend auto operator>=(const element_type &x, const copy_on_write &y) noexcept -> bool
Definition copy_on_write.hpp:369
friend auto operator==(const copy_on_write &x, const copy_on_write &y) noexcept -> bool
Definition copy_on_write.hpp:373
friend auto operator<(const copy_on_write &x, const element_type &y) noexcept -> bool
Definition copy_on_write.hpp:329
auto unique_instance() const noexcept -> bool
Definition copy_on_write.hpp:303
friend auto operator>=(const copy_on_write &x, const element_type &y) noexcept -> bool
Definition copy_on_write.hpp:365
auto operator->() const noexcept -> const element_type *
Definition copy_on_write.hpp:286
friend auto operator<=(const copy_on_write &x, const element_type &y) noexcept -> bool
Definition copy_on_write.hpp:353
copy_on_write() noexcept(std::is_nothrow_constructible_v< T >)
Definition copy_on_write.hpp:145
auto unique() const noexcept -> bool
Definition copy_on_write.hpp:293
auto write() -> element_type &
Definition copy_on_write.hpp:230
friend auto operator>(const copy_on_write &x, const copy_on_write &y) noexcept -> bool
Definition copy_on_write.hpp:337
friend auto operator>(const element_type &x, const copy_on_write &y) noexcept -> bool
Definition copy_on_write.hpp:345
friend auto operator<=(const copy_on_write &x, const copy_on_write &y) noexcept -> bool
Definition copy_on_write.hpp:349
auto operator=(U &&x) -> disable_copy_assign< U >
Definition copy_on_write.hpp:215
friend auto operator!=(const copy_on_write &x, const copy_on_write &y) noexcept -> bool
Definition copy_on_write.hpp:385
copy_on_write(const copy_on_write &x) noexcept
Definition copy_on_write.hpp:168
friend auto operator==(const copy_on_write &x, const element_type &y) noexcept -> bool
Definition copy_on_write.hpp:377
friend auto operator==(const element_type &x, const copy_on_write &y) noexcept -> bool
Definition copy_on_write.hpp:381
friend auto operator>(const copy_on_write &x, const element_type &y) noexcept -> bool
Definition copy_on_write.hpp:341
copy_on_write(copy_on_write &&x) noexcept
Definition copy_on_write.hpp:178
Definition copy_on_write.hpp:91