2010. 5. 22. 17:34

클래스 멤버 메소드를 콜백 함수로 사용하기

이것은 초급자를 위한 팁임을 미리 말씀드립니다. 

 

1. Motivation

 우리는 개발한 상위 라이브러리에서 사용하고 싶은 이벤트 혹은 메시지를

 등록하고 그것을 사용자 정의 콜백 함수로 호출하는 구조를 사용자에게

 제공해 주고 싶은 경우가 있습니다.

 MFC와 같은 프레임워크처럼, ON_MESSAGE( UserMessage, OnUserFunction )로

 사용자 정의 메시지 및 이벤트 처리하게 해주는 메커니즘을 말이죠.

 

2. Solution 

우리가 개발한 라이브러리에서 다음과 같이 사용자 이벤트와 콜백 함수를

등록할 수 있는 함수가 있습니다.

RegisterEventHandler( ON_PMSG_CHAT, this, ClassType::OnPmsgChat );

이 구현을 위해 가장 고민해 보아야 할 문제는 여러 클래스 타입들의 메소드를

어떻게 콜백 함수로 등록할 수 있는가입니다. 다시 말해, 이것은 콜백 함수로

등록될 클래스의 타입과 클래스 메소드의 함수 포인터형을 알 수 없다는 것입니다.

MFC의 경우에는 이벤트 처리기와 메시지 맵은 CObject 이하의 수많은 클래스

타입들에 대한 오버로딩 메소드를 미리 정의하고 구현해 놓았지요.

 


3. Conclusion

아래 소스와 같은 구현으로 어떠한 클래스의 타입 메소드가 인수로 대입되더라도

콜백 함수로 등록하고 처리할 수 있습니다.

콜백 함수의 등록은 아래의 매크로 함수로 할 수 있습니다.

RegisterEventHandler( ON_PMSG_CHAT, this, ClassType::OnPmsgChat );

아래의 소스에서 매크로로 구현된 CMyClass::GetInstance()->AddEventHandler()는

우리가 개발한 라이브러리의 클래스 내에서 (싱글톤 패턴이라는 가정하에서는) Static

Object의 포인터를 저장하는 역할을 하는 메소드입니다.

 

// HandlerManager.h

#ifndef  HANDLER_MANAGER_
#define HANDLER_MANAGER_

#include <iostream>
#include <windows.h>

#define RegisterEventHandler(dwMessage, pInst, lpFunc);                                     \
           static CHandlerObject &lpfn##dwMessage = SetEventProc( pInst, &lpFunc );   \
           CMyClass::GetInstance()->AddEventHandler( &lpfn##dwMessage );

#define CALLBACKPROC(dwMessage, lpBuffer, dwSize);                                       \
           lpfn##dwMessage.CallBackProc( dwMessage, lpBuffer, dwSize );

 
class CHandlerObject
{
public:
    virtual LPVOID CallBackProc(DWORD dwMessage, LPVOID lpBuffer, DWORD dwSize) = 0;
};


template <class ObjectType, typename FuncPtrType>
class CHandlerManager : public CHandlerObject
{
private:
    ObjectType *m_pObject;
    FuncPtrType m_pFuncPtrType;

    int nCntHandler;

public:
    CHandlerManager(ObjectType* pObject, FuncPtrType pFuncPtrType) : m_pObject( pObject ),
                                                                                                 m_pFuncPtrType               
                                                                                                ( pFuncPtrType ) {}
    ~CHandlerManager() {}
 

    LPVOID CallBackProc(DWORD dwMessage, LPVOID lpBuffer, DWORD dwSize)
    {
               (m_pObject->*m_pFuncPtrType)( dwMessage, lpBuffer, dwSize );

               return NULL;
    }

};

 
template <class ObjectType, typename FuncPtrType>
CHandlerManager<ObjectType, FuncPtrType>
SetEventProc(ObjectType *pObject, FuncPtrType pFuncPtr)
{
               return CHandlerManager<ObjectType, FuncPtrType>(pObject, pFuncPtr);
}


#endif

 

Written By Simhyeon, Choe