- added struct callback::TempConnect
[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 to 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 somefunktion()
102    *   {
103    *     ...// don't know what to return?
104    *     return Foo::somefunktion();
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 disconneced. 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 DistributeReport<_Report> Distributor;
148
149         virtual ~ReceiveReport()
150         { disconnect(); }
151
152         ReceiveReport * whoIsConnected() const
153         { return Distributor::instance().getReceiver(); }
154
155         bool connected() const
156         { return whoIsConnected() == this; }
157
158         void connect()
159         { Distributor::instance().setReceiver( *this ); }
160
161         void disconnect()
162         { Distributor::instance().unsetReceiver( *this ); }
163
164         virtual void reportbegin()
165         {}
166         virtual void reportend()
167         {}
168       };
169
170     /**  */
171     template<class _Report>
172       struct DistributeReport
173       {
174        public:
175          typedef ReceiveReport<_Report> Receiver;
176
177          static DistributeReport & instance()
178          {
179            static DistributeReport _self;
180            return _self;
181          }
182
183          Receiver * getReceiver() const
184          { return _receiver == &_noReceiver ? NULL : _receiver; }
185
186          void setReceiver( Receiver & rec_r )
187          { _receiver = &rec_r; }
188
189          void unsetReceiver( Receiver & rec_r )
190          { if ( _receiver == &rec_r ) noReceiver(); }
191
192          void noReceiver()
193          { _receiver = &_noReceiver; }
194
195       public:
196          Receiver * operator->()
197          { return _receiver; }
198
199       private:
200         DistributeReport()
201         : _receiver( &_noReceiver )
202         {}
203         Receiver _noReceiver;
204         Receiver * _receiver;
205       };
206
207     /**  */
208     template<class _Report>
209       struct SendReport : private zypp::base::NonCopyable
210       {
211         typedef DistributeReport<_Report> Distributor;
212
213         SendReport()
214         { Distributor::instance()->reportbegin(); }
215
216         ~SendReport()
217         { Distributor::instance()->reportend(); }
218
219         Distributor & operator->()
220         { return Distributor::instance(); }
221       };
222
223     /** Temporarily connect a ReceiveReport then restore the previous one.
224      *
225      * Pass the ReceiveReport you want to connect temporarily
226      * to the ctor. The ReceiveReport is connected, a previously
227      * connected ReceiveReport is remembered and re-connected in
228      * the dtor.
229      * \code
230      *  struct FooReceive : public callback::ReceiveReport<Foo>
231      *  {..};
232      *  struct FooReceive2 : public callback::ReceiveReport<Foo>
233      *  {..};
234      *
235      *  FooReceive  r;
236      *  FooReceive2 r2;
237      *
238      *  r.connect();
239      *  ... // r receiving the report
240      *  {
241      *    callback::TempConnect<Foo> temp( r2 );
242      *    ...// r2 receiving the report
243      *  }
244      *  ...// r receiving the report
245      * \endcode
246     */
247     template<class _Report>
248       struct TempConnect
249       {
250         typedef ReceiveReport<_Report>    Receiver;
251         typedef DistributeReport<_Report> Distributor;
252
253         TempConnect( Receiver & rec_r )
254         : _oldRec( Distributor::instance().getReceiver() )
255         {
256           rec_r.connect();
257         }
258
259         ~TempConnect()
260         {
261           if ( _oldRec )
262             Distributor::instance().setReceiver( *_oldRec );
263           else
264             Distributor::instance().noReceiver();
265         }
266       private:
267         Receiver * _oldRec;
268       };
269
270     /////////////////////////////////////////////////////////////////
271   } // namespace callback
272   ///////////////////////////////////////////////////////////////////
273   /////////////////////////////////////////////////////////////////
274 } // namespace zypp
275 ///////////////////////////////////////////////////////////////////
276 #endif // ZYPP_CALLBACK_H