Imported Upstream version 15.0.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
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 whether 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    * \par Passing Userdata via Callbacks
129    *
130    * For typesafe passing of user data via callbacks \see \ref UserData.
131    *
132    */
133   namespace callback
134   { /////////////////////////////////////////////////////////////////
135
136     /**  */
137     struct ReportBase
138     {
139       virtual ~ReportBase()
140       {}
141     };
142
143     /**  */
144     template<class _Report>
145       class DistributeReport;
146
147     /**  */
148     template<class _Report>
149       struct ReceiveReport : public _Report
150       {
151         typedef _Report                   ReportType;
152         typedef ReceiveReport<_Report>    Receiver;
153         typedef DistributeReport<_Report> Distributor;
154
155         virtual ~ReceiveReport()
156         { disconnect(); }
157
158         ReceiveReport * whoIsConnected() const
159         { return Distributor::instance().getReceiver(); }
160
161         bool connected() const
162         { return whoIsConnected() == this; }
163
164         void connect()
165         { Distributor::instance().setReceiver( *this ); }
166
167         void disconnect()
168         { Distributor::instance().unsetReceiver( *this ); }
169
170         virtual void reportbegin()
171         {}
172         virtual void reportend()
173         {}
174       };
175
176     /**  */
177     template<class _Report>
178       struct DistributeReport
179       {
180        public:
181         typedef _Report                   ReportType;
182         typedef ReceiveReport<_Report>    Receiver;
183         typedef DistributeReport<_Report> Distributor;
184
185          static DistributeReport & instance()
186          {
187            static DistributeReport _self;
188            return _self;
189          }
190
191          Receiver * getReceiver() const
192          { return _receiver == &_noReceiver ? 0 : _receiver; }
193
194          void setReceiver( Receiver & rec_r )
195          { _receiver = &rec_r; }
196
197          void unsetReceiver( Receiver & rec_r )
198          { if ( _receiver == &rec_r ) noReceiver(); }
199
200          void noReceiver()
201          { _receiver = &_noReceiver; }
202
203       public:
204          Receiver * operator->()
205          { return _receiver; }
206
207       private:
208         DistributeReport()
209         : _receiver( &_noReceiver )
210         {}
211         Receiver _noReceiver;
212         Receiver * _receiver;
213       };
214
215     /**  */
216     template<class _Report>
217       struct SendReport : private zypp::base::NonCopyable
218       {
219         typedef _Report                   ReportType;
220         typedef ReceiveReport<_Report>    Receiver;
221         typedef DistributeReport<_Report> Distributor;
222
223         SendReport()
224         { Distributor::instance()->reportbegin(); }
225
226         ~SendReport()
227         { Distributor::instance()->reportend(); }
228
229         static Receiver * whoIsConnected()
230         { return Distributor::instance().getReceiver(); }
231
232         static bool connected()
233         { return whoIsConnected(); }
234
235         Distributor & operator->()
236         { return Distributor::instance(); }
237       };
238
239     /** Temporarily connect a ReceiveReport then restore the previous one.
240      *
241      * Pass the ReceiveReport you want to connect temporarily
242      * to the ctor. The ReceiveReport is connected, a previously
243      * connected ReceiveReport is remembered and re-connected in
244      * the dtor.
245      * Use the default ctpr to temporarily disconnect any connected report.
246      * \code
247      *  struct FooReceive : public callback::ReceiveReport<Foo>
248      *  {..};
249      *  struct FooReceive2 : public callback::ReceiveReport<Foo>
250      *  {..};
251      *
252      *  FooReceive  r;
253      *  FooReceive2 r2;
254      *
255      *  r.connect();
256      *  ... // r receiving the report
257      *  {
258      *    callback::TempConnect<Foo> temp( r2 );
259      *    ...// r2 receiving the report
260      *  }
261      *  ...// r receiving the report
262      * \endcode
263     */
264     template<class _Report>
265       struct TempConnect
266       {
267         typedef _Report                   ReportType;
268         typedef ReceiveReport<_Report>    Receiver;
269         typedef DistributeReport<_Report> Distributor;
270
271         TempConnect()
272         : _oldRec( Distributor::instance().getReceiver() )
273         {
274           Distributor::instance().noReceiver();
275         }
276
277         TempConnect( Receiver & rec_r )
278         : _oldRec( Distributor::instance().getReceiver() )
279         {
280           rec_r.connect();
281         }
282
283         ~TempConnect()
284         {
285           if ( _oldRec )
286             Distributor::instance().setReceiver( *_oldRec );
287           else
288             Distributor::instance().noReceiver();
289         }
290       private:
291         Receiver * _oldRec;
292       };
293
294     /////////////////////////////////////////////////////////////////
295   } // namespace callback
296   ///////////////////////////////////////////////////////////////////
297   /////////////////////////////////////////////////////////////////
298 } // namespace zypp
299 ///////////////////////////////////////////////////////////////////
300 #endif // ZYPP_CALLBACK_H