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>
30 /** Thread-unsafe refcounting
32 This is the default locking policy for cow_wrapper. No
33 locking/guarding against concurrent access is performed
36 struct UnsafeRefCountingPolicy
38 typedef std::size_t ref_count_t
;
39 static void incrementCount( ref_count_t
& rCount
) { ++rCount
; }
40 static bool decrementCount( ref_count_t
& rCount
) { return --rCount
!= 0; }
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 oslInterlockedCount ref_count_t
;
51 static void incrementCount( ref_count_t
& rCount
) { osl_atomic_increment(&rCount
); }
52 static bool decrementCount( ref_count_t
& rCount
)
54 if( rCount
== 1 ) // caller is already the only/last reference
57 return osl_atomic_decrement(&rCount
) != 0;
61 /** Copy-on-write wrapper.
63 This template provides copy-on-write semantics for the wrapped
64 type: when copying, the operation is performed shallow,
65 i.e. different cow_wrapper objects share the same underlying
66 instance. Only when accessing the underlying object via
67 non-const methods, a unique copy is provided.
69 The type parameter <code>T</code> must satisfy the following
70 requirements: it must be default-constructible, copyable (it
71 need not be assignable), and be of non-reference type. Note
72 that, despite the fact that this template provides access to
73 the wrapped type via pointer-like methods
74 (<code>operator->()</code> and <code>operator*()</code>), it does
75 <em>not</em> work like e.g. the std smart pointer wrappers
76 (shared_ptr, unique_ptr, etc.). Internally, the cow_wrapper
77 holds a by-value instance of the wrapped object. This is to
78 avoid one additional heap allocation, and providing access via
79 <code>operator->()</code>/<code>operator*()</code> is because
80 <code>operator.()</code> cannot be overridden.
82 Regarding thread safety: this wrapper is <em>not</em>
83 thread-safe per se, because cow_wrapper has no way of
84 synchronizing the potentially many different cow_wrapper
85 instances, that reference a single shared value_type
86 instance. That said, when passing
87 <code>ThreadSafeRefCountingPolicy</code> as the
88 <code>MTPolicy</code> parameter, accessing a thread-safe
89 pointee through multiple cow_wrapper instances might be
90 thread-safe, if the individual pointee methods are
91 thread-safe, <em>including</em> pointee's copy
92 constructor. Any wrapped object that needs external
93 synchronisation (e.g. via an external mutex, which arbitrates
94 access to object methods, and can be held across multiple
95 object method calls) cannot easily be dealt with in a
96 thread-safe way, because, as noted, objects are shared behind
99 @attention if one wants to use the pimpl idiom together with
100 cow_wrapper (i.e. put an opaque type into the cow_wrapper),
101 then <em>all<em> methods in the surrounding class needs to be
102 non-inline (<em>including</em> destructor, copy constructor
103 and assignment operator).
107 class cow_wrapper_client_impl;
109 class cow_wrapper_client
112 cow_wrapper_client();
113 cow_wrapper_client( const cow_wrapper_client& );
114 cow_wrapper_client( cow_wrapper_client&& );
115 ~cow_wrapper_client();
117 cow_wrapper_client& operator=( const cow_wrapper_client& );
118 cow_wrapper_client& operator=( cow_wrapper_client&& );
120 void modify( int nVal );
121 int queryUnmodified() const;
124 o3tl::cow_wrapper< cow_wrapper_client_impl > maImpl;
127 and the implementation file would look like this:
129 class cow_wrapper_client_impl
132 void setValue( int nVal ) { mnValue = nVal; }
133 int getValue() const { return mnValue; }
139 cow_wrapper_client::cow_wrapper_client() :
143 cow_wrapper_client::cow_wrapper_client( const cow_wrapper_client& rSrc ) :
144 maImpl( rSrc.maImpl )
147 cow_wrapper_client::cow_wrapper_client( cow_wrapper_client& rSrc ) :
148 maImpl( std::move( rSrc.maImpl ) )
151 cow_wrapper_client::~cow_wrapper_client()
154 cow_wrapper_client& cow_wrapper_client::operator=( const cow_wrapper_client& rSrc )
156 maImpl = rSrc.maImpl;
159 cow_wrapper_client& cow_wrapper_client::operator=( cow_wrapper_client&& rSrc )
161 maImpl = std::move( rSrc.maImpl );
164 void cow_wrapper_client::modify( int nVal )
166 maImpl->setValue( nVal );
168 int cow_wrapper_client::queryUnmodified() const
170 return maImpl->getValue();
174 template<typename T
, class MTPolicy
=UnsafeRefCountingPolicy
> class cow_wrapper
176 /** shared value object - gets cloned before cow_wrapper hands
177 out a non-const reference to it
181 impl_t(const impl_t
&) = delete;
182 impl_t
& operator=(const impl_t
&) = delete;
190 explicit impl_t( const T
& v
) :
197 typename
MTPolicy::ref_count_t m_ref_count
;
202 if( m_pimpl
&& !MTPolicy::decrementCount(m_pimpl
->m_ref_count
) )
210 typedef T value_type
;
212 typedef const T
* const_pointer
;
213 typedef MTPolicy mt_policy
;
215 /** Default-construct wrapped type instance
218 m_pimpl( new impl_t() )
222 /** Copy-construct wrapped type instance from given object
224 explicit cow_wrapper( const value_type
& r
) :
225 m_pimpl( new impl_t(r
) )
229 /** Shallow-copy given cow_wrapper
231 explicit cow_wrapper( const cow_wrapper
& rSrc
) : // nothrow
232 m_pimpl( rSrc
.m_pimpl
)
234 MTPolicy::incrementCount( m_pimpl
->m_ref_count
);
237 /** Move-construct and steal rSrc shared resource
239 explicit cow_wrapper( cow_wrapper
&& rSrc
) :
240 m_pimpl( rSrc
.m_pimpl
)
242 rSrc
.m_pimpl
= nullptr;
245 ~cow_wrapper() // nothrow, if ~T does not throw
250 /// now sharing rSrc cow_wrapper instance with us
251 cow_wrapper
& operator=( const cow_wrapper
& rSrc
) // nothrow
253 // this already guards against self-assignment
254 MTPolicy::incrementCount( rSrc
.m_pimpl
->m_ref_count
);
257 m_pimpl
= rSrc
.m_pimpl
;
262 /// stealing rSrc's resource
263 cow_wrapper
& operator=( cow_wrapper
&& rSrc
)
265 // self-movement guts ourself, see also 17.6.4.9
267 m_pimpl
= rSrc
.m_pimpl
;
269 rSrc
.m_pimpl
= nullptr;
274 /// unshare with any other cow_wrapper instance
275 value_type
& make_unique()
277 if( m_pimpl
->m_ref_count
> 1 )
279 impl_t
* pimpl
= new impl_t(m_pimpl
->m_value
);
284 return m_pimpl
->m_value
;
287 /// true, if not shared with any other cow_wrapper instance
288 bool is_unique() const // nothrow
290 return !m_pimpl
|| m_pimpl
->m_ref_count
== 1;
293 /// return number of shared instances (1 for unique object)
294 typename
MTPolicy::ref_count_t
use_count() const // nothrow
296 return m_pimpl
? m_pimpl
->m_ref_count
: 0;
299 void swap(cow_wrapper
& r
) // never throws
301 std::swap(m_pimpl
, r
.m_pimpl
);
304 pointer
operator->() { return &make_unique(); }
305 value_type
& operator*() { return make_unique(); }
306 const_pointer
operator->() const { return &m_pimpl
->m_value
; }
307 const value_type
& operator*() const { return m_pimpl
->m_value
; }
309 pointer
get() { return &make_unique(); }
310 const_pointer
get() const { return &m_pimpl
->m_value
; }
312 /// true, if both cow_wrapper internally share the same object
313 bool same_object( const cow_wrapper
& rOther
) const
315 return rOther
.m_pimpl
== m_pimpl
;
323 template<class T
, class P
> inline bool operator==( const cow_wrapper
<T
,P
>& a
,
324 const cow_wrapper
<T
,P
>& b
)
326 return a
.same_object(b
) || *a
== *b
;
329 template<class T
, class P
> inline bool operator!=( const cow_wrapper
<T
,P
>& a
,
330 const cow_wrapper
<T
,P
>& b
)
332 return !a
.same_object(b
) && *a
!= *b
;
335 template<class A
, class B
, class P
> inline bool operator<( const cow_wrapper
<A
,P
>& a
,
336 const cow_wrapper
<B
,P
>& b
)
341 template<class T
, class P
> inline void swap( cow_wrapper
<T
,P
>& a
,
342 cow_wrapper
<T
,P
>& b
)
349 #endif /* INCLUDED_O3TL_COW_WRAPPER_HXX */
351 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */