보통 통신을 할때에 메시지ID를 사용합니다.
보내는쪽과 받는쪽이 이 메시지ID로 구분하여 어떤것때문에 보냈는지를 알 수 있습니다.
{
enum Type
{
Test1,
Test2,
Test3,
...
};
}
그런데 문득 이런생각이 들었습니다.
'메시지ID가 보내고 받는 데이터의 타입도 같이 가지고 있으면 좋겠다..'
그래서 만들어보았습니다.
{
Message<unsigned int> Test1;
Message<unsigned int, const Vector3&> Test2;
Message<int, const std::vector<int>&> Test3;
...
};
extern const MessageTable MessageType;
(네.. 저는 템플릿매니아..)
대충 이런식으로 선언을 하고 MessageType.Test1 이 메시지의 객체가 됩니다.
'멤버변수의 초기화는 선언한 순서대로 초기화가된다' 라는 특성을 이용하여
MessageType.Test1.GetMessageID() 는 0
MessageType.Test2.GetMessageID() 는 1
MessageType.Test3.GetMessageID() 는 2
가 되도록 Message들의 가상 상위 클래스인 MessageBase에 static 변수를 하나 만들고 카운팅을 해주었습니다.
class MessageBase
{
static unsigned short s_count;
...
};
// MessageBase.cpp
unsigned short MessageBase::s_count = 0;
MessageBase::MessageBase()
: m_messageID( s_count++ )
{
}
그러면 타입은 어떻게 가지고 있는걸까요?
class Message : public MessageBase
{
public:
typedef D1 DataType1;
typedef D2 DataType2;
typedef typename type_traits::remove_cv<typename type_traits::remove_reference<D1>::type>::type DataBaseType1;
typedef typename type_traits::remove_cv<typename type_traits::remove_reference<D2>::type>::type DataBaseType2;
...
};
이쯤 보셨으면 도대체 이걸 어따가 쓰지? 하는 생각이 드실지도 모르겠는데요.. 저는 이런식으로 사용합니다.
void SendMessage( const T& messageType, typename T::DataType1 d1 )
{
...
}
SendMessage( MessageType.Test1, 100 );
이렇게 호출하면 SendMessage 함수 안에서 컴파일타임에 메시지에서 사용하는 타입인지 체크가 가능합니다!
그런데 이런걸 만들면 도대체 뭐가 좋은거지?! enum은 메모리도 안먹는데!!
이건 메시지타입 하나당 메모리도 메시지ID의 타입만큼(예제에선 unsigned short)씩 먹자나!!
라고 하실 분들이 많으실텐데.. 전 편하게 쓰고 있습니다.....
편한것들 중 한가지를 말해보자면..
메시지ID가 데이터타입을 알고 있기 때문에 버퍼에 데이터를 넣거나 빼는것도 제공할 수 있습니다.
void MessageBase::PushMessageID( std::streambuf& buffer, const MessageBase& messageType )
{
boost::archive::binary_oarchive oa( buffer, boost::archive::no_header );
oa << messageType.m_messageID;
}
void MessageBase::PopMessageID( std::streambuf& buffer, unsigned short& messageID )
{
boost::archive::binary_iarchive ia( buffer, boost::archive::no_header );
ia >> messageID;
}
// Message<D1, D2>
template<typename D1=void, typename D2=void>
class Message : public MessageBase
{
...
void Push( std::streambuf& buffer, DataType1 d1, DataType2 d2 ) const
{
boost::archive::binary_oarchive oa( buffer, boost::archive::no_header );
oa << d1;
oa << d2;
}
void Pop( std::streambuf& buffer, DataBaseType1& d1, DataBaseType2& d2 ) const
{
boost::archive::binary_iarchive ia( buffer, boost::archive::no_header );
ia >> d1;
ia >> d2;
}
};
SendMessage에서는 이렇게..
void SendMessage( const T& messageType, typename T::DataType1 d1, typename T::DataType2 d2 )
{
if( m_pServerSession != NULL )
{
MessageBuffer buffer( 512 );
MessageBase::PushMessageID( buffer, messageType );
messageType.Push( buffer, d1, d2 );
m_pServerSession->Send( buffer );
}
}
받는 부분에선..
MessageBase::PopMessageID( messageBuffer, messageID );
...
int data1;
std::vector<int> data2;
MessageType.Test3.Pop( messageBuffer, data1, data2 );
이런식으로 사용하고 있습니다..
템플릿에 익숙하면 여기저기에서 유용하게 쓰일 수 있지만
템플릿을 안쓰는 분들이면 쓸곳이 없긴 하겠네요..
저는 템플릿 매니아..
'프로그래밍' 카테고리의 다른 글
PC에서 3D 입체 영상 게임 개발하기 #3 (3) | 2012.06.21 |
---|---|
유니티엔진의 coroutine & yield 1편. FSM의 습격 (5) | 2012.06.20 |
std::function이 좋네. (12) | 2012.06.17 |
유니티는 C#으로 작성했다? (9) | 2012.06.17 |
Unity, 널 사랑하지만 너의 UV만은 용납할 수 없다. 철썩~ (21) | 2012.06.10 |