1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/Callback.h
12 #ifndef ZYPP_CALLBACK_H
13 #define ZYPP_CALLBACK_H
15 #include "zypp/base/NonCopyable.h"
16 #include "zypp/UserData.h"
18 ///////////////////////////////////////////////////////////////////
20 { /////////////////////////////////////////////////////////////////
22 /** \todo Eliminate this! */
29 ///////////////////////////////////////////////////////////////////
32 * \par The task report structure (SENDER SIDE).
34 * A default constructible struct derived from callback::ReportBase.
35 * It \b must \b not contain any data, just virtual methods.
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.
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.
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.
49 * struct Foo : public callback::ReportBase
51 * virtual void ping( int i )
59 * \par Sending a Task report (SENDER SIDE).
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.
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.
72 * callback::SendReport<Foo> report;
74 * int response = report->pong();
78 * \par Receiving Task reports (RECEIVER SIDE).
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:
85 * virtual void reportbegin() {}
86 * virtual void reportend() {}
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.
95 * Overload the methods you're interested in.
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.
104 * ...// don't know what to return?
105 * return Foo::somefunction();
109 * \par Connecting the Receiver
111 * For this callback::ReceiveReport provides 4 methods:
115 * bool connected() const;
116 * ReceiveReport * whoIsConnected() const;
119 * \li \c connect Connect this ReceiveReport (in case some other
120 * ReceiveReport is connected, it get disconnected. Remember its
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
126 * \li \c whoIsConnected Return a 'ReceiveReport*' to the currently
127 * connected ReceiveReport, or \c NULL if none is connected.
129 * \par Passing Userdata via Callbacks
131 * For typesafe passing of user data via callbacks \see \ref UserData.
133 * ReportBase provides a generic \ref callback::ReportBase:report method
134 * which can be used to communicate by encoding everything in its \a UserData
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().
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.
146 { /////////////////////////////////////////////////////////////////
151 typedef callback::UserData UserData;
152 typedef UserData::ContentType ContentType;
154 /** The most generic way of sending/receiving data. */
155 virtual void report( const UserData & userData_r = UserData() )
158 virtual ~ReportBase()
163 template<class TReport>
164 class DistributeReport;
167 template<class TReport>
168 struct ReceiveReport : public TReport
170 typedef TReport ReportType;
171 typedef ReceiveReport<TReport> Receiver;
172 typedef DistributeReport<TReport> Distributor;
174 virtual ~ReceiveReport()
177 ReceiveReport * whoIsConnected() const
178 { return Distributor::instance().getReceiver(); }
180 bool connected() const
181 { return whoIsConnected() == this; }
184 { Distributor::instance().setReceiver( *this ); }
187 { Distributor::instance().unsetReceiver( *this ); }
189 virtual void reportbegin()
191 virtual void reportend()
196 template<class TReport>
197 struct DistributeReport
200 typedef TReport ReportType;
201 typedef ReceiveReport<TReport> Receiver;
202 typedef DistributeReport<TReport> Distributor;
204 static DistributeReport & instance()
206 static DistributeReport _self;
210 Receiver * getReceiver() const
211 { return _receiver == &_noReceiver ? 0 : _receiver; }
213 void setReceiver( Receiver & rec_r )
214 { _receiver = &rec_r; }
216 void unsetReceiver( Receiver & rec_r )
217 { if ( _receiver == &rec_r ) noReceiver(); }
220 { _receiver = &_noReceiver; }
223 Receiver * operator->()
224 { return _receiver; }
228 : _receiver( &_noReceiver )
230 Receiver _noReceiver;
231 Receiver * _receiver;
235 template<class TReport>
236 struct SendReport : private zypp::base::NonCopyable
238 typedef TReport ReportType;
239 typedef ReceiveReport<TReport> Receiver;
240 typedef DistributeReport<TReport> Distributor;
243 { Distributor::instance()->reportbegin(); }
246 { Distributor::instance()->reportend(); }
248 static Receiver * whoIsConnected()
249 { return Distributor::instance().getReceiver(); }
251 static bool connected()
252 { return whoIsConnected(); }
254 Distributor & operator->()
255 { return Distributor::instance(); }
258 /** Temporarily connect a ReceiveReport then restore the previous one.
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
264 * Use the default ctpr to temporarily disconnect any connected report.
266 * struct FooReceive : public callback::ReceiveReport<Foo>
268 * struct FooReceive2 : public callback::ReceiveReport<Foo>
275 * ... // r receiving the report
277 * callback::TempConnect<Foo> temp( r2 );
278 * ...// r2 receiving the report
280 * ...// r receiving the report
283 template<class TReport>
286 typedef TReport ReportType;
287 typedef ReceiveReport<TReport> Receiver;
288 typedef DistributeReport<TReport> Distributor;
291 : _oldRec( Distributor::instance().getReceiver() )
293 Distributor::instance().noReceiver();
296 TempConnect( Receiver & rec_r )
297 : _oldRec( Distributor::instance().getReceiver() )
305 Distributor::instance().setReceiver( *_oldRec );
307 Distributor::instance().noReceiver();
313 /////////////////////////////////////////////////////////////////
314 } // namespace callback
315 ///////////////////////////////////////////////////////////////////
316 /////////////////////////////////////////////////////////////////
318 ///////////////////////////////////////////////////////////////////
319 #endif // ZYPP_CALLBACK_H