Boost Meta State Machine 의 위대함에 한참 넋을 놓고 파헤치고 있을때 뭔가 어딘가에 잘 써먹을 수 있어
보이는 이벤트 처리 방법을 발견했습니다. 유레카!
그래서 일반적으로 서버 & 클라이언트의 통신 구조에서 주고받은 패킷 이벤트를 처리할 때 사용해보기로
마음먹었습니다.
#pragma once
//================================================================
/**
@author skhong
@since 11/8/2014 14:39
@remarks EventObject
*/
//================================================================
//
// Windows
//
#include <windows.h>
//
// boost
//
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
//
// VMEvent Object
//
#include "VMEventHandled.h"
#include "VMEventObject.h"
namespace VMFramework
{
namespace FCT
{
struct NONE
{
NONE(){}
NONE(CVMEventObject const&){}
};
} // namespace FCT
template <class EXEC,class CONDITION=FCT::NONE,class REJECT=FCT::NONE>
struct VMRow
{
typedef EXEC Execute;
typedef CONDITION Condition;
typedef REJECT Reject;
typedef HandledEventEnum result_type;
HandledEventEnum exec_call( CVMEventObject const& _EventObject )
{
// create functor, call it
Execute()(_EventObject);
return HANDLED_EXEC_TRUE;
}
HandledEventEnum reject_call( CVMEventObject const& _EventObject )
{
// create functor, call it
Reject()(_EventObject);
return HANDLED_EXEC_REJECT;
}
BOOL cond_call( CVMEventObject const& _EventObject )
{
// create functor, call it
return Condition()(_EventObject);
}
HandledEventEnum operator()( CVMEventObject const& _EventObject )
{
// 성공하면
if( TRUE == cond_call( _EventObject ) )
{
return exec_call( _EventObject );
}
else
{
return reject_call( _EventObject );
}
return HANDLED_EXEC_TRUE;
}
};
template<class EXEC,class CONDITION>
struct VMRow<EXEC,CONDITION,FCT::NONE>
{
typedef EXEC Execute;
typedef CONDITION Condition;
typedef FCT::NONE Reject;
typedef HandledEventEnum result_type;
HandledEventEnum exec_call( CVMEventObject const& _EventObject )
{
// create functor, call it
Execute()(_EventObject);
return HANDLED_EXEC_TRUE;
}
BOOL cond_call( CVMEventObject const& _EventObject )
{
// create functor, call it
return Condition()(_EventObject);
}
HandledEventEnum operator()( CVMEventObject const& _EventObject )
{
// 성공하면
if( TRUE == cond_call( _EventObject ) )
{
return exec_call( _EventObject );
}
return HANDLED_EXEC_REJECT;
}
};
template<class CONDITION>
struct VMRow<FCT::NONE,CONDITION,FCT::NONE>
{
typedef FCT::NONE Execute;
typedef CONDITION Condition;
typedef FCT::NONE Reject;
typedef HandledEventEnum result_type;
BOOL cond_call( CVMEventObject const& _EventObject )
{
// create functor, call it
return Condition()(_EventObject);
}
HandledEventEnum operator()( CVMEventObject const& _EventObject )
{
// 성공하면
if( TRUE == cond_call( _EventObject ) )
{
return HANDLED_EXEC_TRUE;
}
return HANDLED_EXEC_REJECT;
}
};
template<class CONDITION, class REJECT>
struct VMRow<FCT::NONE,CONDITION,REJECT>
{
typedef FCT::NONE Execute;
typedef CONDITION Condition;
typedef REJECT Reject;
typedef HandledEventEnum result_type;
BOOL cond_call( CVMEventObject const& _EventObject )
{
// create functor, call it
return Condition()(_EventObject);
}
HandledEventEnum reject_call( CVMEventObject const& _EventObject )
{
// create functor, call it
Reject()(_EventObject);
return HANDLED_EXEC_REJECT;
}
HandledEventEnum operator()( CVMEventObject const& _EventObject )
{
// 성공하면
if( FALSE == cond_call( _EventObject ) )
{
return reject_call( _EventObject );
}
return HANDLED_EXEC_TRUE;
}
};
template<class EXEC,class REJECT>
struct VMRow<EXEC,FCT::NONE,REJECT>
{
typedef EXEC Execute;
typedef FCT::NONE Condition;
typedef REJECT Reject;
typedef HandledEventEnum result_type;
HandledEventEnum exec_call( CVMEventObject const& _EventObject )
{
// create functor, call it
Execute()(_EventObject);
return HANDLED_EXEC_TRUE;
}
HandledEventEnum reject_call( CVMEventObject const& _EventObject )
{
// create functor, call it
Reject()(_EventObject);
return HANDLED_EXEC_REJECT;
}
HandledEventEnum operator()( CVMEventObject const& _EventObject )
{
// 성공하면
if( FALSE == cond_call( _EventObject ) )
{
return reject_call( _EventObject );
}
return HANDLED_EXEC_TRUE;
}
};
template<class EXEC>
struct VMRow<EXEC,FCT::NONE,FCT::NONE>
{
typedef EXEC Execute;
typedef FCT::NONE Condition;
typedef FCT::NONE Reject;
typedef HandledEventEnum result_type;
HandledEventEnum exec_call( CVMEventObject const& _EventObject )
{
// create functor, call it
Execute()(_EventObject);
return HANDLED_EXEC_TRUE;
}
HandledEventEnum operator()( CVMEventObject const& _EventObject )
{
return exec_call( _EventObject );
}
};
// 액션 시퀀스를 구현해보자
template <class Sequence>
struct VMExecSeq_
{
typedef Sequence sequence;
struct Call
{
Call(CVMEventObject const& _EventObject ):
EventObject_(_EventObject) {}
template <class FCT>
void operator()( FCT const& )
{
FCT()( EventObject_ );
}
private:
CVMEventObject const& EventObject_;
};
HandledEventEnum operator()( CVMEventObject const& _EventObject )
{
boost::mpl::for_each< Sequence >( Call( _EventObject ) );
return HANDLED_EXEC_TRUE;
}
};
// 가드 Or 조건
template <class T1,class T2>
struct VMOr_
{
BOOL operator()( CVMEventObject const& _EventObject )
{
return (T1()(_EventObject) || T2()(_EventObject));
}
};
// 가드 And 조건
template <class T1,class T2>
struct VMAnd_
{
BOOL operator()( CVMEventObject const& _EventObject )
{
return (T1()(_EventObject) && T2()(_EventObject));
}
};
} // namespace VMFramework
위와 같이 템플릿 특수화를 이용해서 모든 조건에 동작 가능한 Event Functor 를 만들고 난 후
이벤트를 등록하기 쉽도록 한번더 랩핑했습니다.
#pragma once
//================================================================
/**
@author skhong
@since 19/12/2014 15:09
@remarks VMEvent Registration
*/
//================================================================
#include "VMEventFunctor.h"
#include "VMEventTable.h"
namespace VMFramework
{
template <class EXEC,class CONDITION=FCT::NONE,class REJECT=FCT::NONE>
struct VMEventRegistration
{
typedef EXEC Execute;
typedef CONDITION Condition;
typedef REJECT Reject;
VMEventRegistration( CommonEvent::ID::Value _ID, CVMEventTable* const _Self )
{
_Self->addEventRow( _ID, boost::bind( VMRow< Execute, Condition, Reject >(), _1 ) );
}
};
template<class EXEC,class CONDITION>
struct VMEventRegistration<EXEC,CONDITION,FCT::NONE>
{
typedef EXEC Execute;
typedef CONDITION Condition;
VMEventRegistration( CommonEvent::ID::Value _ID, CVMEventTable* const _Self )
{
_Self->addEventRow( _ID, boost::bind( VMRow< Execute, Condition >(), _1 ) );
}
};
template<class CONDITION>
struct VMEventRegistration<FCT::NONE,CONDITION,FCT::NONE>
{
typedef FCT::NONE Execute;
typedef CONDITION Condition;
typedef FCT::NONE Reject;
VMEventRegistration( CommonEvent::ID::Value _ID, CVMEventTable* const _Self )
{
_Self->addEventRow( _ID, boost::bind( VMRow< Execute, Condition >(), _1 ) );
}
};
template<class CONDITION, class REJECT>
struct VMEventRegistration<FCT::NONE,CONDITION,REJECT>
{
typedef FCT::NONE Execute;
typedef CONDITION Condition;
typedef REJECT Reject;
VMEventRegistration( CommonEvent::ID::Value _ID, CVMEventTable* const _Self )
{
_Self->addEventRow( _ID, boost::bind( VMRow< Execute, Condition, Reject >(), _1 ) );
}
};
template<class EXEC,class REJECT>
struct VMEventRegistration<EXEC,FCT::NONE,REJECT>
{
typedef EXEC Execute;
typedef FCT::NONE Condition;
typedef REJECT Reject;
VMEventRegistration( CommonEvent::ID::Value _ID, CVMEventTable* const _Self )
{
_Self->addEventRow( _ID, boost::bind( VMRow< Execute, Condition, Reject >(), _1 ) );
}
};
template<class EXEC>
struct VMEventRegistration<EXEC,FCT::NONE,FCT::NONE>
{
typedef EXEC Execute;
typedef FCT::NONE Condition;
typedef FCT::NONE Reject;
VMEventRegistration( CommonEvent::ID::Value _ID, CVMEventTable* const _Self )
{
_Self->addEventRow( _ID, boost::bind( VMRow< Execute, Condition, Reject >(), _1 ) );
}
};
} // namespace VMFramework
자 이제 이벤트를 등록하고 어떻게 사용하는지 보겠습니다.
VMEventRegistration
<
RoomEvtHandler::ReconnectMachine // 재접속 처리
>( CommonEvent::ID::C_Req_ReconnectMachine, _getEventTable() ); // 머신이 재접속을 요청한다.
패킷을 받고 연결할 콜백함수를 등록하는 방법입니다.
// 등록한 함수도 사실 함수객체 입니다.
struct ReconnectMachine { typedef void result_type; void operator()( VMEventObject const& _Evt ); };
오퍼레이터에 실제 동작 구현을 합니다.
실제 작업을 하다보면 이벤트를 받고 재접속 처리뿐만 아니라 사실을 더 많은 일을 해야합니다.
다른 예를 들자면
VMEventRegistration
<
VMExecSeq_< mpl::vector
<
GamePlayEvtHandler::RecvRoomScoreCard, // 룸 스코어 카드를 저장한다.
RoomEvtHandler::BroadcastRoomScoreCard, // 새롭게 입장 플레이어 데이터를 방안에 플레이어에게 전송해준다.
RoomEvtHandler::SendScoreCardListInRoom // 방안의 모든 스코어 카드를 전송한다.
> >
>( CommonEvent::ID::Inf_RoomScoreCard, _getEventTable() ); // 플레이어 데이터 받기
위와 같은 이벤트는 스코어 카드의 정보를 받고 3가지 동작을 순서대로 처리가 가능합니다.
여기서 한가지 더 나아가면 이벤트 등록과 함께 조건의 분기 처리도 가능해집니다.
VMEventRegistration
<
// 방장일 경우(TRUE)
VMExecSeq_< mpl::vector
<
RoomEvtHandler::DestroyRoom, // 방장일시 방폭
RoomEvtHandler::SendUpdatedRoomForShopCode // 같은 매장내에 있는 머신에게 방 정보를 갱신시켜준다.
> >,
// 이것은 조건문이 됩니다.
RoomEvtHandler::CheckRoomChief, // 방장 인가?
// 방장이 아닐경우( FALSE )
VMExecSeq_< mpl::vector
<
RoomEvtHandler::LeaveRoom, // 방장아닐시 방 나가기
RoomEvtHandler::SendUpdatedRoomForShopCode // 같은 매장내에 있는 머신에게 방 정보를 갱신시켜준다.
> >
>( CommonEvent::ID::C_Req_Disconnect, _getEventTable() ); // 접속 종료 요청
위와같이 조건의 따른 분기 처리도 가능해집니다. VMExecSeq_ 의 갯수 제한도 사실상 없다고 보시면 됩니다.
VMEventRegistration
<
// 방장일 경우(TRUE)
FCT::NONE, // 아무것도 처리하지 않는다.
// 이것은 조건문이 됩니다.
RoomEvtHandler::CheckRoomChief, // 방장 인가?
// 방장이 아닐경우( FALSE )
VMExecSeq_< mpl::vector
<
RoomEvtHandler::LeaveRoom, // 방장아닐시 방 나가기
RoomEvtHandler::SendUpdatedRoomForShopCode // 같은 매장내에 있는 머신에게 방 정보를 갱신시켜준다.
> >
>( CommonEvent::ID::C_Req_Disconnect, _getEventTable() ); // 접속 종료 요청
TRUE 경우 아무것도 처리하고 싶지 않을때는 NONE 함수를 등록해 주면 됩니다.
VMEventRegistration
<
// 방장일 경우(TRUE)
FCT::NONE, // 아무것도 처리하지 않는다.
// 이것은 조건문이 됩니다.
VMAnd_
<
RoomEvtHandler::CheckRoomChief, // 방장 인가?
RoomEvtHandler::IsGamePlay // 플레이 중인가?
>,
// 방장이 아닐경우( FALSE )
VMExecSeq_< mpl::vector
<
RoomEvtHandler::LeaveRoom, // 방장아닐시 방 나가기
RoomEvtHandler::SendUpdatedRoomForShopCode // 같은 매장내에 있는 머신에게 방 정보를 갱신시켜준다.
> >
>( CommonEvent::ID::C_Req_Disconnect, _getEventTable() ); // 접속 종료 요청
조건문의 조금더 복잡한 조건을 사용하고 싶을 때는 위와 같이 VMAnd_ 를 또는 VMOr_ 를 사용해 가능합니다.
서버 & 클라이언트 모델 뿐 만 아니라 실질적으로 시스템과 시스템, 스레드와 스레드, 컨텐츠와 컨텐츠 간의 통신 또는 이벤트를
처리 할 경우에도 매우 유용하게 사용할 수 있습니다.
일부 코드는 프로젝트에 맡게 수정하셔야 합니다 소스코드 첨부합니다.
Boost Asio 를 이용한 Async Http Client (0) | 2016.02.01 |
---|---|
Enum 형을 String 으로 변환하기 위한 꼼수 (0) | 2016.02.01 |
C++ 용 Database Active Record (0) | 2016.01.31 |
자주 헷갈리는 const 연산자 사용 방법 (0) | 2014.04.22 |