Stroika Library 3.0d23
 
Loading...
Searching...
No Matches
SharedByValue.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Memory_SharedByValue_h_
5#define _Stroika_Foundation_Memory_SharedByValue_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <functional>
10#include <memory>
11
12#include "Stroika/Foundation/Common/Common.h"
13#include "Stroika/Foundation/Common/Concepts.h"
16
17/**
18 * \file
19 *
20 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
21 *
22 * TODO:
23 * @todo Probably should use Debug::AssertExternallySynchronized in SharedByValue
24 * @todo Understand and either remove or better document why we allow calling rwget etc with
25 * external COPIER - why not always use the same one?
26 */
27
28namespace Stroika::Foundation::Memory {
29
30 /**
31 * @brief Support types and concepts for SharedByValue template.
32 */
33 namespace SharedByValueSupport {
34
35 /**
36 * \brief DefaultValueCopier is the default value-copier for copying SharedByValue (thing that clones a shared_ptr)
37 *
38 * DefaultValueCopier is the a simple copying mechanism used by SharedByValue<>.
39 * It simply hardwires use of new T() - the default T(T&) constructor to copy elements of type T.
40 */
41 template <typename T, typename SHARED_IMPL = shared_ptr<T>>
42 SHARED_IMPL DefaultValueCopier (const T& t);
43
44 /**
45 * \brief DefaultValueCopier_FunctionObject same as DefaultValueCopier, but as a function object
46 *
47 * \note before Stroika 3.0d23, this was the only style of copier supported.
48 */
49 template <typename T, typename SHARED_IMPL = shared_ptr<T>>
51#if __cplusplus >= kStrokia_Foundation_Common_cplusplus_23 || _HAS_CXX23 /*vis studio uses _HAS_CXX23 */
52 static SHARED_IMPL operator() (const T& t);
53#else
54 SHARED_IMPL operator() (const T& t) const;
55#endif
56 };
57
58 /**
59 * Sometimes we want to have NO COPIER defined - either in the traits, or the instance value.
60 * Use this sentinel value to check that case. Like optional<> for types.
61 */
62 using MissingCopierTypeSentinel = nullptr_t;
63
64 /**
65 * Is COPIER_TYPE a legit copier of T to SHARED_IMPL.
66 */
67 template <typename COPIER_TYPE, typename T, typename SHARED_IMPL>
68 concept ICopier = requires (COPIER_TYPE copier, SHARED_IMPL, T t) {
69 { copier (t) } -> same_as<SHARED_IMPL>;
70 };
71
72 /**
73 * logically this is optional<ICopier> (@see MissingCopierTypeSentinel)
74 */
75 template <typename COPIER_TYPE, typename T, typename SHARED_IMPL>
76 concept IOptionalCopier = same_as<remove_cvref_t<COPIER_TYPE>, MissingCopierTypeSentinel> or ICopier<COPIER_TYPE, T, SHARED_IMPL>;
77
78 /**
79 * Check if a TRAITS is a valid 'traits type' for SharedByValue:
80 * o element_type (this must be T)
81 * o shared_ptr_type (typically shared_ptr<T> or something that looks like that)
82 * o instance_defined_copier_type is ICopier or MissingCopierTypeSentinel
83 * o type of kDefaultCopier is ICopier or MissingCopierTypeSentinel
84 * o element_copier_type is ICopier (at least one must not be a sentinel value)
85 *
86 * @tparam TRAITS
87 * @tparam T
88 */
89 template <typename TRAITS, typename T>
90 concept ITraits =
91 requires (TRAITS, T t) {
92 typename TRAITS::element_type;
93 typename TRAITS::shared_ptr_type;
94 } and same_as<typename TRAITS::shared_ptr_type::element_type, T> and Common::ICVRefTd<typename TRAITS::element_type>
95
96 and
97 requires (TRAITS) {
98 typename TRAITS::default_copier_type;
99 { TRAITS::kDefaultCopier } -> IOptionalCopier<T, typename TRAITS::shared_ptr_type>;
101
102 and
103 requires (TRAITS) {
104 typename TRAITS::instance_defined_copier_type;
105 { typename TRAITS::instance_defined_copier_type{} } -> IOptionalCopier<T, typename TRAITS::shared_ptr_type>;
106 } and Common::ICVRefTd<typename TRAITS::instance_defined_copier_type>
107
108 and
109 requires (TRAITS, T t) {
110 typename TRAITS::element_copier_type;
111 { typename TRAITS::element_copier_type{} } -> ICopier<T, typename TRAITS::shared_ptr_type>;
112 } and Common::ICVRefTd<typename TRAITS::element_copier_type>
113
114 and not(same_as<typename TRAITS::instance_defined_copier_type, MissingCopierTypeSentinel> and
115 same_as<decltype (TRAITS::kDefaultCopier), MissingCopierTypeSentinel>)
116
117 and (same_as<typename TRAITS::instance_defined_copier_type, MissingCopierTypeSentinel> or
118 same_as<decltype (TRAITS::kDefaultCopier), MissingCopierTypeSentinel> or
119 convertible_to<decltype (TRAITS::kDefaultCopier), typename TRAITS::instance_defined_copier_type>);
120
121 /**
122 * \brief ExplicitTraits is a utility struct to provide parameterized TRAITS support for SharedByValue<>
123 *
124 * @tparam T
125 * @tparam SHARED_IMPL
126 * @tparam DEFAULT_COPIER_TYPE
127 * @tparam DEFAULT_COPIER
128 * @tparam INSTANCE_COPIER_TYPE typically MissingCopierTypeSentinel, or occasionally function<SHARED_IMPL (const T&)>
129 */
130 template <typename T, typename SHARED_IMPL, IOptionalCopier<T, SHARED_IMPL> DEFAULT_COPIER_TYPE, DEFAULT_COPIER_TYPE DEFAULT_COPIER, IOptionalCopier<T, SHARED_IMPL> INSTANCE_COPIER_TYPE>
132 /**
133 * @brief SharedByValue 'of T' type
134 */
135 using element_type = remove_cvref_t<T>;
136
137 /**
138 * @brief shared_ptr<T> typically, but could be another 'shared_ptr'-like class
139 */
140 using shared_ptr_type = remove_cvref_t<SHARED_IMPL>;
141
142 /**
143 * @brief satisfies IOptionalCopier, and used as default for instance copier, and if no instance copier, used to copy objects when a change occurs
144 */
145 using default_copier_type = remove_cvref_t<DEFAULT_COPIER_TYPE>;
146
147 /**
148 * @brief the default copier - which takes a 'T' and generates the appropriate SHARED_IMPL
149 */
150 static constexpr conditional_t<is_function_v<default_copier_type>, default_copier_type*, default_copier_type> kDefaultCopier{DEFAULT_COPIER};
151
152 /**
153 * instance_defined_copier_type can be MissingCopierTypeSentinel, to indicate no user-defined (instance-defined) copy function
154 * or it refers to the type of the function which converts to the appropriate shared_ptr type (typically function<SHARED_IMPL (const T&)>)
155 */
156 using instance_defined_copier_type = remove_cvref_t<INSTANCE_COPIER_TYPE>;
157
158 /**
159 * This is the type returned by GetElementCopier () - its either instance_defined_copier_type, or function<SHARED_IMPL (const T&)>
160 */
162 conditional_t<same_as<instance_defined_copier_type, MissingCopierTypeSentinel>, function<shared_ptr_type (const T&)>, instance_defined_copier_type>;
163 };
164
165 /**
166 * @brief Unified DefaultTraits_NoInstanceCopier - works with both function objects and function pointers
167 *
168 * This is the default, and most efficient, and nearly always appropriate way to go.
169 * Automatically detects whether COPIER_TYPE is a function object or function pointer and uses appropriate default instance.
170 *
171 * @tparam T
172 * @tparam COPIER_INSTANCE - the instance of the copier (defaults to appropriate value based on type)
173 *
174 * \note shared_ptr_type SHOULD BE inferred from the result of COPIER_INSTANCE.
175 *
176 * \note
177 * Tempting to make the SHARED_IMPL passed to ExplicitTraits be
178 * invoke_result_t<decltype (COPIER_INSTANCE), T>
179 * instead of
180 * shared_ptr<T>.
181 * Maybe I can get that working in the future. But SUPER low priority (since you can easily use ExplitTraits directly),
182 * and use of other shared_ptr types is quite rare.
183 *
184 * Dev/Library/Sources/Stroika/Foundation/DataExchange/Variant/Reader.h:98:94: required from here
185 * 98 | Memory::SharedByValue<_IRep, Memory::SharedByValueSupport::DefaultTraits<_IRep, MakeSharedRep_>>;
186 * | ^~~~~~~~~~~~~~
187 * /usr/include/c++/14/type_traits:3163:53: error: static assertion failed: each argument type must be a complete class or an unbounded array
188 */
189 template <typename T, auto COPIER_INSTANCE = &DefaultValueCopier<T, shared_ptr<T>>>
191 ExplicitTraits<T, shared_ptr<T>, decltype (COPIER_INSTANCE), COPIER_INSTANCE, MissingCopierTypeSentinel>;
193 static_assert (ITraits<DefaultTraits_NoInstanceCopier<int, DefaultValueCopier_FunctionObject<int>{}>, int>);
194 static_assert (ITraits<DefaultTraits_NoInstanceCopier<int>, int>);
195 static_assert (ITraits<DefaultTraits_NoInstanceCopier<int, &DefaultValueCopier<int>>, int>);
196
197 /**
198 * @brief SharedByValue traits object for per-instance constructor specification of shared_ptr copier only
199 *
200 * This has no 'default' value for the copier (must always be explicitly specified in CTOR or copied from other instance through CTOR).
201 *
202 * @tparam T
203 * @tparam COPIER
204 *
205 * \note shared_ptr_type is inferred from the result of COPIER_TYPE.
206 */
207 template <typename T, typename COPIER_TYPE = function<shared_ptr<T> (const T&)>>
210 static_assert (ITraits<DefaultTraits_InstanceCopierOnly<int>, int>);
211
212 /**
213 * @brief Both a default copier, and a function<sharedimp(T)> instance copier.
214 *
215 * @tparam T
216 * @tparam DEFAULT_COPIER_TYPE
217 * @tparam INSTANCE_COPIER_TYPE
218 *
219 * \note shared_ptr_type is inferred from the result of INSTANCE_COPIER_TYPE.
220 */
221 template <typename T, typename DEFAULT_COPIER_TYPE = DefaultValueCopier_FunctionObject<T, shared_ptr<T>>,
222 typename INSTANCE_COPIER_TYPE = function<shared_ptr<T> (const T&)>>
224 ExplicitTraits<T, invoke_result_t<INSTANCE_COPIER_TYPE, T>, DEFAULT_COPIER_TYPE, DEFAULT_COPIER_TYPE{}, INSTANCE_COPIER_TYPE>;
225 static_assert (ITraits<DefaultTraits_DefaultAndInstanceCopiers<int>, int>);
226
227 /**
228 * \brief DefaultTraits is a utility struct to provide parameterized support
229 * for SharedByValue<>
230 *
231 * This class should allow SHARED_IMPL to be std::shared_ptr (or another shared_ptr implementation).
232 *
233 * \note we selected DefaultTraits_NoInstanceCopier as the default, since its the lowest overhead,
234 * and nearly always easiest to use.
235 *
236 * \par Example Usage
237 * \code
238 * static shared_ptr<_IRep> Clone_ (const _IRep& rep);
239 * using _SharedByValueRepType = Memory::SharedByValue<_IRep, Memory::SharedByValueSupport::DefaultTraits<_IRep, Clone_>>;
240 * \endcode
241 */
242 template <typename T, auto COPIER_INSTANCE = &DefaultValueCopier<T, shared_ptr<T>>>
244 static_assert (ITraits<DefaultTraits<int>, int>);
246
247 /**
248 * This state is meant purely for code that may manage their internal behavior
249 * based on details of reference counting - not for semantic reasons, but to enhance performance.
250 */
251 enum class SharingState {
252 eNull,
253 eSolo,
254 eShared,
255 };
256
257 }
258
259 /**
260 * \brief SharedByValue is a utility class to implement Copy-On-Write (aka COW) - sort of halfway between unique_ptr and shared_ptr
261 *
262 * SharedByValue is a utility class to implement Copy-On-Write (aka Copy on Write, or COW).
263 *
264 * This utility class should not be used lightly. Its somewhat tricky to use properly. Its meant
265 * to facilitate implementing the copy-on-write semantics which are often handy in providing
266 * high-performance data structures.
267 *
268 * This class should allow SHARED_IMPL to be std::shared_ptr (or another shared_ptr implementation).
269 *
270 * This class template was originally called CopyOnWrite.
271 *
272 * \note Though there IS a fCopier, this is only the default copier, and calls to rwget() can always provide
273 * an alternative copier.
274 *
275 * \note - though we theoretically support instance_copier_type, I don't think this has ever been tested, and is
276 * either very little used, or never used.
277 *
278 * \par Example Usage
279 * \code
280 * SharedByValue<vector<byte>> b{BLOB::Hex ("abcd1245").Repeat (100).As<vector<byte>> ()};
281 * SharedByValue<vector<byte>> c = b; // copied by reference until 'c' or 'b' changed values
282 * EXPECT_TRUE (c == b);
283 * \endcode
284 *
285 * \par Example Usage
286 * \code
287 * static shared_ptr<_IRep> Clone_ (const _IRep& rep); // if you need fancier logic to clone your shared_ptr value
288 * using _SharedByValueRepType = SharedByValue<_IRep, SharedByValueSupport::DefaultTraits<_IRep, &Clone_>>;
289 * \endcode
290 *
291 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
292 *
293 * Understand that this works because SharedByValue objects are really shared_ptr, but with copy by value semantics.
294 * C++-Standard-Thread-Safety means that the envelope is always safe because its just following standard c++
295 * rules for copying the shared_ptr.
296 *
297 * And copying the indirected shared_ptr is always safe because the ONLY time anyone can ever MODIFY
298 * an object is if the shared_count == 1 (so no other threads using it).
299 *
300 * \note Design choice: embed fCopier into instance
301 * vs. just constructing the object on the fly the way we do for comparison functions like std::less<T> {} etc.
302 *
303 * PRO embed: If constructor cost for COPIER non-trivial, best todo once. If size can be zero, doesn't really
304 * matter/cost anything ([[no_unique_address]]/qStroika_ATTRIBUTE_NO_UNIQUE_ADDRESS). If you want to have some data in copier, and have that specific to
305 * the instance (I can think of no use case for this) - very tricky unless embedded.
306 *
307 * PRO NOT EMBED: Simpler todo access functions (default parameter instead of overload passing fCopier).
308 * For now - go with more flexible approach since not much more complex to implement.
309 *
310 * \note <a href="Design-Overview.md#Comparisons">Comparisons</a>:
311 * o Only comparison (operator==/!=) with nullptr is supported.
312 *
313 * Earlier versions of Stroika (before 2.1a5) supported operator==(SharedByValue) - and this kind of makes sense
314 * but is a little ambiguous if its measuring pointer (shared reference) equality or actual value equality.
315 *
316 * Better to let the caller use operator<=> on cget() or *cget() to make clear their intentions.
317 */
318 template <typename T, SharedByValueSupport::ITraits<T> TRAITS = SharedByValueSupport::DefaultTraits<T>>
320 public:
322 using TraitsType = TRAITS;
323
324 public:
325 using element_type = typename TRAITS::element_type;
326 using element_copier_type = typename TRAITS::element_copier_type;
327 using shared_ptr_type = typename TRAITS::shared_ptr_type;
328 using MissingCopierTypeSentinel = SharedByValueSupport::MissingCopierTypeSentinel;
329 using instance_defined_copier_type = typename TRAITS::instance_defined_copier_type;
330 using default_copier_type = typename TRAITS::default_copier_type;
331
332 public:
333 static_assert (same_as<T, typename TRAITS::element_type>);
334
335 public:
336 /**
337 * SharedByValue::SharedByValue():
338 * The constructor takes either no/args to nullptr, to construct an empty SharedByValue.
339 *
340 * It can be copied by another copy of the same kind (including same kind of copier).
341 *
342 * Or it can be explicitly constructed from a SHARED_IMPL (any existing shared_ptr, along
343 * with a copier (defaults to DefaultValueCopier).
344 *
345 * You can also copy a straight 'element_type' value into a SharedByValue.
346 *
347 * \note prior to Stroika v3.0d23 you could pass a bare pointer that
348 * pointer will be wrapped in a shared_ptr and the SharedByValue()
349 * will take ownership of the lifetime of that pointer. BUt I decided rarely useful and
350 * easy to manually wrap in an explicit shared_ptr{}, and then the behavior provides less
351 * chance to surprise.
352 */
353#if qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
354 SharedByValue () noexcept
355 requires (not same_as<default_copier_type, MissingCopierTypeSentinel>)
356 : fSharedImpl_{}
357 {
358 if constexpr (not same_as<instance_defined_copier_type, MissingCopierTypeSentinel>) {
359 fCopier_ = TRAITS::kDefaultCopier;
360 }
361 }
362 SharedByValue ([[maybe_unused]] nullptr_t) noexcept
363 requires (not same_as<default_copier_type, MissingCopierTypeSentinel>)
364 : fSharedImpl_{}
365 {
366 if constexpr (not same_as<instance_defined_copier_type, MissingCopierTypeSentinel>) {
367 fCopier_ = TRAITS::kDefaultCopier;
368 }
369 }
370#else
371 SharedByValue () noexcept
372 requires (not same_as<default_copier_type, MissingCopierTypeSentinel>);
373 SharedByValue (nullptr_t n) noexcept
374 requires (not same_as<default_copier_type, MissingCopierTypeSentinel>);
375#endif
376 SharedByValue (SharedByValue&& from) noexcept = default;
377 SharedByValue (const SharedByValue& from) noexcept = default;
378#if qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
379 SharedByValue (const shared_ptr_type& from) noexcept
380 requires (not same_as<default_copier_type, MissingCopierTypeSentinel>)
381 : fSharedImpl_{from}
382 {
383 if constexpr (not same_as<instance_defined_copier_type, MissingCopierTypeSentinel>) {
384 fCopier_ = TRAITS::kDefaultCopier;
385 }
386 }
387 SharedByValue (const element_type& from)
388 requires (not same_as<default_copier_type, MissingCopierTypeSentinel>)
389 : fSharedImpl_{TRAITS::kDefaultCopier (from)}
390 {
391 if constexpr (not same_as<instance_defined_copier_type, MissingCopierTypeSentinel>) {
392 fCopier_ = TRAITS::kDefaultCopier;
393 }
394 }
395 SharedByValue (shared_ptr_type&& from) noexcept
396 requires (not same_as<default_copier_type, MissingCopierTypeSentinel>)
397 : fSharedImpl_{move (from)}
398 {
399 if constexpr (not same_as<instance_defined_copier_type, MissingCopierTypeSentinel>) {
400 fCopier_ = TRAITS::kDefaultCopier;
401 }
402 }
403 SharedByValue (const shared_ptr_type& from, const instance_defined_copier_type& copier) noexcept
404 requires (not same_as<instance_defined_copier_type, MissingCopierTypeSentinel>)
405 : fCopier_{copier}
406 , fSharedImpl_{from}
407 {
408 }
409 SharedByValue (const element_type& from, const instance_defined_copier_type& copier)
410 requires (not same_as<instance_defined_copier_type, MissingCopierTypeSentinel>)
411 : fCopier_{copier}
412 , fSharedImpl_{copier (from)}
413 {
414 }
415 SharedByValue (shared_ptr_type&& from, const instance_defined_copier_type&& copier) noexcept
416 requires (not same_as<instance_defined_copier_type, MissingCopierTypeSentinel>)
417 : fCopier_{move (copier)}
418 , fSharedImpl_{move (from)}
419 {
420 }
421#else
422 explicit SharedByValue (const element_type& from)
423 requires (not same_as<default_copier_type, MissingCopierTypeSentinel>);
424 explicit SharedByValue (const shared_ptr_type& from) noexcept
425 requires (not same_as<default_copier_type, MissingCopierTypeSentinel>);
426 explicit SharedByValue (shared_ptr_type&& from) noexcept
427 requires (not same_as<default_copier_type, MissingCopierTypeSentinel>);
428 SharedByValue (const element_type& from, const instance_defined_copier_type& copier)
429 requires (not same_as<instance_defined_copier_type, MissingCopierTypeSentinel>);
430 SharedByValue (const shared_ptr_type& from, const instance_defined_copier_type& copier) noexcept
431 requires (not same_as<instance_defined_copier_type, MissingCopierTypeSentinel>);
432 SharedByValue (shared_ptr_type&& from, const instance_defined_copier_type&& copier) noexcept
433 requires (not same_as<instance_defined_copier_type, MissingCopierTypeSentinel>);
434#endif
435
436 public:
437 nonvirtual SharedByValue& operator= (SharedByValue&& src) noexcept = default;
438 nonvirtual SharedByValue& operator= (const SharedByValue& src) noexcept = default;
439 nonvirtual SharedByValue& operator= (shared_ptr_type&& from) noexcept;
440 nonvirtual SharedByValue& operator= (const shared_ptr_type& from) noexcept;
441
442 public:
443 /**
444 * @brief returns true iff sharedptr is not null
445 *
446 * \see https://en.cppreference.com/w/cpp/memory/shared_ptr/operator_bool.html
447 */
448 nonvirtual explicit operator bool () const noexcept;
449
450 public:
451 /**
452 * \brief access te underlying shared_ptr stored in the SharedByValue. This should be treated as readonly and
453 * only used to make calls that don't change / mutate the underlying object.
454 *
455 * \todo @todo Consider if using const somehow can help make this safer - returning a shared_ptr<const T>?? somehow
456 */
457 nonvirtual shared_ptr_type cget_ptr () const;
458
459 public:
460 /**
461 * \brief forced copy of the underlying shared_ptr data
462 *
463 * \note In Stroika v2.1, this was broken.
464 */
465 nonvirtual shared_ptr_type rwget_ptr ();
466 template <typename COPIER>
467 nonvirtual shared_ptr_type rwget_ptr (COPIER&& copier);
468
469 public:
470 /**
471 * rwget () returns the real underlying (modifiable) ptr we store. It can be nullptr.
472 *
473 * Importantly, it makes sure that there is at most one reference to the 'shared_ptr' value
474 * before returning that pointer, so the caller is the only one modifying the object.
475 *
476 * The no-arg overload uses the builtin copier (overwhelmingly most common), but occasionally its helpful
477 * to specify an alternate copier (see CONTAINER::_GetWritableRepAndPatchAssociatedIterator for example).
478 */
479 nonvirtual element_type* rwget ();
480 template <typename COPIER>
481 nonvirtual element_type* rwget (COPIER&& copier);
482
483 public:
484 /**
485 * cget returns returns the real underlying const ptr we store.
486 *
487 * \em Note: cget () will never invoke BreakReferences/Clone.
488 *
489 * To get a non-const pointer, @see rwget ()
490 */
491 nonvirtual const element_type* cget () const noexcept;
492
493 public:
494 /**
495 * These operators require that the underlying ptr is non-nil.
496 *
497 * \em note - the non-const overloads of operator-> and operator* only work if you use a COPY function
498 * that takes no arguments (otherwise there are no arguments to pass to the clone/copy function).
499 *
500 * You can always safely use the copy overload.
501 *
502 * \note This can be confusing, because at the point of call, its unclear if this may invoke BreakReferences or not
503 */
504 nonvirtual const element_type* operator->() const;
505 nonvirtual element_type* operator->();
506
507 public:
508 /**
509 * These operators require that the underlying ptr is non-null.
510 */
511 nonvirtual const element_type& operator* () const;
512
513 public:
514 /**
515 */
516 constexpr bool operator== (nullptr_t) const;
517
518 public:
519 /**
520 */
521 nonvirtual element_copier_type GetElementCopier () const;
522
523 public:
524 /**
525 * @see SharingState.
526 *
527 * Note that two subsequent calls on an object CAN return different answers, without any calls to 'this' object.
528 * That's because another shared copy can lose a reference. So - if this once returns 'shared', it might later return
529 * solo, without any change to THIS object.
530 */
531 nonvirtual SharingState GetSharingState () const;
532
533 public:
534 /**
535 * Returns true if there is exactly one object referenced. Note that if empty () - then not unique().
536 */
537 nonvirtual bool unique () const;
538
539 public:
540 /**
541 * Returns the number of references to the underlying shared pointer.
542 *
543 * @see SharingState
544 */
545 nonvirtual unsigned int use_count () const;
546
547 private:
548 using DeclaredInstanceCopierType_ =
549 conditional_t<same_as<instance_defined_copier_type, MissingCopierTypeSentinel>, Common::Empty, instance_defined_copier_type>;
550 shared_ptr_type fSharedImpl_;
551 qStroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE DeclaredInstanceCopierType_ fCopier_; // often zero sized
552
553 public:
554 /**
555 * Assure there are at most N (typically one or 2) references to this object, and if there are more, break references.
556 * This method should be applied before destructive operations are applied to the shared object.
557 *
558 * Argument copier is typically fCopier_
559 *
560 * \note - QUEER - copies generated by BreakReferences_() use the original fCopier_, not the argument copier.
561 */
562 template <typename COPIER>
563 nonvirtual void AssureNOrFewerReferences (COPIER&& copier, unsigned int n = 1u);
564 nonvirtual void AssureNOrFewerReferences (unsigned int n = 1u);
565
566 private:
567 template <typename COPIER>
568 nonvirtual void BreakReferences_ (COPIER&& copier);
569 };
570 // NOT strictly gauranteed by C++, but we want to be warned if this ever fails, and correct or if we must
571 // conditionalize the test
572 static_assert (same_as<SharedByValue<int>::TraitsType, SharedByValueSupport::DefaultTraits_NoInstanceCopier<int>>); // if this fails, next one is meaningless
573 static_assert (sizeof (SharedByValue<int>) == sizeof (shared_ptr<int>)); // no space overhead for copier (by default)
574
575}
576
577/*
578 ********************************************************************************
579 ***************************** Implementation Details ***************************
580 ********************************************************************************
581 */
582#include "SharedByValue.inl"
583
584#endif /*_Stroika_Foundation_Memory_SharedByValue_h_*/
#define qStroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE
[[msvc::no_unique_address]] isn't always broken in MSVC. Annotate with this on things where its not b...
Definition StdCompat.h:445
SharedByValue is a utility class to implement Copy-On-Write (aka COW) - sort of halfway between uniqu...
nonvirtual unsigned int use_count() const
nonvirtual shared_ptr_type cget_ptr() const
access te underlying shared_ptr stored in the SharedByValue. This should be treated as readonly and o...
nonvirtual shared_ptr_type rwget_ptr()
forced copy of the underlying shared_ptr data
nonvirtual const element_type * cget() const noexcept
nonvirtual const element_type * operator->() const
nonvirtual void AssureNOrFewerReferences(COPIER &&copier, unsigned int n=1u)
nonvirtual SharingState GetSharingState() const
nonvirtual const element_type & operator*() const
check T has had remove_cvref_t called on it (e.g. ICVRefTd<const string&> is string)
Definition Concepts.h:453
SHARED_IMPL DefaultValueCopier(const T &t)
DefaultValueCopier is the default value-copier for copying SharedByValue (thing that clones a shared_...
DefaultValueCopier_FunctionObject same as DefaultValueCopier, but as a function object.
ExplicitTraits is a utility struct to provide parameterized TRAITS support for SharedByValue<>
conditional_t< same_as< instance_defined_copier_type, MissingCopierTypeSentinel >, function< shared_ptr_type(const T &)>, instance_defined_copier_type > element_copier_type
remove_cvref_t< INSTANCE_COPIER_TYPE > instance_defined_copier_type
remove_cvref_t< SHARED_IMPL > shared_ptr_type
shared_ptr<T> typically, but could be another 'shared_ptr'-like class
remove_cvref_t< T > element_type
SharedByValue 'of T' type.
remove_cvref_t< DEFAULT_COPIER_TYPE > default_copier_type
satisfies IOptionalCopier, and used as default for instance copier, and if no instance copier,...