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