Allow test whether a callback receiver is connected
[platform/upstream/libzypp.git] / zypp / Callback.h
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/Callback.h
10  *
11 */
12 #ifndef ZYPP_CALLBACK_H
13 #define ZYPP_CALLBACK_H
14
15 #include "zypp/base/NonCopyable.h"
16
17 ///////////////////////////////////////////////////////////////////
18 namespace zypp
19 { /////////////////////////////////////////////////////////////////
20
21   /** \todo Eliminate this! */
22   namespace HACK {
23     class Callback
24     {
25     };
26   } // namespace HACK
27
28   ///////////////////////////////////////////////////////////////////
29   /** Callbacks light.
30    *
31    * \par The task report structure (SENDER SIDE).
32    *
33    * A default constructible struct derived from callback::ReportBase.
34    * It \b must \b not conatin any data, just virtual methods.
35    *
36    * These are the functions the sender invokes, and which will be forwarded
37    * to some receiver. If no receiver is present, the defined default
38    * implementations are invoked.
39    *
40    * For methods returning non-void, define a reasonable return value,
41    * because this is what you get back in case no receiver is listening.
42    *
43    * That way the sending side does not need to know whether some receiver
44    * is listening. And it enables the receiver to return a reasonable value,
45    * in case he's got no idea, what else to return.
46    *
47    * \code
48    *   struct Foo : public callback::ReportBase
49    *   {
50    *     virtual void ping( int i )
51    *     {}
52    *     virtual int pong()
53    *     { return -1; }
54    *
55    *   };
56    * \endcode
57    *
58    * \par Sending a Task report (SENDER SIDE).
59    *
60    * Simply create a callback::SendReport<_Report>, where _Report
61    * is your task report structure. Invoke the callback functions
62    * as needed. That's it.
63    *
64    * \note Even creation and destruction of a callback::SendReport
65    * are indicated to a receiver. So even in case of an Exception,
66    * the receiver is able to recognize, that the task ended.
67    * So don't create it without need.
68    *
69    * \code
70    * {
71    *   callback::SendReport<Foo> report;
72    *   report->ping( 13 );
73    *   int response = report->pong();
74    * }
75    * \endcode
76    *
77    * \par Receiving Task reports (RECEIVER SIDE).
78    *
79    * To receive task reports of type \c Foo the recipient class
80    * derives from callback::ReceiveReport\<Foo\>. callback::ReceiveReport
81    * inherits \c Foo and provides two additional virtual methods:
82    *
83    * \code
84    *   virtual void reportbegin() {}
85    *   virtual void reportend() {}
86    * \endcode
87    *
88    * These two are automatically invoked, whenever the sender
89    * creates a callback::SendReport instance, and when it gets
90    * destructed. So even if the sending task is aborted without
91    * sending an explicit notification, the reciever may notice it,
92    * by overloading \c reportend.
93    *
94    * Overload the methods you're interested in.
95    *
96    * \note In case you must return some value and don't know which,
97    * return the task structures default. The author of the task
98    * structure had to provide this value, so it's probabely better
99    * than anything you \e invent.
100    * \code
101    *   int somefunction()
102    *   {
103    *     ...// don't know what to return?
104    *     return Foo::somefunction();
105    *   }
106    * \endcode
107    *
108    * \par Connecting the Receiver
109    *
110    * For this callback::ReceiveReport provides 4 methods:
111    * \code
112    *  void connect();
113    *  void disconnect();
114    *  bool connected() const;
115    *  ReceiveReport * whoIsConnected() const;
116    * \endcode
117    *
118    * \li \c connect Connect this ReceiveReport (in case some other
119    * ReceiveReport is connected, it get disconnected. Remember its
120    * a Callback light).
121    * \li \c disconnect Disconnect this ReceiveReport in case it is
122    * connected. If not connected nothing happens.
123    * \li \c connected Test wheter this ReceiveReport is currently
124    * connected.
125    * \li \c whoIsConnected Return a 'ReceiveReport*' to the currently
126    * connected ReceiveReport, or \c NULL if none is connected.
127    *
128   */
129   namespace callback
130   { /////////////////////////////////////////////////////////////////
131
132     /**  */
133     struct ReportBase
134     {
135       virtual ~ReportBase()
136       {}
137     };
138
139     /**  */
140     template<class _Report>
141       class DistributeReport;
142
143     /**  */
144     template<class _Report>
145       struct ReceiveReport : public _Report
146       {
147         typedef _Report                   ReportType;
148         typedef ReceiveReport<_Report>    Receiver;
149         typedef DistributeReport<_Report> Distributor;
150
151         virtual ~ReceiveReport()
152         { disconnect(); }
153
154         ReceiveReport * whoIsConnected() const
155         { return Distributor::instance().getReceiver(); }
156
157         bool connected() const
158         { return whoIsConnected() == this; }
159
160         void connect()
161         { Distributor::instance().setReceiver( *this ); }
162
163         void disconnect()
164         { Distributor::instance().unsetReceiver( *this ); }
165
166         virtual void reportbegin()
167         {}
168         virtual void reportend()
169         {}
170       };
171
172     /**  */
173     template<class _Report>
174       struct DistributeReport
175       {
176        public:
177         typedef _Report                   ReportType;
178         typedef ReceiveReport<_Report>    Receiver;
179         typedef DistributeReport<_Report> Distributor;
180
181          static DistributeReport & instance()
182          {
183            static DistributeReport _self;
184            return _self;
185          }
186
187          Receiver * getReceiver() const
188          { return _receiver == &_noReceiver ? 0 : _receiver; }
189
190          void setReceiver( Receiver & rec_r )
191          { _receiver = &rec_r; }
192
193          void unsetReceiver( Receiver & rec_r )
194          { if ( _receiver == &rec_r ) noReceiver(); }
195
196          void noReceiver()
197          { _receiver = &_noReceiver; }
198
199       public:
200          Receiver * operator->()
201          { return _receiver; }
202
203       private:
204         DistributeReport()
205         : _receiver( &_noReceiver )
206         {}
207         Receiver _noReceiver;
208         Receiver * _receiver;
209       };
210
211     /**  */
212     template<class _Report>
213       struct SendReport : private zypp::base::NonCopyable
214       {
215         typedef _Report                   ReportType;
216         typedef ReceiveReport<_Report>    Receiver;
217         typedef DistributeReport<_Report> Distributor;
218
219         SendReport()
220         { Distributor::instance()->reportbegin(); }
221
222         ~SendReport()
223         { Distributor::instance()->reportend(); }
224
225         static Receiver * whoIsConnected()
226         { return Distributor::instance().getReceiver(); }
227
228         static bool connected()
229         { return whoIsConnected(); }
230
231         Distributor & operator->()
232         { return Distributor::instance(); }
233       };
234
235     /** Temporarily connect a ReceiveReport then restore the previous one.
236      *
237      * Pass the ReceiveReport you want to connect temporarily
238      * to the ctor. The ReceiveReport is connected, a previously
239      * connected ReceiveReport is remembered and re-connected in
240      * the dtor.
241      * Use the default ctpr to temporarily disconnect any connected report.
242      * \code
243      *  struct FooReceive : public callback::ReceiveReport<Foo>
244      *  {..};
245      *  struct FooReceive2 : public callback::ReceiveReport<Foo>
246      *  {..};
247      *
248      *  FooReceive  r;
249      *  FooReceive2 r2;
250      *
251      *  r.connect();
252      *  ... // r receiving the report
253      *  {
254      *    callback::TempConnect<Foo> temp( r2 );
255      *    ...// r2 receiving the report
256      *  }
257      *  ...// r receiving the report
258      * \endcode
259     */
260     template<class _Report>
261       struct TempConnect
262       {
263         typedef _Report                   ReportType;
264         typedef ReceiveReport<_Report>    Receiver;
265         typedef DistributeReport<_Report> Distributor;
266
267         TempConnect()
268         : _oldRec( Distributor::instance().getReceiver() )
269         {
270           Distributor::instance().noReceiver();
271         }
272
273         TempConnect( Receiver & rec_r )
274         : _oldRec( Distributor::instance().getReceiver() )
275         {
276           rec_r.connect();
277         }
278
279         ~TempConnect()
280         {
281           if ( _oldRec )
282             Distributor::instance().setReceiver( *_oldRec );
283           else
284             Distributor::instance().noReceiver();
285         }
286       private:
287         Receiver * _oldRec;
288       };
289
290     /////////////////////////////////////////////////////////////////
291   } // namespace callback
292   ///////////////////////////////////////////////////////////////////
293   /////////////////////////////////////////////////////////////////
294 } // namespace zypp
295 ///////////////////////////////////////////////////////////////////
296 #endif // ZYPP_CALLBACK_H