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
23 #include <osl/interlck.h>
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 sal_uInt32 ref_count_t
;
38 static void incrementCount( ref_count_t
& rCount
) { ++rCount
; }
39 static bool decrementCount( ref_count_t
& rCount
) { return --rCount
!= 0; }
42 /** Thread-safe refcounting
44 Use this to have the cow_wrapper refcounting mechanisms employ
45 the thread-safe oslInterlockedCount .
47 struct ThreadSafeRefCountingPolicy
49 typedef oslInterlockedCount ref_count_t
;
50 static void incrementCount( ref_count_t
& rCount
) { osl_atomic_increment(&rCount
); }
51 static bool decrementCount( ref_count_t
& rCount
)
53 if( rCount
== 1 ) // caller is already the only/last reference
56 return osl_atomic_decrement(&rCount
) != 0;
60 /** Copy-on-write wrapper.
62 This template provides copy-on-write semantics for the wrapped
63 type: when copying, the operation is performed shallow,
64 i.e. different cow_wrapper objects share the same underlying
65 instance. Only when accessing the underlying object via
66 non-const methods, a unique copy is provided.
68 The type parameter <code>T</code> must satisfy the following
69 requirements: it must be default-constructible, copyable (it
70 need not be assignable), and be of non-reference type. Note
71 that, despite the fact that this template provides access to
72 the wrapped type via pointer-like methods
73 (<code>operator->()</code> and <code>operator*()</code>), it does
74 <em>not</em> work like e.g. the std smart pointer wrappers
75 (shared_ptr, unique_ptr, etc.). Internally, the cow_wrapper
76 holds a by-value instance of the wrapped object. This is to
77 avoid one additional heap allocation, and providing access via
78 <code>operator->()</code>/<code>operator*()</code> is because
79 <code>operator.()</code> cannot be overridden.
81 Regarding thread safety: this wrapper is <em>not</em>
82 thread-safe per se, because cow_wrapper has no way of
83 synchronizing the potentially many different cow_wrapper
84 instances, that reference a single shared value_type
85 instance. That said, when passing
86 <code>ThreadSafeRefCountingPolicy</code> as the
87 <code>MTPolicy</code> parameter, accessing a thread-safe
88 pointee through multiple cow_wrapper instances might be
89 thread-safe, if the individual pointee methods are
90 thread-safe, <em>including</em> pointee's copy
91 constructor. Any wrapped object that needs external
92 synchronisation (e.g. via an external mutex, which arbitrates
93 access to object methods, and can be held across multiple
94 object method calls) cannot easily be dealt with in a
95 thread-safe way, because, as noted, objects are shared behind
98 @attention if one wants to use the pimpl idiom together with
99 cow_wrapper (i.e. put an opaque type into the cow_wrapper),
100 then <em>all<em> methods in the surrounding class needs to be
101 non-inline (<em>including</em> destructor, copy constructor
102 and assignment operator).
106 class cow_wrapper_client_impl;
108 class cow_wrapper_client
111 cow_wrapper_client();
112 cow_wrapper_client( const cow_wrapper_client& );
113 cow_wrapper_client( cow_wrapper_client&& );
114 ~cow_wrapper_client();
116 cow_wrapper_client& operator=( const cow_wrapper_client& );
117 cow_wrapper_client& operator=( cow_wrapper_client&& );
119 void modify( int nVal );
120 int queryUnmodified() const;
123 o3tl::cow_wrapper< cow_wrapper_client_impl > maImpl;
126 and the implementation file would look like this:
128 class cow_wrapper_client_impl
131 void setValue( int nVal ) { mnValue = nVal; }
132 int getValue() const { return mnValue; }
138 cow_wrapper_client::cow_wrapper_client() :
142 cow_wrapper_client::cow_wrapper_client( const cow_wrapper_client& rSrc ) :
143 maImpl( rSrc.maImpl )
146 cow_wrapper_client::cow_wrapper_client( cow_wrapper_client& rSrc ) :
147 maImpl( std::move( rSrc.maImpl ) )
150 cow_wrapper_client::~cow_wrapper_client()
153 cow_wrapper_client& cow_wrapper_client::operator=( const cow_wrapper_client& rSrc )
155 maImpl = rSrc.maImpl;
158 cow_wrapper_client& cow_wrapper_client::operator=( cow_wrapper_client&& rSrc )
160 maImpl = std::move( rSrc.maImpl );
163 void cow_wrapper_client::modify( int nVal )
165 maImpl->setValue( nVal );
167 int cow_wrapper_client::queryUnmodified() const
169 return maImpl->getValue();
173 template<typename T
, class MTPolicy
=UnsafeRefCountingPolicy
> class cow_wrapper
175 /** shared value object - gets cloned before cow_wrapper hands
176 out a non-const reference to it
180 impl_t(const impl_t
&) = delete;
181 impl_t
& operator=(const impl_t
&) = delete;
189 explicit impl_t( const T
& v
) :
196 typename
MTPolicy::ref_count_t m_ref_count
;
201 if( m_pimpl
&& !MTPolicy::decrementCount(m_pimpl
->m_ref_count
) )
209 typedef T value_type
;
211 typedef const T
* const_pointer
;
212 typedef MTPolicy mt_policy
;
214 /** Default-construct wrapped type instance
217 m_pimpl( new impl_t() )
221 /** Copy-construct wrapped type instance from given object
223 explicit cow_wrapper( const value_type
& r
) :
224 m_pimpl( new impl_t(r
) )
228 /** Shallow-copy given cow_wrapper
230 explicit cow_wrapper( const cow_wrapper
& rSrc
) : // nothrow
231 m_pimpl( rSrc
.m_pimpl
)
233 MTPolicy::incrementCount( m_pimpl
->m_ref_count
);
236 /** Move-construct and steal rSrc shared resource
238 explicit cow_wrapper( cow_wrapper
&& rSrc
) :
239 m_pimpl( rSrc
.m_pimpl
)
241 rSrc
.m_pimpl
= nullptr;
244 ~cow_wrapper() // nothrow, if ~T does not throw
249 /// now sharing rSrc cow_wrapper instance with us
250 cow_wrapper
& operator=( const cow_wrapper
& rSrc
) // nothrow
252 // this already guards against self-assignment
253 MTPolicy::incrementCount( rSrc
.m_pimpl
->m_ref_count
);
256 m_pimpl
= rSrc
.m_pimpl
;
261 /// stealing rSrc's resource
262 cow_wrapper
& operator=( cow_wrapper
&& rSrc
)
264 // self-movement guts ourself, see also 17.6.4.9
266 m_pimpl
= rSrc
.m_pimpl
;
268 rSrc
.m_pimpl
= nullptr;
273 /// unshare with any other cow_wrapper instance
274 value_type
& make_unique()
276 if( m_pimpl
->m_ref_count
> 1 )
278 impl_t
* pimpl
= new impl_t(m_pimpl
->m_value
);
283 return m_pimpl
->m_value
;
286 /// true, if not shared with any other cow_wrapper instance
287 bool is_unique() const // nothrow
289 return !m_pimpl
|| m_pimpl
->m_ref_count
== 1;
292 /// return number of shared instances (1 for unique object)
293 typename
MTPolicy::ref_count_t
use_count() const // nothrow
295 return m_pimpl
? m_pimpl
->m_ref_count
: 0;
298 void swap(cow_wrapper
& r
) // never throws
300 std::swap(m_pimpl
, r
.m_pimpl
);
303 pointer
operator->() { return &make_unique(); }
304 value_type
& operator*() { return make_unique(); }
305 const_pointer
operator->() const { return &m_pimpl
->m_value
; }
306 const value_type
& operator*() const { return m_pimpl
->m_value
; }
308 pointer
get() { return &make_unique(); }
309 const_pointer
get() const { return &m_pimpl
->m_value
; }
311 /// true, if both cow_wrapper internally share the same object
312 bool same_object( const cow_wrapper
& rOther
) const
314 return rOther
.m_pimpl
== m_pimpl
;
322 template<class T
, class P
> inline bool operator==( const cow_wrapper
<T
,P
>& a
,
323 const cow_wrapper
<T
,P
>& b
)
325 return a
.same_object(b
) || *a
== *b
;
328 template<class T
, class P
> inline bool operator!=( const cow_wrapper
<T
,P
>& a
,
329 const cow_wrapper
<T
,P
>& b
)
331 return !a
.same_object(b
) && *a
!= *b
;
334 template<class A
, class B
, class P
> inline bool operator<( const cow_wrapper
<A
,P
>& a
,
335 const cow_wrapper
<B
,P
>& b
)
340 template<class T
, class P
> inline void swap( cow_wrapper
<T
,P
>& a
,
341 cow_wrapper
<T
,P
>& b
)
348 #endif /* INCLUDED_O3TL_COW_WRAPPER_HXX */
350 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */