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
24 #include <osl/interlck.h>
31 /** Thread-unsafe refcounting
33 This is the default locking policy for cow_wrapper. No
34 locking/guarding against concurrent access is performed
37 struct UnsafeRefCountingPolicy
39 typedef std::size_t ref_count_t
;
40 static void incrementCount( ref_count_t
& rCount
) { ++rCount
; }
41 static bool decrementCount( ref_count_t
& rCount
) { return --rCount
!= 0; }
44 /** Thread-safe refcounting
46 Use this to have the cow_wrapper refcounting mechanisms employ
47 the thread-safe oslInterlockedCount .
49 struct ThreadSafeRefCountingPolicy
51 typedef oslInterlockedCount ref_count_t
;
52 static void incrementCount( ref_count_t
& rCount
) { osl_atomic_increment(&rCount
); }
53 static bool decrementCount( ref_count_t
& rCount
)
55 if( rCount
== 1 ) // caller is already the only/last reference
58 return osl_atomic_decrement(&rCount
) != 0;
62 /** Copy-on-write wrapper.
64 This template provides copy-on-write semantics for the wrapped
65 type: when copying, the operation is performed shallow,
66 i.e. different cow_wrapper objects share the same underlying
67 instance. Only when accessing the underlying object via
68 non-const methods, a unique copy is provided.
70 The type parameter <code>T</code> must satisfy the following
71 requirements: it must be default-constructible, copyable (it
72 need not be assignable), and be of non-reference type. Note
73 that, despite the fact that this template provides access to
74 the wrapped type via pointer-like methods
75 (<code>operator->()</code> and <code>operator*()</code>), it does
76 <em>not</em> work like e.g. the std smart pointer wrappers
77 (shared_ptr, unique_ptr, etc.). Internally, the cow_wrapper
78 holds a by-value instance of the wrapped object. This is to
79 avoid one additional heap allocation, and providing access via
80 <code>operator->()</code>/<code>operator*()</code> is because
81 <code>operator.()</code> cannot be overridden.
83 Regarding thread safety: this wrapper is <em>not</em>
84 thread-safe per se, because cow_wrapper has no way of
85 synchronizing the potentially many different cow_wrapper
86 instances, that reference a single shared value_type
87 instance. That said, when passing
88 <code>ThreadSafeRefCountingPolicy</code> as the
89 <code>MTPolicy</code> parameter, accessing a thread-safe
90 pointee through multiple cow_wrapper instances might be
91 thread-safe, if the individual pointee methods are
92 thread-safe, <em>including</em> pointee's copy
93 constructor. Any wrapped object that needs external
94 synchronisation (e.g. via an external mutex, which arbitrates
95 access to object methods, and can be held across multiple
96 object method calls) cannot easily be dealt with in a
97 thread-safe way, because, as noted, objects are shared behind
100 @attention if one wants to use the pimpl idiom together with
101 cow_wrapper (i.e. put an opaque type into the cow_wrapper),
102 then <em>all<em> methods in the surrounding class needs to be
103 non-inline (<em>including</em> destructor, copy constructor
104 and assignment operator).
108 class cow_wrapper_client_impl;
110 class cow_wrapper_client
113 cow_wrapper_client();
114 cow_wrapper_client( const cow_wrapper_client& );
115 cow_wrapper_client( cow_wrapper_client&& );
116 ~cow_wrapper_client();
118 cow_wrapper_client& operator=( const cow_wrapper_client& );
119 cow_wrapper_client& operator=( cow_wrapper_client&& );
121 void modify( int nVal );
122 int queryUnmodified() const;
125 o3tl::cow_wrapper< cow_wrapper_client_impl > maImpl;
128 and the implementation file would look like this:
130 class cow_wrapper_client_impl
133 void setValue( int nVal ) { mnValue = nVal; }
134 int getValue() const { return mnValue; }
140 cow_wrapper_client::cow_wrapper_client() :
144 cow_wrapper_client::cow_wrapper_client( const cow_wrapper_client& rSrc ) :
145 maImpl( rSrc.maImpl )
148 cow_wrapper_client::cow_wrapper_client( cow_wrapper_client& rSrc ) :
149 maImpl( std::move( rSrc.maImpl ) )
152 cow_wrapper_client::~cow_wrapper_client()
155 cow_wrapper_client& cow_wrapper_client::operator=( const cow_wrapper_client& rSrc )
157 maImpl = rSrc.maImpl;
160 cow_wrapper_client& cow_wrapper_client::operator=( cow_wrapper_client&& rSrc )
162 maImpl = std::move( rSrc.maImpl );
165 void cow_wrapper_client::modify( int nVal )
167 maImpl->setValue( nVal );
169 int cow_wrapper_client::queryUnmodified() const
171 return maImpl->getValue();
175 template<typename T
, class MTPolicy
=UnsafeRefCountingPolicy
> class cow_wrapper
177 /** shared value object - gets cloned before cow_wrapper hands
178 out a non-const reference to it
182 impl_t(const impl_t
&) = delete;
183 impl_t
& operator=(const impl_t
&) = delete;
191 explicit impl_t( const T
& v
) :
198 typename
MTPolicy::ref_count_t m_ref_count
;
203 if( m_pimpl
&& !MTPolicy::decrementCount(m_pimpl
->m_ref_count
) )
211 typedef T value_type
;
213 typedef const T
* const_pointer
;
214 typedef MTPolicy mt_policy
;
216 /** Default-construct wrapped type instance
219 m_pimpl( new impl_t() )
223 /** Copy-construct wrapped type instance from given object
225 explicit cow_wrapper( const value_type
& r
) :
226 m_pimpl( new impl_t(r
) )
230 /** Shallow-copy given cow_wrapper
232 explicit cow_wrapper( const cow_wrapper
& rSrc
) : // nothrow
233 m_pimpl( rSrc
.m_pimpl
)
235 MTPolicy::incrementCount( m_pimpl
->m_ref_count
);
238 /** Move-construct and steal rSrc shared resource
240 explicit cow_wrapper( cow_wrapper
&& rSrc
) :
241 m_pimpl( rSrc
.m_pimpl
)
243 rSrc
.m_pimpl
= nullptr;
246 ~cow_wrapper() // nothrow, if ~T does not throw
251 /// now sharing rSrc cow_wrapper instance with us
252 cow_wrapper
& operator=( const cow_wrapper
& rSrc
) // nothrow
254 // this already guards against self-assignment
255 MTPolicy::incrementCount( rSrc
.m_pimpl
->m_ref_count
);
258 m_pimpl
= rSrc
.m_pimpl
;
263 /// stealing rSrc's resource
264 cow_wrapper
& operator=( cow_wrapper
&& rSrc
)
266 // self-movement guts ourself, see also 17.6.4.9
268 m_pimpl
= rSrc
.m_pimpl
;
270 rSrc
.m_pimpl
= nullptr;
275 /// unshare with any other cow_wrapper instance
276 value_type
& make_unique()
278 if( m_pimpl
->m_ref_count
> 1 )
280 impl_t
* pimpl
= new impl_t(m_pimpl
->m_value
);
285 return m_pimpl
->m_value
;
288 /// true, if not shared with any other cow_wrapper instance
289 bool is_unique() const // nothrow
291 return !m_pimpl
|| m_pimpl
->m_ref_count
== 1;
294 /// return number of shared instances (1 for unique object)
295 typename
MTPolicy::ref_count_t
use_count() const // nothrow
297 return m_pimpl
? m_pimpl
->m_ref_count
: 0;
300 void swap(cow_wrapper
& r
) // never throws
302 std::swap(m_pimpl
, r
.m_pimpl
);
305 pointer
operator->() { return &make_unique(); }
306 value_type
& operator*() { return make_unique(); }
307 const_pointer
operator->() const { return &m_pimpl
->m_value
; }
308 const value_type
& operator*() const { return m_pimpl
->m_value
; }
310 pointer
get() { return &make_unique(); }
311 const_pointer
get() const { return &m_pimpl
->m_value
; }
313 /// true, if both cow_wrapper internally share the same object
314 bool same_object( const cow_wrapper
& rOther
) const
316 return rOther
.m_pimpl
== m_pimpl
;
324 template<class T
, class P
> inline bool operator==( const cow_wrapper
<T
,P
>& a
,
325 const cow_wrapper
<T
,P
>& b
)
327 return a
.same_object(b
) || *a
== *b
;
330 template<class T
, class P
> inline bool operator!=( const cow_wrapper
<T
,P
>& a
,
331 const cow_wrapper
<T
,P
>& b
)
333 return !a
.same_object(b
) && *a
!= *b
;
336 template<class A
, class B
, class P
> inline bool operator<( const cow_wrapper
<A
,P
>& a
,
337 const cow_wrapper
<B
,P
>& b
)
342 template<class T
, class P
> inline void swap( cow_wrapper
<T
,P
>& a
,
343 cow_wrapper
<T
,P
>& b
)
350 #endif /* INCLUDED_O3TL_COW_WRAPPER_HXX */
352 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */