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 .
19 #ifndef INCLUDED_SLIDESHOW_SOURCE_INC_LISTENERCONTAINER_HXX
20 #define INCLUDED_SLIDESHOW_SOURCE_INC_LISTENERCONTAINER_HXX
27 namespace slideshow::internal
{
31 struct EmptyGuard
{ explicit EmptyGuard(EmptyBase
) {} };
32 struct EmptyClearableGuard
34 explicit EmptyClearableGuard(EmptyBase
) {}
35 static void clear() {}
38 typedef EmptyGuard Guard
;
39 typedef EmptyClearableGuard ClearableGuard
;
42 template< typename result_type
, typename ListenerTargetT
> struct FunctionApply
44 template<typename FuncT
> static bool apply(
46 ListenerTargetT
const& rArg
)
52 template<typename ListenerTargetT
> struct FunctionApply
<void,ListenerTargetT
>
54 template<typename FuncT
> static bool apply(
56 ListenerTargetT
const& rArg
)
63 template< typename ListenerT
> struct ListenerOperations
65 /// Notify a single one of the listeners
66 template< typename ContainerT
,
68 static bool notifySingleListener( ContainerT
& rContainer
,
71 // true: a handler in this queue processed the event
72 // false: no handler in this queue finally processed the event
73 return std::any_of( rContainer
.begin(),
78 /// Notify all listeners
79 template< typename ContainerT
,
81 static bool notifyAllListeners( ContainerT
& rContainer
,
85 for( const auto& rCurr
: rContainer
)
87 if( FunctionApply
< typename ::std::invoke_result
< FuncT
, const typename
ContainerT::value_type
& >::type
,
88 typename
ContainerT::value_type
>::apply(
96 // true: at least one handler returned true
97 // false: not a single handler returned true
101 /// Prune container from deceased listeners
102 template< typename ContainerT
>
103 static void pruneListeners( ContainerT
&, size_t )
108 template< typename ListenerTargetT
>
109 struct ListenerOperations
< std::weak_ptr
<ListenerTargetT
> >
111 template< typename ContainerT
,
113 static bool notifySingleListener( ContainerT
& rContainer
,
116 for( const auto& rCurr
: rContainer
)
118 std::shared_ptr
<ListenerTargetT
> pListener( rCurr
.lock() );
120 if( pListener
&& func(pListener
) )
127 template< typename ContainerT
,
129 static bool notifyAllListeners( ContainerT
& rContainer
,
133 for( const auto& rCurr
: rContainer
)
135 std::shared_ptr
<ListenerTargetT
> pListener( rCurr
.lock() );
137 if( pListener
.get() &&
138 FunctionApply
<typename ::std::invoke_result
<FuncT
, std::shared_ptr
<ListenerTargetT
> const&>::type
,
139 std::shared_ptr
<ListenerTargetT
> >::apply(func
,pListener
) )
147 template< typename ContainerT
>
148 static void pruneListeners( ContainerT
& rContainer
,
149 size_t nSizeThreshold
)
151 if( rContainer
.size() <= nSizeThreshold
)
154 ContainerT aAliveListeners
;
155 aAliveListeners
.reserve(rContainer
.size());
157 for( const auto& rCurr
: rContainer
)
159 if( !rCurr
.expired() )
160 aAliveListeners
.push_back( rCurr
);
163 std::swap( rContainer
, aAliveListeners
);
167 /** Container for objects that can be notified.
169 This templatized container holds listener objects, then can get
170 notified (by calling certain methods on them).
173 Type for the listener objects to be held
176 Full type of the container to store the listener objects. Defaults
177 to std::vector<ListenerT>
179 @tpl MaxDeceasedListenerUllage
180 Threshold, from which upwards the listener container gets
181 pruned. Avoids frequent copying of nearly empty containers.
183 @attention internal class, not to be used. functionality is
184 incomplete, please use the Thread(Un)safeListenerContainer types
187 template< typename ListenerT
,
188 typename MutexHolderBaseT
,
189 typename ContainerT
=std::vector
<ListenerT
>,
190 size_t MaxDeceasedListenerUllage
=16 > class ListenerContainerBase
: public MutexHolderBaseT
192 typedef typename
MutexHolderBaseT::Guard Guard
;
193 typedef typename
MutexHolderBaseT::ClearableGuard ClearableGuard
;
196 typedef ListenerT listener_type
;
197 typedef ContainerT container_type
;
199 /** Check whether listener container is empty
201 @return true, if currently no listeners registered. Note that
202 in a multi-threaded scenario, without external synchronisation
203 to this object, the return value might become wrong at any time.
208 return maListeners
.empty();
211 /** Check whether given listener is already added
213 @return true, if given listener is already added.
215 bool isAdded( listener_type
const& rListener
) const
219 const typename
container_type::const_iterator
aEnd( maListeners
.end() );
220 if( std::find( maListeners
.begin(),
222 rListener
) != aEnd
)
224 return true; // already added
235 void add( listener_type
const& rListener
)
240 if( isAdded(rListener
) )
241 return; // already added
243 maListeners
.push_back( rListener
);
245 ListenerOperations
<ListenerT
>::pruneListeners(
247 MaxDeceasedListenerUllage
);
250 /** Add new listener into sorted container
252 The stored listeners are kept sorted (using this method
253 requires listener_type to have operator< defined on it). Make
254 sure to call addSorted() for <em>each</em> listener to add to
255 this container - sorting is performed under the assumption
256 that existing entries are already sorted.
261 @return false, if the listener is already added, true
264 bool addSorted( listener_type
const& rListener
)
269 if( isAdded(rListener
) )
270 return false; // already added
272 maListeners
.push_back( rListener
);
274 // a single entry does not need to be sorted
275 if( maListeners
.size() > 1 )
279 std::prev(maListeners
.end()),
283 ListenerOperations
<ListenerT
>::pruneListeners(
285 MaxDeceasedListenerUllage
);
290 /** Remove listener from container
293 The listener to remove
295 @return false, if listener not found in container, true
298 bool remove( listener_type
const& rListener
)
302 // TODO : needed for the moment since ANDROID doesn't know size_t return from std::erase
304 const typename
container_type::iterator
aEnd( maListeners
.end() );
305 typename
container_type::iterator aIter
;
306 if( (aIter
=std::remove(maListeners
.begin(),
308 rListener
)) == aEnd
)
310 return false; // listener not found
313 maListeners
.erase( aIter
, aEnd
);
317 size_t nb
= std::erase(maListeners
, rListener
);
318 // if nb == 0, it means listener wasn't found
323 /// Removes all listeners in one go
331 /** Apply functor to one listener
333 This method applies functor to one of the listeners. Starting
334 with the first entry of the container, the functor is called
335 with the listener entries, until it returns true.
338 Given functor is called with listeners, until it returns true
340 @return true, if functor was successfully applied to a
343 template< typename FuncT
> bool apply( FuncT func
) const
345 ClearableGuard
aGuard(*this);
347 // generate a local copy of all handlers, to make method
348 // reentrant and thread-safe.
349 container_type
const local( maListeners
);
353 ListenerOperations
<ListenerT
>::notifySingleListener(
358 Guard
aGuard2(*this);
359 ListenerOperations
<ListenerT
>::pruneListeners(
360 const_cast<container_type
&>(maListeners
),
361 MaxDeceasedListenerUllage
);
367 /** Apply functor to all listeners
369 This method applies functor to all of the listeners. Starting
370 with the first entry of the container, the functor is called
371 with the listener entries.
374 Given functor is called with listeners.
376 @return true, if functor was successfully applied to at least
379 template< typename FuncT
> bool applyAll( FuncT func
) const
381 ClearableGuard
aGuard(*this);
383 // generate a local copy of all handlers, to make method
384 // reentrant and thread-safe.
385 container_type
const local( maListeners
);
389 ListenerOperations
<ListenerT
>::notifyAllListeners(
394 Guard
aGuard2(*this);
395 ListenerOperations
<ListenerT
>::pruneListeners(
396 const_cast<container_type
&>(maListeners
),
397 MaxDeceasedListenerUllage
);
404 ContainerT maListeners
;
408 /** ListenerContainer variant that does not serialize access
410 This ListenerContainer version is not safe to use in a
411 multi-threaded scenario, but has less overhead.
413 template< typename ListenerT
,
414 typename ContainerT
=std::vector
<ListenerT
> >
415 class ThreadUnsafeListenerContainer
: public ListenerContainerBase
<ListenerT
,
421 } // namespace slideshow::internal
423 #endif // INCLUDED_SLIDESHOW_SOURCE_INC_LISTENERCONTAINER_HXX
425 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */