build fix
[LibreOffice.git] / include / o3tl / cow_wrapper.hxx
bloba80241e3a8a187f4a9f4ca0582187f2272e75bf5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
25 #include <utility>
26 #include <cstddef>
28 namespace o3tl
30 /** Thread-unsafe refcounting
32 This is the default locking policy for cow_wrapper. No
33 locking/guarding against concurrent access is performed
34 whatsoever.
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
55 return false;
56 else
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
97 the client's back.
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).
105 @example
106 <pre>
107 class cow_wrapper_client_impl;
109 class cow_wrapper_client
111 public:
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;
123 private:
124 o3tl::cow_wrapper< cow_wrapper_client_impl > maImpl;
126 </pre>
127 and the implementation file would look like this:
128 <pre>
129 class cow_wrapper_client_impl
131 public:
132 void setValue( int nVal ) { mnValue = nVal; }
133 int getValue() const { return mnValue; }
135 private:
136 int mnValue;
139 cow_wrapper_client::cow_wrapper_client() :
140 maImpl()
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;
157 return *this;
159 cow_wrapper_client& cow_wrapper_client::operator=( cow_wrapper_client&& rSrc )
161 maImpl = std::move( rSrc.maImpl );
162 return *this;
164 void cow_wrapper_client::modify( int nVal )
166 maImpl->setValue( nVal );
168 int cow_wrapper_client::queryUnmodified() const
170 return maImpl->getValue();
172 </pre>
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
179 struct impl_t
181 impl_t(const impl_t&) = delete;
182 impl_t& operator=(const impl_t&) = delete;
184 impl_t() :
185 m_value(),
186 m_ref_count(1)
190 explicit impl_t( const T& v ) :
191 m_value(v),
192 m_ref_count(1)
196 T m_value;
197 typename MTPolicy::ref_count_t m_ref_count;
200 void release()
202 if( m_pimpl && !MTPolicy::decrementCount(m_pimpl->m_ref_count) )
204 delete m_pimpl;
205 m_pimpl = nullptr;
209 public:
210 typedef T value_type;
211 typedef T* pointer;
212 typedef const T* const_pointer;
213 typedef MTPolicy mt_policy;
215 /** Default-construct wrapped type instance
217 cow_wrapper() :
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
247 release();
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 );
256 release();
257 m_pimpl = rSrc.m_pimpl;
259 return *this;
262 /// stealing rSrc's resource
263 cow_wrapper& operator=( cow_wrapper&& rSrc )
265 // self-movement guts ourself, see also 17.6.4.9
266 release();
267 m_pimpl = rSrc.m_pimpl;
269 rSrc.m_pimpl = nullptr;
271 return *this;
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);
280 release();
281 m_pimpl = pimpl;
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;
318 private:
319 impl_t* 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 )
338 return *a < *b;
341 template<class T, class P> inline void swap( cow_wrapper<T,P>& a,
342 cow_wrapper<T,P>& b )
344 a.swap(b);
349 #endif /* INCLUDED_O3TL_COW_WRAPPER_HXX */
351 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */