2010年12月1日 星期三

在C++ Class中使用callback function

C++ 的類別中如何使用APIcallback function, 相信這是剛學會callback function程式員會遇到的問題, 所以在這整理一下解決的方法.
1.      callback function要成為類別的member function必須宣告成static

解析 - 首先, 我們先來釐清類別中的成員函式, 是如何存取成員變數(注意: 記憶體中只會有一份類別成員函式, 而類別成員變數可能有好幾份).

關鍵就是透過this指標. 因為, C++在編譯時, 會隱含this指標在類別的成員函式中
例如有一個類別和其成員函式為:
class CShape
{
...
public:

void SetColor(int color) { m_color = color;};
}
經過編譯後, 會變成
class CShape
{
...
public:
    void SetColor(int color, (CShape*)this) { this->m_color = color; }
};
因此, 透過this指標可用來指向呼叫它的物件(instance), 進而存取各自的成員變數.
callback function通常是由系統呼叫, 也就是系統不需經由任何物件呼叫此函式,  因此member callback function的 this 指標會沒有指向任何實體物件, 這將會造成錯誤. 

為了不要讓callback function自動含有this指標, 可以將callback function宣告成static member function. 
如此一來, callback function就變成 global function, 不需透過物件的實體就可呼叫.

2.   若要在callback function中存取類別成員變數或成員函式, 需要修改callback function prototype, 讓函式的參數列中, 可傳入this指標.

解析 - 由於static member function是屬於類別而不是物件, 所以呼叫static member function時, 不會傳入物件的位址, 所以static member function中不會有this指標, 因此, static member function中不允許使用non-static成員.
 

所以, 如果要在static callback function中存取non-static成員, 需要在callback functuon的參數列表中傳入物件的this指標.

例如API中有一個callback function宣告如下:
      void (*TouchCallback)(TouchPoint p);

若要成為類別的成員函式, 可改為:
     void (*TouchCallback)(TouchPoint p, void* pInstance);
 


下面為範例:
有一個Touch API, 它利用callback的方法來取得觸控點資料.其介面如下:

#ifdef  TOUCHAPI_EXPORTS
  #define TOUCH_API __declspec(dllexport)
#else
  #define TOUCH_API __declspec(dllimport)
#endif

// 定義觸控點的相關資訊
struct TouchPoint
{
 int X; 

 int Y; 
};

// 定義可傳入this指標的callback function prototype
typedef void (_stdcall *CB_TOUCH_CLASS_FUNC) (TouchPoint, void* pObject);

// 定義了一個RegisterCBTouchMemberFunc的函式,
// 可傳入一個具有CB_TOUCH_FUNC型別的callback function
TOUCH_API void RegisterCBTouchMemberFunc(CB_TOUCH_CLASS_FUNC callback,void* pObject);

定義一個CTouchClass的類別, 必且利用Touch APIcallback function 作為member function, 程式碼如下:
TouchClass.h

#include "TouchAPI.h"
class CTouchClass
{
private:
 int m_x;
 int m_y;

public:
 CTouchClass(void);
 ~CTouchClass(void);

public:
 static void _stdcall TouchCallback(TouchPoint point, void* pObject);

public: 
 int GetTouchX();
 int GetTouchY();

};
TouchClass.cpp

#include "StdAfx.h"
#include "TouchClass.h"

CTouchClass::CTouchClass(void)
{
    this->m_x = 0;
    this->m_y = 0;
}

CTouchClass::~CTouchClass(void)
{
   this->m_x = 0;
   this->m_y = 0;
}

// 利用this指標存取member function
void CTouchClass::TouchCallback(TouchPoint point, void* pObject)
{
   CTouchClass* pClass =  (CTouchClass*)pObject;
   pClass->m_x = point.X;
   pClass->m_y = point.Y;
}
 
int CTouchClass::GetTouchX()
{
   return m_x;
}
    
int CTouchClass::GetTouchY()
{
   return m_y;
}

沒有留言:

張貼留言