The traditional approach is to determine the structure of C ++ messages for each protocol message and implement serialization and deserialization functions. For example, an entry request may be presented as follows:
#include <string> #include <stdint.h> struct LoginRequest { int32_t protocol_version; std::string username; std::string password; int64_t map_seed; int8_t dimension; };
Now you need serialization functions. First, he needs serialization functions for integers and strings, as these are member types in LoginRequest .
Integer serialization functions must do the conversion in and out of the big-endian view. Since message members are copied to and from the buffer, byte ordering can be done when copying:
#include <boost/detail/endian.hpp> #include <algorithm> #ifdef BOOST_LITTLE_ENDIAN inline void xcopy(void* dst, void const* src, size_t n) { char const* csrc = static_cast<char const*>(src); std::reverse_copy(csrc, csrc + n, static_cast<char*>(dst)); } #elif defined(BOOST_BIG_ENDIAN) inline void xcopy(void* dst, void const* src, size_t n) { char const* csrc = static_cast<char const*>(src); std::copy(csrc, csrc + n, static_cast<char*>(dst)); } #endif // serialize an integer in big-endian format // returns one past the last written byte, or >buf_end if would overflow template<class T> typename boost::enable_if<boost::is_integral<T>, char*>::type serialize(T val, char* buf_beg, char* buf_end) { char* p = buf_beg + sizeof(T); if(p <= buf_end) xcopy(buf_beg, &val, sizeof(T)); return p; } // deserialize an integer from big-endian format // returns one past the last written byte, or >buf_end if would underflow (incomplete message) template<class T> typename boost::enable_if<boost::is_integral<T>, char const*>::type deserialize(T& val, char const* buf_beg, char const* buf_end) { char const* p = buf_beg + sizeof(T); if(p <= buf_end) xcopy(&val, buf_beg, sizeof(T)); return p; }
And for strings (handling modified UTF-8 is the same as asciiz strings ):
// serialize a UTF-8 string // returns one past the last written byte, or >buf_end if would overflow char* serialize(std::string const& val, char* buf_beg, char* buf_end) { int16_t len = val.size(); buf_beg = serialize(len, buf_beg, buf_end); char* p = buf_beg + len; if(p <= buf_end) memcpy(buf_beg, val.data(), len); return p; } // deserialize a UTF-8 string // returns one past the last written byte, or >buf_end if would underflow (incomplete message) char const* deserialize(std::string& val, char const* buf_beg, char const* buf_end) { int16_t len; buf_beg = deserialize(len, buf_beg, buf_end); if(buf_beg > buf_end) return buf_beg; // incomplete message char const* p = buf_beg + len; if(p <= buf_end) val.assign(buf_beg, p); return p; }
And a few helper functors:
struct Serializer { template<class T> char* operator()(T const& val, char* buf_beg, char* buf_end) { return serialize(val, buf_beg, buf_end); } }; struct Deserializer { template<class T> char const* operator()(T& val, char const* buf_beg, char const* buf_end) { return deserialize(val, buf_beg, buf_end); } };
Now, using these primitive functions, we can easily serialize and deserialize the LoginRequest message:
template<class Iterator, class Functor> Iterator do_io(LoginRequest& msg, Iterator buf_beg, Iterator buf_end, Functor f) { buf_beg = f(msg.protocol_version, buf_beg, buf_end); buf_beg = f(msg.username, buf_beg, buf_end); buf_beg = f(msg.password, buf_beg, buf_end); buf_beg = f(msg.map_seed, buf_beg, buf_end); buf_beg = f(msg.dimension, buf_beg, buf_end); return buf_beg; } char* serialize(LoginRequest const& msg, char* buf_beg, char* buf_end) { return do_io(const_cast<LoginRequest&>(msg), buf_beg, buf_end, Serializer()); } char const* deserialize(LoginRequest& msg, char const* buf_beg, char const* buf_end) { return do_io(msg, buf_beg, buf_end, Deserializer()); }
Using the helper functors above and presenting I / O buffers as char ranges of iterators requires only one function template to serialize and deserialize the message.
And all together, use:
int main() { char buf[0x100]; char* buf_beg = buf; char* buf_end = buf + sizeof buf; LoginRequest msg; char* msg_end_1 = serialize(msg, buf, buf_end); if(msg_end_1 > buf_end) ;