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 return osl_atomic_decrement(&rCount
) != 0;
58 /** Copy-on-write wrapper.
60 This template provides copy-on-write semantics for the wrapped
61 type: when copying, the operation is performed shallow,
62 i.e. different cow_wrapper objects share the same underlying
63 instance. Only when accessing the underlying object via
64 non-const methods, a unique copy is provided.
66 The type parameter <code>T</code> must satisfy the following
67 requirements: it must be default-constructible, copyable (it
68 need not be assignable), and be of non-reference type. Note
69 that, despite the fact that this template provides access to
70 the wrapped type via pointer-like methods
71 (<code>operator->()</code> and <code>operator*()</code>), it does
72 <em>not</em> work like e.g. the std smart pointer wrappers
73 (shared_ptr, unique_ptr, etc.). Internally, the cow_wrapper
74 holds a by-value instance of the wrapped object. This is to
75 avoid one additional heap allocation, and providing access via
76 <code>operator->()</code>/<code>operator*()</code> is because
77 <code>operator.()</code> cannot be overridden.
79 Regarding thread safety: this wrapper is <em>not</em>
80 thread-safe per se, because cow_wrapper has no way of
81 synchronizing the potentially many different cow_wrapper
82 instances, that reference a single shared value_type
83 instance. That said, when passing
84 <code>ThreadSafeRefCountingPolicy</code> as the
85 <code>MTPolicy</code> parameter, accessing a thread-safe
86 pointee through multiple cow_wrapper instances might be
87 thread-safe, if the individual pointee methods are
88 thread-safe, <em>including</em> pointee's copy
89 constructor. Any wrapped object that needs external
90 synchronisation (e.g. via an external mutex, which arbitrates
91 access to object methods, and can be held across multiple
92 object method calls) cannot easily be dealt with in a
93 thread-safe way, because, as noted, objects are shared behind
96 @attention if one wants to use the pimpl idiom together with
97 cow_wrapper (i.e. put an opaque type into the cow_wrapper),
98 then <em>all<em> methods in the surrounding class needs to be
99 non-inline (<em>including</em> destructor, copy constructor
100 and assignment operator).
104 class cow_wrapper_client_impl;
106 class cow_wrapper_client
109 cow_wrapper_client();
110 cow_wrapper_client( const cow_wrapper_client& );
111 cow_wrapper_client( cow_wrapper_client&& );
112 ~cow_wrapper_client();
114 cow_wrapper_client& operator=( const cow_wrapper_client& );
115 cow_wrapper_client& operator=( cow_wrapper_client&& );
117 void modify( int nVal );
118 int queryUnmodified() const;
121 o3tl::cow_wrapper< cow_wrapper_client_impl > maImpl;
124 and the implementation file would look like this:
126 class cow_wrapper_client_impl
129 void setValue( int nVal ) { mnValue = nVal; }
130 int getValue() const { return mnValue; }
136 cow_wrapper_client::cow_wrapper_client() :
140 cow_wrapper_client::cow_wrapper_client( const cow_wrapper_client& rSrc ) :
141 maImpl( rSrc.maImpl )
144 cow_wrapper_client::cow_wrapper_client( cow_wrapper_client& rSrc ) :
145 maImpl( std::move( rSrc.maImpl ) )
148 cow_wrapper_client::~cow_wrapper_client()
151 cow_wrapper_client& cow_wrapper_client::operator=( const cow_wrapper_client& rSrc )
153 maImpl = rSrc.maImpl;
156 cow_wrapper_client& cow_wrapper_client::operator=( cow_wrapper_client&& rSrc )
158 maImpl = std::move( rSrc.maImpl );
161 void cow_wrapper_client::modify( int nVal )
163 maImpl->setValue( nVal );
165 int cow_wrapper_client::queryUnmodified() const
167 return maImpl->getValue();
171 template<typename T
, class MTPolicy
=UnsafeRefCountingPolicy
> class cow_wrapper
173 /** shared value object - gets cloned before cow_wrapper hands
174 out a non-const reference to it
178 impl_t(const impl_t
&) = delete;
179 impl_t
& operator=(const impl_t
&) = delete;
187 explicit impl_t( const T
& v
) :
193 explicit impl_t( T
&& v
) :
194 m_value(std::move(v
)),
200 typename
MTPolicy::ref_count_t m_ref_count
;
205 if( m_pimpl
&& !MTPolicy::decrementCount(m_pimpl
->m_ref_count
) )
213 typedef T value_type
;
215 typedef const T
* const_pointer
;
216 typedef MTPolicy mt_policy
;
218 /** Default-construct wrapped type instance
221 m_pimpl( new impl_t() )
225 /** Copy-construct wrapped type instance from given object
227 explicit cow_wrapper( const value_type
& r
) :
228 m_pimpl( new impl_t(r
) )
232 /** Move-construct wrapped type instance from given object
234 explicit cow_wrapper( value_type
&& r
) :
235 m_pimpl( new impl_t(std::move(r
)) )
239 /** Shallow-copy given cow_wrapper
241 explicit cow_wrapper( const cow_wrapper
& rSrc
) : // nothrow
242 m_pimpl( rSrc
.m_pimpl
)
244 MTPolicy::incrementCount( m_pimpl
->m_ref_count
);
247 /** Move-construct and steal rSrc shared resource
249 explicit cow_wrapper( cow_wrapper
&& rSrc
) noexcept
:
250 m_pimpl( rSrc
.m_pimpl
)
252 rSrc
.m_pimpl
= nullptr;
255 // Only intended to be used by std::optional specialisations
256 explicit cow_wrapper( std::nullopt_t
) noexcept
:
261 // Only intended to be used by std::optional specialisations
262 explicit cow_wrapper( const cow_wrapper
& rSrc
, std::nullopt_t
) : // nothrow
263 m_pimpl( rSrc
.m_pimpl
)
266 MTPolicy::incrementCount( m_pimpl
->m_ref_count
);
269 ~cow_wrapper() // nothrow, if ~T does not throw
274 /// now sharing rSrc cow_wrapper instance with us
275 cow_wrapper
& operator=( const cow_wrapper
& rSrc
) // nothrow
277 // this already guards against self-assignment
278 MTPolicy::incrementCount( rSrc
.m_pimpl
->m_ref_count
);
281 m_pimpl
= rSrc
.m_pimpl
;
286 /// stealing rSrc's resource
287 cow_wrapper
& operator=(cow_wrapper
&& rSrc
) noexcept
289 // self-movement guts ourself, see also 17.6.4.9
291 m_pimpl
= rSrc
.m_pimpl
;
293 rSrc
.m_pimpl
= nullptr;
298 /// unshare with any other cow_wrapper instance
299 value_type
& make_unique()
301 if( m_pimpl
->m_ref_count
> 1 )
303 impl_t
* pimpl
= new impl_t(m_pimpl
->m_value
);
308 return m_pimpl
->m_value
;
311 /// true, if not shared with any other cow_wrapper instance
312 bool is_unique() const // nothrow
314 return !m_pimpl
|| m_pimpl
->m_ref_count
== 1;
317 /// return number of shared instances (1 for unique object)
318 typename
MTPolicy::ref_count_t
use_count() const // nothrow
320 return m_pimpl
? m_pimpl
->m_ref_count
: 0;
323 void swap(cow_wrapper
& r
) // never throws
325 std::swap(m_pimpl
, r
.m_pimpl
);
328 pointer
operator->() { return &make_unique(); }
329 value_type
& operator*() { return make_unique(); }
330 const_pointer
operator->() const { return &m_pimpl
->m_value
; }
331 const value_type
& operator*() const { return m_pimpl
->m_value
; }
333 pointer
get() { return &make_unique(); }
334 const_pointer
get() const { return &m_pimpl
->m_value
; }
336 /// true, if both cow_wrapper internally share the same object
337 bool same_object( const cow_wrapper
& rOther
) const
339 return rOther
.m_pimpl
== m_pimpl
;
342 // Only intended to be used by std::optional specialisations
343 bool empty() const { return m_pimpl
== nullptr; }
344 // Only intended to be used by std::optional specialisations
359 template<class T
, class P
> inline bool operator==( const cow_wrapper
<T
,P
>& a
,
360 const cow_wrapper
<T
,P
>& b
)
362 return a
.same_object(b
) || *a
== *b
;
365 template<class T
, class P
> inline bool operator!=( const cow_wrapper
<T
,P
>& a
,
366 const cow_wrapper
<T
,P
>& b
)
368 return !a
.same_object(b
) && *a
!= *b
;
371 template<class A
, class B
, class P
> inline bool operator<( const cow_wrapper
<A
,P
>& a
,
372 const cow_wrapper
<B
,P
>& b
)
377 template<class T
, class P
> inline void swap( cow_wrapper
<T
,P
>& a
,
378 cow_wrapper
<T
,P
>& b
)
385 #endif /* INCLUDED_O3TL_COW_WRAPPER_HXX */
387 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */