1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #ifndef INCLUDED_O3TL_COW_WRAPPER_HXX
21 #define INCLUDED_O3TL_COW_WRAPPER_HXX
29 /** Thread-unsafe refcounting
31 This is the default locking policy for cow_wrapper. No
32 locking/guarding against concurrent access is performed
35 struct UnsafeRefCountingPolicy
37 typedef std::size_t ref_count_t
;
38 static void incrementCount( ref_count_t
& rCount
) { ++rCount
; }
39 static bool decrementCount( ref_count_t
& rCount
) { return --rCount
!= 0; }
40 static std::size_t getCount( ref_count_t
& rCount
) { return rCount
; }
43 /** Thread-safe refcounting
45 Use this to have the cow_wrapper refcounting mechanisms employ
46 the thread-safe oslInterlockedCount .
48 struct ThreadSafeRefCountingPolicy
50 typedef std::atomic
<int> ref_count_t
;
51 static void incrementCount( ref_count_t
& rCount
) { rCount
++; }
52 static bool decrementCount( ref_count_t
& rCount
)
54 return (--rCount
) != 0;
56 static std::size_t getCount( ref_count_t
& rCount
) { return rCount
; }
59 /** Copy-on-write wrapper.
61 This template provides copy-on-write semantics for the wrapped
62 type: when copying, the operation is performed shallow,
63 i.e. different cow_wrapper objects share the same underlying
64 instance. Only when accessing the underlying object via
65 non-const methods, a unique copy is provided.
67 The type parameter <code>T</code> must satisfy the following
68 requirements: it must be default-constructible, copyable (it
69 need not be assignable), and be of non-reference type. Note
70 that, despite the fact that this template provides access to
71 the wrapped type via pointer-like methods
72 (<code>operator->()</code> and <code>operator*()</code>), it does
73 <em>not</em> work like e.g. the std smart pointer wrappers
74 (shared_ptr, unique_ptr, etc.). Internally, the cow_wrapper
75 holds a by-value instance of the wrapped object. This is to
76 avoid one additional heap allocation, and providing access via
77 <code>operator->()</code>/<code>operator*()</code> is because
78 <code>operator.()</code> cannot be overridden.
80 Regarding thread safety: this wrapper is <em>not</em>
81 thread-safe per se, because cow_wrapper has no way of
82 synchronizing the potentially many different cow_wrapper
83 instances, that reference a single shared value_type
84 instance. That said, when passing
85 <code>ThreadSafeRefCountingPolicy</code> as the
86 <code>MTPolicy</code> parameter, accessing a thread-safe
87 pointee through multiple cow_wrapper instances might be
88 thread-safe, if the individual pointee methods are
89 thread-safe, <em>including</em> pointee's copy
90 constructor. Any wrapped object that needs external
91 synchronisation (e.g. via an external mutex, which arbitrates
92 access to object methods, and can be held across multiple
93 object method calls) cannot easily be dealt with in a
94 thread-safe way, because, as noted, objects are shared behind
97 @attention if one wants to use the pimpl idiom together with
98 cow_wrapper (i.e. put an opaque type into the cow_wrapper),
99 then <em>all<em> methods in the surrounding class needs to be
100 non-inline (<em>including</em> destructor, copy constructor
101 and assignment operator).
105 class cow_wrapper_client_impl;
107 class cow_wrapper_client
110 cow_wrapper_client();
111 cow_wrapper_client( const cow_wrapper_client& );
112 cow_wrapper_client( cow_wrapper_client&& );
113 ~cow_wrapper_client();
115 cow_wrapper_client& operator=( const cow_wrapper_client& );
116 cow_wrapper_client& operator=( cow_wrapper_client&& );
118 void modify( int nVal );
119 int queryUnmodified() const;
122 o3tl::cow_wrapper< cow_wrapper_client_impl > maImpl;
125 and the implementation file would look like this:
127 class cow_wrapper_client_impl
130 void setValue( int nVal ) { mnValue = nVal; }
131 int getValue() const { return mnValue; }
137 cow_wrapper_client::cow_wrapper_client() :
141 cow_wrapper_client::cow_wrapper_client( const cow_wrapper_client& rSrc ) :
142 maImpl( rSrc.maImpl )
145 cow_wrapper_client::cow_wrapper_client( cow_wrapper_client& rSrc ) :
146 maImpl( std::move( rSrc.maImpl ) )
149 cow_wrapper_client::~cow_wrapper_client()
152 cow_wrapper_client& cow_wrapper_client::operator=( const cow_wrapper_client& rSrc )
154 maImpl = rSrc.maImpl;
157 cow_wrapper_client& cow_wrapper_client::operator=( cow_wrapper_client&& rSrc )
159 maImpl = std::move( rSrc.maImpl );
162 void cow_wrapper_client::modify( int nVal )
164 maImpl->setValue( nVal );
166 int cow_wrapper_client::queryUnmodified() const
168 return maImpl->getValue();
172 template<typename T
, class MTPolicy
=UnsafeRefCountingPolicy
> class cow_wrapper
174 /** shared value object - gets cloned before cow_wrapper hands
175 out a non-const reference to it
179 impl_t(const impl_t
&) = delete;
180 impl_t
& operator=(const impl_t
&) = delete;
188 explicit impl_t( const T
& v
) :
194 explicit impl_t( T
&& v
) :
195 m_value(std::move(v
)),
201 typename
MTPolicy::ref_count_t m_ref_count
;
206 if( m_pimpl
&& !MTPolicy::decrementCount(m_pimpl
->m_ref_count
) )
214 typedef T value_type
;
216 typedef const T
* const_pointer
;
217 typedef MTPolicy mt_policy
;
219 /** Default-construct wrapped type instance
222 m_pimpl( new impl_t() )
226 /** Copy-construct wrapped type instance from given object
228 explicit cow_wrapper( const value_type
& r
) :
229 m_pimpl( new impl_t(r
) )
233 /** Move-construct wrapped type instance from given object
235 explicit cow_wrapper( value_type
&& r
) :
236 m_pimpl( new impl_t(std::move(r
)) )
240 /** Shallow-copy given cow_wrapper
242 explicit cow_wrapper( const cow_wrapper
& rSrc
) : // nothrow
243 m_pimpl( rSrc
.m_pimpl
)
245 MTPolicy::incrementCount( m_pimpl
->m_ref_count
);
248 /** Move-construct and steal rSrc shared resource
250 explicit cow_wrapper( cow_wrapper
&& rSrc
) noexcept
:
251 m_pimpl( rSrc
.m_pimpl
)
253 rSrc
.m_pimpl
= nullptr;
256 // Only intended to be used by std::optional specialisations
257 explicit cow_wrapper( std::nullopt_t
) noexcept
:
262 // Only intended to be used by std::optional specialisations
263 explicit cow_wrapper( const cow_wrapper
& rSrc
, std::nullopt_t
) : // nothrow
264 m_pimpl( rSrc
.m_pimpl
)
267 MTPolicy::incrementCount( m_pimpl
->m_ref_count
);
270 ~cow_wrapper() // nothrow, if ~T does not throw
275 /// now sharing rSrc cow_wrapper instance with us
276 cow_wrapper
& operator=( const cow_wrapper
& rSrc
) // nothrow
278 // this already guards against self-assignment
279 MTPolicy::incrementCount( rSrc
.m_pimpl
->m_ref_count
);
282 m_pimpl
= rSrc
.m_pimpl
;
287 /// stealing rSrc's resource
288 cow_wrapper
& operator=(cow_wrapper
&& rSrc
) noexcept
290 // self-movement guts ourself, see also 17.6.4.9
292 m_pimpl
= rSrc
.m_pimpl
;
294 rSrc
.m_pimpl
= nullptr;
299 /// unshare with any other cow_wrapper instance
300 value_type
& make_unique()
302 if( m_pimpl
->m_ref_count
> 1 )
304 impl_t
* pimpl
= new impl_t(m_pimpl
->m_value
);
309 return m_pimpl
->m_value
;
312 /// true, if not shared with any other cow_wrapper instance
313 bool is_unique() const // nothrow
315 return !m_pimpl
|| m_pimpl
->m_ref_count
== 1;
318 /// return number of shared instances (1 for unique object)
319 size_t use_count() const // nothrow
321 return m_pimpl
? MTPolicy::getCount(m_pimpl
->m_ref_count
) : 0;
324 void swap(cow_wrapper
& r
) // never throws
326 std::swap(m_pimpl
, r
.m_pimpl
);
329 pointer
operator->() { return &make_unique(); }
330 value_type
& operator*() { return make_unique(); }
331 const_pointer
operator->() const { return &m_pimpl
->m_value
; }
332 const value_type
& operator*() const { return m_pimpl
->m_value
; }
334 pointer
get() { return &make_unique(); }
335 const_pointer
get() const { return &m_pimpl
->m_value
; }
337 /// true, if both cow_wrapper internally share the same object
338 bool same_object( const cow_wrapper
& rOther
) const
340 return rOther
.m_pimpl
== m_pimpl
;
343 // Only intended to be used by std::optional specialisations
344 bool empty() const { return m_pimpl
== nullptr; }
345 // Only intended to be used by std::optional specialisations
360 template<class T
, class P
> inline bool operator==( const cow_wrapper
<T
,P
>& a
,
361 const cow_wrapper
<T
,P
>& b
)
363 return a
.same_object(b
) || *a
== *b
;
366 template<class T
, class P
> inline bool operator!=( const cow_wrapper
<T
,P
>& a
,
367 const cow_wrapper
<T
,P
>& b
)
369 return !a
.same_object(b
) && *a
!= *b
;
372 template<class A
, class B
, class P
> inline bool operator<( const cow_wrapper
<A
,P
>& a
,
373 const cow_wrapper
<B
,P
>& b
)
378 template<class T
, class P
> inline void swap( cow_wrapper
<T
,P
>& a
,
379 cow_wrapper
<T
,P
>& b
)
386 #endif /* INCLUDED_O3TL_COW_WRAPPER_HXX */
388 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */