-
-
Methods for implementing callback in C ++
(1) callback Method
The essence of callback is to set a function pointer and call this method when an event needs to be triggered. For example, Windows's window message processing function is of this type. For example, in the following sample code, an event outside the notification must be triggered when download is complete:
typedef void (__stdcall *DownloadCallback)(const char* pURL, bool bOK);void DownloadFile(const char* pURL, DownloadCallback callback){ cout << "downloading: " << pURL << "" << endl; callback(pURL, true);}void __stdcall OnDownloadFinished(const char* pURL, bool bOK){ cout << "OnDownloadFinished, URL:" << pURL << " status:" << bOK << endl;}
(2) sink Mode
The essence of sink is that you implement a C ++ interface as required by the other party, and then set your implemented interface to the other party. The other party calls this interface when it needs to trigger an event, the connection point in COM is in this way. If the above requirement for downloading files is implemented using sink, the Code is as follows:
class IDownloadSink{public: virtual void OnDownloadFinished(const char* pURL, bool bOK) = 0;};class CMyDownloader{public: CMyDownloader(IDownloadSink* pSink) :m_pSink(pSink) { } void DownloadFile(const char* pURL) { cout << "downloading: " << pURL << "" << endl; if(m_pSink != NULL) { m_pSink->OnDownloadFinished(pURL, true); } }private: IDownloadSink* m_pSink;};class CMyFile: public IDownloadSink{public: void download() { CMyDownloader downloader(this); downloader.DownloadFile("www.baidu.com"); } virtual void OnDownloadFinished(const char* pURL, bool bOK) { cout << "OnDownloadFinished, URL:" << pURL << " status:" << bOK << endl; }};
(3) delegate Mode
The essence of delegate is to set the member function pointer to the other party and then let the other party call the function when an event needs to be triggered. C # using delegate to implement event makes C ++ programmers very envious. In C ++, it is still very troublesome to implement delegate because of the language itself. The preceding example is implemented using delegate as follows:
class CDownloadDelegateBase{public: virtual void Fire(const char* pURL, bool bOK) = 0;};template<typename O, typename T>class CDownloadDelegate: public CDownloadDelegateBase{ typedef void (T::*Fun)(const char*, bool);public: CDownloadDelegate(O* pObj = NULL, Fun pFun = NULL) :m_pFun(pFun), m_pObj(pObj) { } virtual void Fire(const char* pURL, bool bOK) { if(m_pFun != NULL && m_pObj != NULL) { (m_pObj->*m_pFun)(pURL, bOK); } }private: Fun m_pFun; O* m_pObj;};template<typename O, typename T>CDownloadDelegate<O,T>* MakeDelegate(O* pObject, void (T::*pFun)(const char* pURL, bool)){ return new CDownloadDelegate<O, T>(pObject, pFun);}class CDownloadEvent{public: ~CDownloadEvent() { vector<CDownloadDelegateBase*>::iterator itr = m_arDelegates.begin(); while (itr != m_arDelegates.end()) { delete *itr; ++itr; } m_arDelegates.clear(); } void operator += (CDownloadDelegateBase* p) { m_arDelegates.push_back(p); } void operator -= (CDownloadDelegateBase* p) { ITR itr = remove(m_arDelegates.begin(), m_arDelegates.end(), p); ITR itrTemp = itr; while (itrTemp != m_arDelegates.end()) { delete *itr; ++itr; } m_arDelegates.erase(itr, m_arDelegates.end()); } void operator()(const char* pURL, bool bOK) { ITR itrTemp = m_arDelegates.begin(); while (itrTemp != m_arDelegates.end()) { (*itrTemp)->Fire(pURL, bOK); ++itrTemp; } }private: vector<CDownloadDelegateBase*> m_arDelegates; typedef vector<CDownloadDelegateBase*>::iterator ITR;};class CMyDownloaderEx{public: void DownloadFile(const char* pURL) { cout << "downloading: " << pURL << "" << endl; downloadEvent(pURL, true); } CDownloadEvent downloadEvent;};class CMyFileEx{public: void download() { CMyDownloaderEx downloader; downloader.downloadEvent += MakeDelegate(this, &CMyFileEx::OnDownloadFinished); downloader.DownloadFile("www.baidu.com"); } virtual void OnDownloadFinished(const char* pURL, bool bOK) { cout << "OnDownloadFinished, URL:" << pURL << " status:" << bOK << endl; }};
We can see that the delegate method has more code than the other two methods above, and we have fixed parameter quantity and type implementation methods above. If you want to implement variable parameters, it is more troublesome. You can refer to the following two methods for variable parameters:
Yet another C #-style delegate class in Standard C ++
Member function pointers and the fastest possible C ++ delegates
We can use the following code to test our above implementation:
int _tmain(int argc, _TCHAR* argv[]){ DownloadFile("www.baidu.com", OnDownloadFinished); CMyFile f1; f1.download(); CMyFileEx ff; ff.download(); system("pause"); return 0;}
Finally, we will briefly compare the above three methods for implementing callback:
The first callback method is process-oriented. It is easy to use and flexible, just like the C language itself.
The second method of sink is object-oriented, which is widely used in C ++. It can encapsulate a set of callback interfaces in a sink and is suitable for a series of relatively fixed callback events.
The third method of delegate is also object-oriented. Unlike sink encapsulation, the delegate encapsulation is based on functions, and the granularity is smaller and more flexible than sink.
-
Which method do you prefer to implement callback?