1 /*
2 * This program source code file is part of KICAD, a free EDA CAD application.
3 *
4 * Copyright (C) 2016-20 Kicad Developers, see change_log.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
22 */
23 
24 #ifndef COMMON_OBSERVABLE_H__
25 #define COMMON_OBSERVABLE_H__
26 
27 #include <cassert>
28 #include <memory>
29 #include <vector>
30 #include <utility>
31 
32 /**
33  * A model subscriber implementation using links to represent connections.  Subscribers
34  * can be removed during notification.  If no observers are registered, size is the size
35  * of a shared_ptr.
36  */
37 namespace UTIL
38 {
39 class LINK;
40 
41 namespace DETAIL
42 {
43     struct OBSERVABLE_BASE
44     {
45     public:
46         OBSERVABLE_BASE();
47         OBSERVABLE_BASE( OBSERVABLE_BASE& other );
48 
49         ~OBSERVABLE_BASE();
50 
51         size_t size() const;
52 
53     private:
54         friend class UTIL::LINK;
55 
56         struct IMPL
57         {
58             IMPL( OBSERVABLE_BASE* owned_by = nullptr );
59             bool is_shared() const;
60             void set_shared();
61             ~IMPL();
62 
63             void add_observer( void* observer );
64             void remove_observer( void* observer );
65             void collect();
66 
67             bool is_iterating() const;
68 
69             void enter_iteration();
70             void leave_iteration();
71 
72             std::vector<void*> observers_;
73             unsigned int       iteration_count_;
74             OBSERVABLE_BASE*   owned_by_;
75         };
76 
77         void allocate_impl();
78         void allocate_shared_impl();
79 
80         void deallocate_impl();
81 
82         std::shared_ptr<IMPL> get_shared_impl();
83 
84     protected:
85         void on_observers_empty();
86 
87         void enter_iteration();
88         void leave_iteration();
89 
90         void add_observer( void* observer );
91         void remove_observer( void* observer );
92 
93         std::shared_ptr<IMPL> impl_;
94     };
95 
96 } // namespace DETAIL
97 
98 
99 /**
100  * Simple RAII-handle to a subscription.
101  */
102 class LINK
103 {
104 public:
105     LINK();
106     LINK( std::shared_ptr<DETAIL::OBSERVABLE_BASE::IMPL> token, void* observer );
107     LINK( LINK&& other );
108     LINK( const LINK& ) = delete;
109 
110     void  operator=( const LINK& ) = delete;
111     LINK& operator=( LINK&& other );
112 
113     void reset();
114 
115     explicit operator bool() const;
116 
117     ~LINK();
118 
119 private:
120     std::shared_ptr<DETAIL::OBSERVABLE_BASE::IMPL> token_;
121     void*                                          observer_;
122 };
123 
124 
125 template <typename ObserverInterface>
126 class OBSERVABLE : public DETAIL::OBSERVABLE_BASE
127 {
128 public:
129     /**
130      * Construct an observable with empty non-shared subscription list.
131      */
OBSERVABLE()132     OBSERVABLE() {}
133 
134     /**
135      * Construct an observable with a shared subscription list.
136      *
137      * @param aInherit Observable to share the subscription list with.
138      */
OBSERVABLE(OBSERVABLE & aInherit)139     OBSERVABLE( OBSERVABLE& aInherit ) : OBSERVABLE_BASE( aInherit ) {}
140 
141     /**
142      * Add a subscription without RAII link.
143      *
144      * @param aObserver Observer to subscribe.
145      */
SubscribeUnmanaged(ObserverInterface * aObserver)146     void SubscribeUnmanaged( ObserverInterface* aObserver )
147     {
148         OBSERVABLE_BASE::add_observer( static_cast<void*>( aObserver ) );
149     }
150 
151     /**
152      * Add a subscription returning an RAII link.
153      *
154      * @param aObserver observer to subscribe
155      * @return RAII link controlling the lifetime of the subscription
156      */
Subscribe(ObserverInterface * aObserver)157     LINK Subscribe( ObserverInterface* aObserver )
158     {
159         OBSERVABLE_BASE::add_observer( static_cast<void*>( aObserver ) );
160         return LINK( impl_, static_cast<void*>( aObserver ) );
161     }
162 
163     /**
164      * Cancel the subscription of a subscriber.
165      *
166      * This can be called during notification calls.
167      *
168      * @param aObserver observer to remove from the subscription list.
169      */
Unsubscribe(ObserverInterface * aObserver)170     void Unsubscribe( ObserverInterface* aObserver )
171     {
172         OBSERVABLE_BASE::remove_observer( static_cast<void*>( aObserver ) );
173     }
174 
175     /**
176      * Notify event to all subscribed observers.
177      *
178      * @param Ptr is a pointer to method of the observer interface.
179      * @param aArgs is a list of arguments to each notification call, will be perfectly forwarded.
180      */
181     template <typename... Args1, typename... Args2>
Notify(void (ObserverInterface::* Ptr)(Args1...),Args2 &&...aArgs)182     void Notify( void ( ObserverInterface::*Ptr )( Args1... ), Args2&&... aArgs )
183     {
184         static_assert( sizeof...( Args1 ) == sizeof...( Args2 ), "argument counts don't match" );
185 
186         if( impl_ )
187         {
188             enter_iteration();
189             try
190             {
191                 for( auto* void_ptr : impl_->observers_ )
192                 {
193                     if( void_ptr )
194                     {
195                         auto* typed_ptr = static_cast<ObserverInterface*>( void_ptr );
196                         ( typed_ptr->*Ptr )( std::forward<Args2>( aArgs )... );
197                     }
198                 }
199             }
200             catch( ... )
201             {
202                 leave_iteration();
203                 throw;
204             }
205 
206             leave_iteration();
207         }
208     }
209 
210     /**
211      * Notify event to all subscribed observers but one to be ignore.
212      *
213      * @param Ptr is a pointer to method of the observer interface.
214      * @param aIgnore is an observer to ignore during this notification.
215      * @param aArgs is a list of arguments to each notification call, will be perfectly forwarded.
216      */
217     template <typename... Args1, typename... Args2>
NotifyIgnore(void (ObserverInterface::* Ptr)(Args1...),ObserverInterface * aIgnore,Args2 &&...aArgs)218     void NotifyIgnore( void ( ObserverInterface::*Ptr )( Args1... ), ObserverInterface* aIgnore,
219                        Args2&&... aArgs )
220     {
221         static_assert( sizeof...( Args1 ) == sizeof...( Args2 ), "argument counts don't match" );
222 
223         if( impl_ )
224         {
225             enter_iteration();
226 
227             try
228             {
229                 for( auto* void_ptr : impl_->observers_ )
230                 {
231                     if( void_ptr && void_ptr != aIgnore )
232                     {
233                         auto* typed_ptr = static_cast<ObserverInterface*>( void_ptr );
234                         ( typed_ptr->*Ptr )( std::forward<Args2>( aArgs )... );
235                     }
236                 }
237             }
238             catch( ... )
239             {
240                 leave_iteration();
241                 throw;
242             }
243 
244             leave_iteration();
245         }
246     }
247 };
248 
249 } // namespace UTIL
250 
251 #endif
252