#include "stdafx.h"
#include <string>
#include <map>
// boost mpl 라이브러리
#include <boost/mpl/assert.hpp>
#include <boost/mpl/pair.hpp>
#include <boost/mpl/map.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/at.hpp>
// boost function 라이브러리
#include <boost/function.hpp>
// boost bind 라이브러리
#include <boost/bind.hpp>
namespace mpl = boost::mpl;
// 패킷 타입 정의
enum PacketType : int
{
Login,
Logout,
SendData,
};
// 패킷 구조체 정의
struct STLogin
{
std:: string strID;
std:: string strPW;
STLogin() : strID( "test"), strPW("Password" ) {}
};
struct STLogout
{
std:: string strDesc;
STLogout() : strDesc( "Logout") {}
};
struct STData
{
int a;
int b;
STData() : a(0), b(1) {}
};
// 패킷 구조체
struct CPacket
{
};
// mpl map pair 타입
template< int N, class T >
struct Entry
{
typedef mpl::pair < mpl::int_< N >, T > type ;
};
// 패킷 타입과 패킷 구조체 pair 로 묶여 있는 mpl map
typedef mpl::map
<
Entry< Login , STLogin >:: type,
Entry< Logout , STLogout >:: type,
Entry< SendData , STData >:: type
>
PacketStructMap;
// 패킷 구조체 맵에서 구조체 타입 가져오기
template< class T, int N >
struct GetPacketStructType : mpl::at< T, mpl::int_ < N > > {};
class CPacketDispatcher
{
public:
struct IHandler
{
virtual bool callFunc(CPacket* const _pPacket) = 0;
};
template< class T >
class CPacketHandler : public IHandler
{
private:
typedef boost::function < bool( typename T&, CPacket * const) > DeserializeFP;
typedef boost::function < bool(typename T&) > HandlerFP;
DeserializeFP m_fpDeserialize;
HandlerFP m_fpHandler;
CPacketHandler();
public:
CPacketHandler( DeserializeFP _fpDeserialize , HandlerFP _fpHandler)
: m_fpDeserialize( _fpDeserialize)
, m_fpHandler( _fpHandler)
{}
bool callFunc(CPacket * const _pPacket)
{
T packetStruct;
if (m_fpDeserialize(packetStruct, _pPacket ))
{
return m_fpHandler(packetStruct);
}
return false ;
}
};
private:
// 패킷 핸들러들을 담아 놓을 맵
// boost::unordered_map 을 활용해도 좋다.
typedef std::map < int, IHandler* > PacketHandlerMap;
PacketHandlerMap m_mapPacketHandler;
public:
// 역 직렬화
template< int N, class T >
bool deserialize(T & _arg, CPacket* const _pPacket )
{
// 여기서 아래와 같이 패킷을 역직렬화를 통해 구조체를 뽑아낸다.
// return _pPacket->deserialize(_arg);
return true ;
}
// 패킷 핸들러 Bind
template< int N >
void bindPacketHandler(boost::function < bool(typename GetPacketStructType<PacketStructMap , N>::type&) > _UserHandler )
{
// 패킷 구조체 타입
typedef GetPacketStructType <PacketStructMap, N>::type STType;
// 유저 정의 핸들러 타입
typedef boost::function < bool( typename STType& ) > UserHandlerType;
// 역직렬화 함수를 패킷 타입에 따라
typedef boost::function < bool(typename STType&, CPacket * const) > DeserializeHandlerType;
// 역직렬화 함수
DeserializeHandlerType deserializeHandler = boost::bind(&CPacketDispatcher ::deserialize< N, STType >, this, _1, _2);
// 유저 핸들러
UserHandlerType userHandler = _UserHandler;
// 패킷 핸들러 생성
CPacketHandler< STType >* pPacketHandler = new CPacketHandler< STType >(deserializeHandler, userHandler);
// 맵에 넣는다
m_mapPacketHandler.insert( PacketHandlerMap::value_type (N, pPacketHandler));
}
// 패킷 처리
bool dispatchPacket(PacketType _ePacketType, CPacket* const _pPacket)
{
PacketHandlerMap::iterator itrF = m_mapPacketHandler.find(_ePacketType);
if (itrF == m_mapPacketHandler.end()) return false;
// 타입에 맞는 핸들러 실행
return itrF->second->callFunc(_pPacket );
}
};
// 사용자 정의 패킷 핸들러
struct user_packet_handler
{
bool login(const STLogin& _Login)
{
// 로그인 처리
return true ;
}
bool logout(const STLogout& _Logout)
{
// 로그 아웃 처리
return true ;
}
bool recvData(const STData& _Data)
{
// 데이터 처리
return true ;
}
};
int _tmain (int argc, _TCHAR* argv[])
{
// 패킷 처리기
CPacketDispatcher PacketDispatcher;
// 사용자 정의 핸들러
user_packet_handler UserHandler;
// 로그인 핸들러 등록
PacketDispatcher.bindPacketHandler< Login>(boost::bind(&user_packet_handler ::login, &UserHandler, _1));
// 로그아웃 핸들러 등록
PacketDispatcher.bindPacketHandler< Logout>(boost::bind(&user_packet_handler ::logout, &UserHandler, _1));
// 데이터 핸들러 등록
PacketDispatcher.bindPacketHandler< SendData>(boost::bind(&user_packet_handler ::recvData, &UserHandler, _1));
// 잘못 등록했을 경우 컴파일 에러를 내뱉음
// PacketDispatcher.bindPacketHandler<Login>(boost::bind(&user_packet_handler::recvData, &UserHandler, _1));
//
// 패킷을 받았을 경우
CPacket packet;
// 로그인 처리
PacketDispatcher.dispatchPacket( Login, &packet);
// 로그아웃 처리
PacketDispatcher.dispatchPacket( Logout, &packet);
// 데이터 처리
PacketDispatcher.dispatchPacket( SendData, &packet);
getchar();
return 0;
}
패킷 타입을 정의함과 동시에 패킷 타입이 패킷 구조체를 갖게 된다.
위의 예제에서는 수동으로 mpl::map 을 이용해서 타입과 구조체를 등록하게 되어 있는데 이부분은
PacketType을 헤더로 분리하고 간단한 파일 파싱을 통해 자동으로 구조체를 등록하게 하는 방법으로 할것인지
define을 이용해서 간단하게 처리할것인지 아직 고민중이다.
여기에는 없는 패킷의 직렬화/역직렬화 인데, 이전에 구현해뒀던 패킷 직렬화와 합쳐지면
패킷을 보내고 받는 절차가 지금 작업했던 방식에 비해 많이 줄어든다.