update credits
[LibreOffice.git] / include / o3tl / cow_wrapper.hxx
blobb54f99d0f1906829f989bd93dbe2440b0ce7384b
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 <algorithm>
27 #include <boost/utility.hpp>
28 #include <boost/checked_delete.hpp>
30 namespace o3tl
32 /** Thread-unsafe refcounting
34 This is the default locking policy for cow_wrapper. No
35 locking/guarding against concurrent access is performed
36 whatsoever.
38 struct UnsafeRefCountingPolicy
40 typedef sal_uInt32 ref_count_t;
41 static void incrementCount( ref_count_t& rCount ) { ++rCount; }
42 static bool decrementCount( ref_count_t& rCount ) { return --rCount != 0; }
45 /** Thread-safe refcounting
47 Use this to have the cow_wrapper refcounting mechanisms employ
48 the thread-safe oslInterlockedCount .
50 struct ThreadSafeRefCountingPolicy
52 typedef oslInterlockedCount ref_count_t;
53 static void incrementCount( ref_count_t& rCount ) { osl_atomic_increment(&rCount); }
54 static bool decrementCount( ref_count_t& rCount )
56 if( rCount == 1 ) // caller is already the only/last reference
57 return false;
58 else
59 return osl_atomic_decrement(&rCount) != 0;
63 /** Copy-on-write wrapper.
65 This template provides copy-on-write semantics for the wrapped
66 type: when copying, the operation is performed shallow,
67 i.e. different cow_wrapper objects share the same underlying
68 instance. Only when accessing the underlying object via
69 non-const methods, a unique copy is provided.
71 The type parameter <code>T</code> must satisfy the following
72 requirements: it must be default-constructible, copyable (it
73 need not be assignable), and be of non-reference type. Note
74 that, despite the fact that this template provides access to
75 the wrapped type via pointer-like methods
76 (<code>operator->()</code> and <code>operator*()</code>), it does
77 <em>not</em> work like e.g. the boost pointer wrappers
78 (shared_ptr, scoped_ptr, etc.). Internally, the cow_wrapper
79 holds a by-value instance of the wrapped object. This is to
80 avoid one additional heap allocation, and providing access via
81 <code>operator->()</code>/<code>operator*()</code> is because
82 <code>operator.()</code> cannot be overridden.
84 Regarding thread safety: this wrapper is <em>not</em>
85 thread-safe per se, because cow_wrapper has no way of
86 syncronizing the potentially many different cow_wrapper
87 instances, that reference a single shared value_type
88 instance. That said, when passing
89 <code>ThreadSafeRefCountingPolicy</code> as the
90 <code>MTPolicy</code> parameter, accessing a thread-safe
91 pointee through multiple cow_wrapper instances might be
92 thread-safe, if the individual pointee methods are
93 thread-safe, <em>including</em> pointee's copy
94 constructor. Any wrapped object that needs external
95 synchronisation (e.g. via an external mutex, which arbitrates
96 access to object methods, and can be held across multiple
97 object method calls) cannot easily be dealt with in a
98 thread-safe way, because, as noted, objects are shared behind
99 the client's back.
101 @attention if one wants to use the pimpl idiom together with
102 cow_wrapper (i.e. put an opaque type into the cow_wrapper),
103 then <em>all<em> methods in the surrounding class needs to be
104 non-inline (<em>including</em> destructor, copy constructor
105 and assignment operator).
107 @example
108 <pre>
109 class cow_wrapper_client_impl;
111 class cow_wrapper_client
113 public:
114 cow_wrapper_client();
115 cow_wrapper_client( const cow_wrapper_client& );
116 ~cow_wrapper_client();
118 cow_wrapper_client& operator=( const cow_wrapper_client& );
120 void modify( int nVal );
121 int queryUnmodified() const;
123 private:
124 otl::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()
150 cow_wrapper_client& cow_wrapper_client::operator=( const cow_wrapper_client& rSrc )
152 maImpl = rSrc.maImpl;
153 return *this;
155 void cow_wrapper_client::modify( int nVal )
157 maImpl->setValue( nVal );
159 int cow_wrapper_client::queryUnmodified() const
161 return maImpl->getValue();
163 </pre>
165 template<typename T, class MTPolicy=UnsafeRefCountingPolicy> class cow_wrapper
167 /** shared value object - gets cloned before cow_wrapper hands
168 out a non-const reference to it
170 struct impl_t : private boost::noncopyable
172 impl_t() :
173 m_value(),
174 m_ref_count(1)
178 explicit impl_t( const T& v ) :
179 m_value(v),
180 m_ref_count(1)
184 T m_value;
185 typename MTPolicy::ref_count_t m_ref_count;
188 void release()
190 if( !MTPolicy::decrementCount(m_pimpl->m_ref_count) )
191 boost::checked_delete(m_pimpl), m_pimpl=0;
194 public:
195 typedef T value_type;
196 typedef T* pointer;
197 typedef const T* const_pointer;
198 typedef MTPolicy mt_policy;
200 /** Default-construct wrapped type instance
202 cow_wrapper() :
203 m_pimpl( new impl_t() )
207 /** Copy-construct wrapped type instance from given object
209 explicit cow_wrapper( const value_type& r ) :
210 m_pimpl( new impl_t(r) )
214 /** Shallow-copy given cow_wrapper
216 explicit cow_wrapper( const cow_wrapper& rSrc ) : // nothrow
217 m_pimpl( rSrc.m_pimpl )
219 MTPolicy::incrementCount( m_pimpl->m_ref_count );
222 ~cow_wrapper() // nothrow, if ~T does not throw
224 release();
227 /// now sharing rSrc cow_wrapper instance with us
228 cow_wrapper& operator=( const cow_wrapper& rSrc ) // nothrow
230 // this already guards against self-assignment
231 MTPolicy::incrementCount( rSrc.m_pimpl->m_ref_count );
233 release();
234 m_pimpl = rSrc.m_pimpl;
236 return *this;
239 /// unshare with any other cow_wrapper instance
240 value_type& make_unique()
242 if( m_pimpl->m_ref_count > 1 )
244 impl_t* pimpl = new impl_t(m_pimpl->m_value);
245 release();
246 m_pimpl = pimpl;
249 return m_pimpl->m_value;
252 /// true, if not shared with any other cow_wrapper instance
253 bool is_unique() const // nothrow
255 return m_pimpl->m_ref_count == 1;
258 /// return number of shared instances (1 for unique object)
259 typename MTPolicy::ref_count_t use_count() const // nothrow
261 return m_pimpl->m_ref_count;
264 void swap(cow_wrapper& r) // never throws
266 std::swap(m_pimpl, r.m_pimpl);
269 pointer operator->() { return &make_unique(); }
270 value_type& operator*() { return make_unique(); }
271 const_pointer operator->() const { return &m_pimpl->m_value; }
272 const value_type& operator*() const { return m_pimpl->m_value; }
274 pointer get() { return &make_unique(); }
275 const_pointer get() const { return &m_pimpl->m_value; }
277 /// true, if both cow_wrapper internally share the same object
278 bool same_object( const cow_wrapper& rOther ) const
280 return rOther.m_pimpl == m_pimpl;
283 private:
284 impl_t* m_pimpl;
288 template<class T, class P> inline bool operator==( const cow_wrapper<T,P>& a,
289 const cow_wrapper<T,P>& b )
291 return a.same_object(b) ? true : *a == *b;
294 template<class T, class P> inline bool operator!=( const cow_wrapper<T,P>& a,
295 const cow_wrapper<T,P>& b )
297 return a.same_object(b) ? false : *a != *b;
300 template<class A, class B, class P> inline bool operator<( const cow_wrapper<A,P>& a,
301 const cow_wrapper<B,P>& b )
303 return *a < *b;
306 template<class T, class P> inline void swap( cow_wrapper<T,P>& a,
307 cow_wrapper<T,P>& b )
309 a.swap(b);
312 // to enable boost::mem_fn on cow_wrapper
313 template<class T, class P> inline T * get_pointer( const cow_wrapper<T,P>& r )
315 return r.get();
320 #endif /* INCLUDED_O3TL_COW_WRAPPER_HXX */
322 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */