Simbody  3.7
CloneOnWritePtr.h
Go to the documentation of this file.
1 #ifndef SimTK_SimTKCOMMON_CLONE_ON_WRITE_PTR_H_
2 #define SimTK_SimTKCOMMON_CLONE_ON_WRITE_PTR_H_
3 
4 /* -------------------------------------------------------------------------- *
5  * Simbody(tm): SimTKcommon *
6  * -------------------------------------------------------------------------- *
7  * This is part of the SimTK biosimulation toolkit originating from *
8  * Simbios, the NIH National Center for Physics-Based Simulation of *
9  * Biological Structures at Stanford, funded under the NIH Roadmap for *
10  * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody. *
11  * *
12  * Portions copyright (c) 2015 Stanford University and the Authors. *
13  * Authors: Michael Sherman *
14  * Contributors: *
15  * *
16  * Licensed under the Apache License, Version 2.0 (the "License"); you may *
17  * not use this file except in compliance with the License. You may obtain a *
18  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0. *
19  * *
20  * Unless required by applicable law or agreed to in writing, software *
21  * distributed under the License is distributed on an "AS IS" BASIS, *
22  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
23  * See the License for the specific language governing permissions and *
24  * limitations under the License. *
25  * -------------------------------------------------------------------------- */
26 
28 
29 #include <memory>
30 #include <iosfwd>
31 #include <cassert>
32 
33 namespace SimTK {
34 
35 //==============================================================================
36 // CLONE ON WRITE PTR
37 //==============================================================================
59 template <class T> class CloneOnWritePtr {
60 public:
61  typedef T element_type;
62  typedef T* pointer;
63  typedef T& reference;
64 
71  CloneOnWritePtr() noexcept {init();}
72 
76  CloneOnWritePtr(std::nullptr_t) noexcept : CloneOnWritePtr() {}
77 
81  explicit CloneOnWritePtr(T* x) : CloneOnWritePtr()
82  { if (x) {p=x; count=new long(1);} }
83 
89  explicit CloneOnWritePtr(const T* x) : CloneOnWritePtr(cloneOrNull(x)) {}
90 
95  explicit CloneOnWritePtr(const T& x) : CloneOnWritePtr(&x) {}
96 
103  { shareWith(src); }
104 
107  template <class U>
109  { shareWith<U>(src); }
110 
115  { moveFrom(std::move(src)); }
116 
119  template <class U>
121  { moveFrom<U>(std::move(src)); }
133  CloneOnWritePtr& operator=(const CloneOnWritePtr& src) noexcept {
134  if (src.p != p)
135  { reset(); shareWith(src); }
136  return *this;
137  }
138 
141  template <class U>
143  if (static_cast<T*>(src.p) != p)
144  { reset(); shareWith<U>(src); }
145  return *this;
146  }
147 
155  if (&src != this)
156  { reset(); moveFrom(std::move(src)); }
157  return *this;
158  }
159 
162  template <class U>
164  // Can't be the same container since the type is different.
165  reset(); moveFrom<U>(std::move(src));
166  return *this;
167  }
168 
175  { reset(cloneOrNull(&x)); return *this; }
176 
181  CloneOnWritePtr& operator=(T* x) noexcept
182  { reset(x); return *this; }
189  ~CloneOnWritePtr() noexcept {reset();}
200  const T* get() const noexcept {return p;}
201 
208  T* upd() {detach(); return p;}
209 
213  const T& getRef() const {
214  SimTK_ERRCHK(!empty(), "CloneOnWritePtr::getRef()",
215  "An attempt was made to dereference a null pointer.");
216  return *get();
217  }
218 
222  T& updRef() {
223  SimTK_ERRCHK(!empty(), "CloneOnWritePtr::updRef()",
224  "An attempt was made to dereference a null pointer.");
225  return *upd();
226  }
227 
234  const T* operator->() const { return &getRef(); }
235 
238  T* operator->() { return &updRef(); }
239 
246  const T& operator*() const {return getRef();}
247 
250  T& operator*() {return updRef();}
260  void reset() noexcept {
261  if (empty()) return;
262  if (decr()==0) {delete p; delete count;}
263  init();
264  }
265 
271  void reset(T* x) { // could throw when allocating count
272  if (x != p) {
273  reset();
274  if (x) {p=x; count=new long(1);}
275  }
276  }
277 
282  void swap(CloneOnWritePtr& other) noexcept {
283  std::swap(p, other.p);
284  std::swap(count, other.count);
285  }
286 
291  long use_count() const noexcept {return count ? *count : 0;}
292 
296  bool unique() const noexcept {return use_count()==1;}
297 
301  bool empty() const noexcept {return !p;} // count should be null also
302 
305  explicit operator bool() const noexcept {return !empty();}
306 
314  T* release() { // could throw during detach()
315  detach(); // now use count is 1 or 0
316  T* save = p; delete count; init();
317  return save;
318  }
319 
329  void detach() { // can throw during clone()
330  if (use_count() > 1)
331  { decr(); p=p->clone(); count=new long(1); }
332  }
335 private:
336 template <class U> friend class CloneOnWritePtr;
337 
338  // If src is non-null, clone it; otherwise return null.
339  static T* cloneOrNull(const T* src) {
340  return src ? src->clone() : nullptr;
341  }
342 
343  // Set an empty pointer to share with the given object. Type U* must be
344  // implicitly convertible to type T*.
345  template <class U> void shareWith(const CloneOnWritePtr<U>& src) noexcept {
346  assert(!(p||count));
347  if (!src.empty()) {p=src.p; count=src.count; incr();}
348  }
349 
350  // Steal the object and count from the source to initialize this *empty*
351  // pointer, leaving the source empty.
352  template <class U> void moveFrom(CloneOnWritePtr<U>&& src) noexcept {
353  assert(!(p||count));
354  p=src.p; count=src.count; src.init();
355  }
356 
357  // Increment/decrement use count and return the result.
358  long incr() const noexcept {assert(count && *count>=0); return ++(*count);}
359  long decr() const noexcept {assert(count && *count>=1); return --(*count);}
360 
361  void init() noexcept {p=nullptr; count=nullptr;}
362 
363  // Can't use std::shared_ptr here due to lack of release() method.
364  T* p; // this may be null
365  long* count; // if p is null so is count
366 };
367 
368 
369 //==============================================================================
370 // SimTK namespace-scope functions
371 //==============================================================================
372 // These namespace-scope functions will be resolved by the compiler using
373 // "Koenig lookup" which examines the arguments' namespaces first.
374 // See Herb Sutter's discussion here: http://www.gotw.ca/publications/mill08.htm.
375 
380 template <class T> inline void
382  p1.swap(p2);
383 }
384 
388 template <class charT, class traits, class T>
389 inline std::basic_ostream<charT,traits>&
390 operator<<(std::basic_ostream<charT,traits>& os,
391  const CloneOnWritePtr<T>& p)
392 { os << p.get(); return os; }
393 
399 template <class T, class U>
400 inline bool operator==(const CloneOnWritePtr<T>& lhs,
401  const CloneOnWritePtr<U>& rhs)
402 { return lhs.get() == rhs.get(); }
403 
406 template <class T>
407 inline bool operator==(const CloneOnWritePtr<T>& lhs, std::nullptr_t)
408 { return lhs.empty(); }
409 
412 template <class T>
413 inline bool operator==(std::nullptr_t, const CloneOnWritePtr<T>& rhs)
414 { return rhs.empty(); }
415 
422 template <class T, class U>
423 inline bool operator<(const CloneOnWritePtr<T>& lhs,
424  const CloneOnWritePtr<U>& rhs)
425 { return lhs.get() < rhs.get(); }
426 
431 template <class T>
432 inline bool operator<(const CloneOnWritePtr<T>& lhs, std::nullptr_t)
433 { return false; }
434 
439 template <class T>
440 inline bool operator<(std::nullptr_t, const CloneOnWritePtr<T>& rhs)
441 { return !rhs.empty(); }
442 
443 
444 // These functions are derived from operator== and operator<.
445 
448 template <class T, class U>
449 inline bool operator!=(const CloneOnWritePtr<T>& lhs,
450  const CloneOnWritePtr<U>& rhs)
451 { return !(lhs==rhs); }
454 template <class T>
455 inline bool operator!=(const CloneOnWritePtr<T>& lhs, std::nullptr_t)
456 { return !(lhs==nullptr); }
459 template <class T>
460 inline bool operator!=(std::nullptr_t, const CloneOnWritePtr<T>& rhs)
461 { return !(nullptr==rhs); }
462 
465 template <class T, class U>
466 inline bool operator>(const CloneOnWritePtr<T>& lhs,
467  const CloneOnWritePtr<U>& rhs)
468 { return rhs < lhs; }
471 template <class T>
472 inline bool operator>(const CloneOnWritePtr<T>& lhs, std::nullptr_t)
473 { return nullptr < lhs; }
474 
477 template <class T>
478 inline bool operator>(std::nullptr_t, const CloneOnWritePtr<T>& rhs)
479 { return rhs < nullptr; }
480 
481 
484 template <class T, class U>
485 inline bool operator>=(const CloneOnWritePtr<T>& lhs,
486  const CloneOnWritePtr<U>& rhs)
487 { return !(lhs < rhs); }
490 template <class T>
491 inline bool operator>=(const CloneOnWritePtr<T>& lhs, std::nullptr_t)
492 { return !(lhs < nullptr); }
493 
496 template <class T>
497 inline bool operator>=(std::nullptr_t, const CloneOnWritePtr<T>& rhs)
498 { return !(nullptr < rhs); }
499 
500 
504 template <class T, class U>
505 inline bool operator<=(const CloneOnWritePtr<T>& lhs,
506  const CloneOnWritePtr<U>& rhs)
507 { return !(rhs < lhs); }
511 template <class T>
512 inline bool operator<=(const CloneOnWritePtr<T>& lhs, std::nullptr_t)
513 { return !(nullptr < lhs); }
517 template <class T>
518 inline bool operator<=(std::nullptr_t, const CloneOnWritePtr<T>& rhs)
519 { return !(rhs < nullptr); }
520 
521 } // namespace SimTK
522 
523 #endif // SimTK_SimTKCOMMON_CLONE_ON_WRITE_PTR_H_
T * operator->()
Clone if necessary, then dereference a writable pointer to the contained object.
Definition: CloneOnWritePtr.h:238
CloneOnWritePtr & operator=(const CloneOnWritePtr< U > &src) noexcept
Copy assignment from a compatible CloneOnWritePtr.
Definition: CloneOnWritePtr.h:142
Smart pointer with deep copy semantics but with the copying delayed until an attempt is made to write...
Definition: CloneOnWritePtr.h:59
T & updRef()
Clone if necessary to ensure the contained object is not shared, then return a writable reference to ...
Definition: CloneOnWritePtr.h:222
CloneOnWritePtr() noexcept
Default constructor stores a nullptr and sets use count to zero.
Definition: CloneOnWritePtr.h:71
CloneOnWritePtr(T *x)
Given a pointer to a writable heap-allocated object, take over ownership of that object.
Definition: CloneOnWritePtr.h:81
CloneOnWritePtr(const T *x)
Given a pointer to a read-only object, create a new heap-allocated copy of that object via its clone(...
Definition: CloneOnWritePtr.h:89
T * release()
(Advanced) Remove the contained object from management by this container and transfer ownership to th...
Definition: CloneOnWritePtr.h:314
const T & operator*() const
This "dereference" operator returns a const reference to the contained object.
Definition: CloneOnWritePtr.h:246
CloneOnWritePtr(const CloneOnWritePtr< U > &src) noexcept
Copy construction from a compatible CloneOnWritePtr.
Definition: CloneOnWritePtr.h:108
This is the top-level SimTK namespace into which all SimTK names are placed to avoid collision with o...
Definition: Assembler.h:37
long use_count() const noexcept
Return count of how many CloneOnWritePtr objects are currently sharing the referenced object...
Definition: CloneOnWritePtr.h:291
bool operator>(std::nullptr_t, const CloneOnWritePtr< T > &rhs)
nullptr greater-than test defined as rhs < nullptr.
Definition: CloneOnWritePtr.h:478
CloneOnWritePtr & operator=(const CloneOnWritePtr &src) noexcept
Copy assignment replaces the currently-held object by a deferred copy of the object held in the sourc...
Definition: CloneOnWritePtr.h:133
bool operator!=(const CloneOnWritePtr< T > &lhs, std::nullptr_t)
nullptr inequality test defined as !(lhs==nullptr).
Definition: CloneOnWritePtr.h:455
CloneOnWritePtr(const T &x)
Given a read-only reference to an object, create a new heap-allocated copy of that object via its clo...
Definition: CloneOnWritePtr.h:95
bool empty() const noexcept
Return true if this container is empty, which is the state the container is in immediately after defa...
Definition: CloneOnWritePtr.h:301
~CloneOnWritePtr() noexcept
Destructor decrements the reference count and deletes the object if the count goes to zero...
Definition: CloneOnWritePtr.h:189
void detach()
(Advanced) Force the contained object to be unique, that is, not shared with any other container...
Definition: CloneOnWritePtr.h:329
bool operator>=(const CloneOnWritePtr< T > &lhs, const CloneOnWritePtr< U > &rhs)
Pointer greater-or-equal test defined as !(lhs < rhs).
Definition: CloneOnWritePtr.h:485
const T * operator->() const
Dereference a const pointer to the contained object.
Definition: CloneOnWritePtr.h:234
CloneOnWritePtr(std::nullptr_t) noexcept
Constructor from nullptr is the same as the default constructor.
Definition: CloneOnWritePtr.h:76
CloneOnWritePtr(CloneOnWritePtr &&src) noexcept
Move constructor is very fast and leaves the source empty.
Definition: CloneOnWritePtr.h:114
void reset(T *x)
Replace the contents of this container with the supplied heap-allocated object, taking over ownership...
Definition: CloneOnWritePtr.h:271
void swap(CloneOnWritePtr &other) noexcept
Swap the contents of this CloneOnWritePtr with another one, with ownership changing hands but no copy...
Definition: CloneOnWritePtr.h:282
void swap(CloneOnWritePtr< T > &p1, CloneOnWritePtr< T > &p2)
This is an overload of the STL std::swap() algorithm which uses the cheap built-in swap() member of t...
Definition: CloneOnWritePtr.h:381
CloneOnWritePtr(const CloneOnWritePtr &src) noexcept
Copy constructor is deep but deferred so very fast here; the new CloneOnWritePtr object initially sha...
Definition: CloneOnWritePtr.h:102
bool operator>=(const CloneOnWritePtr< T > &lhs, std::nullptr_t)
nullptr greater-or-equal test defined as !(lhs < nullptr).
Definition: CloneOnWritePtr.h:491
const T * get() const noexcept
Return a const pointer to the contained object if any, or nullptr.
Definition: CloneOnWritePtr.h:200
#define SimTK_ERRCHK(cond, whereChecked, msg)
Definition: ExceptionMacros.h:324
T * upd()
Clone if necessary to ensure the contained object is not shared, then return a writable pointer to th...
Definition: CloneOnWritePtr.h:208
bool operator==(std::nullptr_t, const CloneOnWritePtr< T > &rhs)
Comparison against nullptr; same as rhs.empty().
Definition: CloneOnWritePtr.h:413
CloneOnWritePtr & operator=(CloneOnWritePtr< U > &&src) noexcept
Move assignment from a compatible CloneOnWritePtr.
Definition: CloneOnWritePtr.h:163
T element_type
Type of the contained object.
Definition: CloneOnWritePtr.h:61
bool operator>(const CloneOnWritePtr< T > &lhs, const CloneOnWritePtr< U > &rhs)
Pointer greater-than test defined as rhs < lhs.
Definition: CloneOnWritePtr.h:466
Mandatory first inclusion for any Simbody source or header file.
bool operator>=(std::nullptr_t, const CloneOnWritePtr< T > &rhs)
nullptr greater-or-equal test defined as !(nullptr < rhs).
Definition: CloneOnWritePtr.h:497
bool operator==(const CloneOnWritePtr< T > &lhs, const CloneOnWritePtr< U > &rhs)
Compare for equality the managed pointers contained in two compatible CloneOnWritePtr containers...
Definition: CloneOnWritePtr.h:400
bool operator!=(std::nullptr_t, const CloneOnWritePtr< T > &rhs)
nullptr inequality test defined as !(nullptr==rhs).
Definition: CloneOnWritePtr.h:460
const T & getRef() const
Return a const reference to the contained object.
Definition: CloneOnWritePtr.h:213
bool unique() const noexcept
Is this the only user of the referenced object? Note that this means there is exactly one; if the man...
Definition: CloneOnWritePtr.h:296
CloneOnWritePtr & operator=(CloneOnWritePtr &&src) noexcept
Move assignment replaces the currently-held object by the source object, leaving the source empty...
Definition: CloneOnWritePtr.h:154
CloneOnWritePtr(CloneOnWritePtr< U > &&src) noexcept
Move construction from a compatible CloneOnWritePtr.
Definition: CloneOnWritePtr.h:120
bool operator>(const CloneOnWritePtr< T > &lhs, std::nullptr_t)
nullptr greater-than test defined as nullptr < lhs.
Definition: CloneOnWritePtr.h:472
T & operator*()
Clone if necessary, then return a writable reference to the contained object.
Definition: CloneOnWritePtr.h:250
T & reference
Type of a reference to the contained object.
Definition: CloneOnWritePtr.h:63
bool operator==(const CloneOnWritePtr< T > &lhs, std::nullptr_t)
Comparison against nullptr; same as lhs.empty().
Definition: CloneOnWritePtr.h:407
void reset() noexcept
Make this container empty, decrementing the use count of the contained object (if any)...
Definition: CloneOnWritePtr.h:260
T * pointer
Type of a pointer to the contained object.
Definition: CloneOnWritePtr.h:62
bool operator!=(const CloneOnWritePtr< T > &lhs, const CloneOnWritePtr< U > &rhs)
Pointer inequality test defined as !(lhs==rhs).
Definition: CloneOnWritePtr.h:449
CloneOnWritePtr & operator=(T *x) noexcept
This form of assignment replaces the currently-held object by the given source object and takes over ...
Definition: CloneOnWritePtr.h:181
CloneOnWritePtr & operator=(const T &x)
This form of assignment replaces the currently-held object by a heap-allocated copy of the source obj...
Definition: CloneOnWritePtr.h:174