Imported Upstream version 16.3.2
[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 contain 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<TReport>, where TReport
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    * ReportBase provides a generic \ref callback::ReportBase:report method
134    * which can be used to communicate by encoding everything in its \a UserData
135    * argument.
136    *
137    * Convenient sending can be achieved by installing non-virtual methods
138    * in the TReport class, which encode the arguments in UserData and send
139    * them via ReportBase::report().
140    *
141    * Convenient receiving can be achieved by installing virtual methods in
142    * the TReport class, which can be simply overloaded by the receiver. Downside
143    * of this is that adding virtual methods breaks binary compatibility.
144    */
145   namespace callback
146   { /////////////////////////////////////////////////////////////////
147
148     /**  */
149     struct ReportBase
150     {
151       typedef callback::UserData UserData;
152       typedef UserData::ContentType ContentType;
153
154       /** The most generic way of sending/receiving data. */
155       virtual void report( const UserData & userData_r = UserData() )
156       {}
157
158       virtual ~ReportBase()
159       {}
160     };
161
162     /**  */
163     template<class TReport>
164       class DistributeReport;
165
166     /**  */
167     template<class TReport>
168       struct ReceiveReport : public TReport
169       {
170         typedef TReport                   ReportType;
171         typedef ReceiveReport<TReport>    Receiver;
172         typedef DistributeReport<TReport> Distributor;
173
174         virtual ~ReceiveReport()
175         { disconnect(); }
176
177         ReceiveReport * whoIsConnected() const
178         { return Distributor::instance().getReceiver(); }
179
180         bool connected() const
181         { return whoIsConnected() == this; }
182
183         void connect()
184         { Distributor::instance().setReceiver( *this ); }
185
186         void disconnect()
187         { Distributor::instance().unsetReceiver( *this ); }
188
189         virtual void reportbegin()
190         {}
191         virtual void reportend()
192         {}
193       };
194
195     /**  */
196     template<class TReport>
197       struct DistributeReport
198       {
199        public:
200         typedef TReport                   ReportType;
201         typedef ReceiveReport<TReport>    Receiver;
202         typedef DistributeReport<TReport> Distributor;
203
204          static DistributeReport & instance()
205          {
206            static DistributeReport _self;
207            return _self;
208          }
209
210          Receiver * getReceiver() const
211          { return _receiver == &_noReceiver ? 0 : _receiver; }
212
213          void setReceiver( Receiver & rec_r )
214          { _receiver = &rec_r; }
215
216          void unsetReceiver( Receiver & rec_r )
217          { if ( _receiver == &rec_r ) noReceiver(); }
218
219          void noReceiver()
220          { _receiver = &_noReceiver; }
221
222       public:
223          Receiver * operator->()
224          { return _receiver; }
225
226       private:
227         DistributeReport()
228         : _receiver( &_noReceiver )
229         {}
230         Receiver _noReceiver;
231         Receiver * _receiver;
232       };
233
234     /**  */
235     template<class TReport>
236       struct SendReport : private zypp::base::NonCopyable
237       {
238         typedef TReport                   ReportType;
239         typedef ReceiveReport<TReport>    Receiver;
240         typedef DistributeReport<TReport> Distributor;
241
242         SendReport()
243         { Distributor::instance()->reportbegin(); }
244
245         ~SendReport()
246         { Distributor::instance()->reportend(); }
247
248         static Receiver * whoIsConnected()
249         { return Distributor::instance().getReceiver(); }
250
251         static bool connected()
252         { return whoIsConnected(); }
253
254         Distributor & operator->()
255         { return Distributor::instance(); }
256       };
257
258     /** Temporarily connect a ReceiveReport then restore the previous one.
259      *
260      * Pass the ReceiveReport you want to connect temporarily
261      * to the ctor. The ReceiveReport is connected, a previously
262      * connected ReceiveReport is remembered and re-connected in
263      * the dtor.
264      * Use the default ctpr to temporarily disconnect any connected report.
265      * \code
266      *  struct FooReceive : public callback::ReceiveReport<Foo>
267      *  {..};
268      *  struct FooReceive2 : public callback::ReceiveReport<Foo>
269      *  {..};
270      *
271      *  FooReceive  r;
272      *  FooReceive2 r2;
273      *
274      *  r.connect();
275      *  ... // r receiving the report
276      *  {
277      *    callback::TempConnect<Foo> temp( r2 );
278      *    ...// r2 receiving the report
279      *  }
280      *  ...// r receiving the report
281      * \endcode
282     */
283     template<class TReport>
284       struct TempConnect
285       {
286         typedef TReport                   ReportType;
287         typedef ReceiveReport<TReport>    Receiver;
288         typedef DistributeReport<TReport> Distributor;
289
290         TempConnect()
291         : _oldRec( Distributor::instance().getReceiver() )
292         {
293           Distributor::instance().noReceiver();
294         }
295
296         TempConnect( Receiver & rec_r )
297         : _oldRec( Distributor::instance().getReceiver() )
298         {
299           rec_r.connect();
300         }
301
302         ~TempConnect()
303         {
304           if ( _oldRec )
305             Distributor::instance().setReceiver( *_oldRec );
306           else
307             Distributor::instance().noReceiver();
308         }
309       private:
310         Receiver * _oldRec;
311       };
312
313     /////////////////////////////////////////////////////////////////
314   } // namespace callback
315   ///////////////////////////////////////////////////////////////////
316   /////////////////////////////////////////////////////////////////
317 } // namespace zypp
318 ///////////////////////////////////////////////////////////////////
319 #endif // ZYPP_CALLBACK_H