꽃미남 프로그래머 김포프가 창립한 탑 프로그래머 양성 교육 기관 POCU 아카데미 오픈!
절찬리에 수강생 모집 중!
프로그래밍 언어 입문서가 아닌 프로그래밍 기초 개념 입문서
문과생, 비전공자를 위한 프로그래밍 입문책입니다.
jobGuid 꽃미남 프로그래머 "Pope Kim"님의 이론이나 수학에 치우치지 않고 실무에 곧바로 쓸 수 있는 실용적인 셰이더 프로그래밍 입문서 #겁나친절 jobGuid "1판의내용"에 "새로바뀐북미게임업계분위기"와 "비자관련정보", "1판을 기반으로 북미취업에 성공하신 분들의 생생한 경험담"을 담았습니다.
Posted by 알 수 없는 사용자

보통 통신을 할때에 메시지ID를 사용합니다.

보내는쪽과 받는쪽이 이 메시지ID로 구분하여 어떤것때문에 보냈는지를 알 수 있습니다.


namespace eMessage
{
enum Type
{
Test1,
Test2,
Test3,
...
};
}


그런데 문득 이런생각이 들었습니다. 

'메시지ID가 보내고 받는 데이터의 타입도 같이 가지고 있으면 좋겠다..'

그래서 만들어보았습니다.


struct MessageTable
{
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 변수를 하나 만들고 카운팅을 해주었습니다.


// MessageBase.h
class MessageBase
{
static unsigned short s_count;
...
};


// MessageBase.cpp
unsigned short MessageBase::s_count = 0;

MessageBase::MessageBase()
: m_messageID( s_count++ )
{
}


그러면 타입은 어떻게 가지고 있는걸까요?


template<typename D1=void, typename D2=void>
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;
...
};


이쯤 보셨으면 도대체 이걸 어따가 쓰지? 하는 생각이 드실지도 모르겠는데요.. 저는 이런식으로 사용합니다.


template<typename T>
void SendMessage( const T& messageType, typename T::DataType1 d1 )
{
...
}


SendMessage( MessageType.Test1, 100 );

이렇게 호출하면 SendMessage 함수 안에서 컴파일타임에 메시지에서 사용하는 타입인지 체크가 가능합니다!


그런데 이런걸 만들면 도대체 뭐가 좋은거지?! enum은 메모리도 안먹는데!!

이건 메시지타입 하나당 메모리도 메시지ID의 타입만큼(예제에선 unsigned short)씩 먹자나!!

라고 하실 분들이 많으실텐데.. 전 편하게 쓰고 있습니다.....


편한것들 중 한가지를 말해보자면..

메시지ID가 데이터타입을 알고 있기 때문에 버퍼에 데이터를 넣거나 빼는것도 제공할 수 있습니다.


// MessageBase.cpp
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에서는 이렇게..


template<typename T>
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 );
}
}


받는 부분에선..


unsigned short messageID;
MessageBase::PopMessageID( messageBuffer, messageID );

...

int data1;
std::vector<int> data2;
MessageType.Test3.Pop( messageBuffer, data1, data2 );


이런식으로 사용하고 있습니다..

템플릿에 익숙하면 여기저기에서  유용하게 쓰일 수 있지만

템플릿을 안쓰는 분들이면 쓸곳이 없긴 하겠네요..

저는 템플릿 매니아..



반응형
,