gpt4 book ai didi

c++ - 将类序列化为一个定义的对象

转载 作者:太空宇宙 更新时间:2023-11-04 11:34:08 24 4
gpt4 key购买 nike

我想设计一个应用程序协议(protocol),我在其中定义了一个名为 message 的类,它具有 4 个字节的固定 header 大小和可变负载部分。要将负载分配给消息,我想按以下方式执行:

 message msg;
IPAddressv4 ipv4("127.0.0.1");
IPAddressv6 ipv6("::1");
Algorithm alg(2);

msg.version(1);
msg.ack(1);

msg << ipv4 & ipv6 & alg;

其中 IPAddressv4、IPAddressv6 和 Algorithm 是具有基本数据类型的定义对象。例如 ipv4 是 uint32,ipv6 是 2*unit64,算法是 uint8。

这样最终的消息大小为:固定 header 大小“4”+ 4 + 16 + 1 = 25 字节

那么谁能告诉我如何在 C++ 中实现它的一般方法,或者无论如何在 boost 库中都可以做到这一点?

最佳答案

因此,我认为您需要一个行为类似于流但具有二进制格式的类。

这是我为此类(简化)的简单界面:

struct Message
{
// serialize all of the inserted buffer content to the given stream
friend std::ostream& operator <<(std::ostream& os, Message const& msg);

// read all of the given stream into the message buffer for extraction
friend std::istream& operator >>(std::istream& is, Message const& msg);

// insert integral data into the buffer, with fixed endianness
template <typename Int>
friend Message& operator <<(Message& msg, Int v);

template <typename Int, size_t N>
friend Message& operator <<(Message& msg, Int const (&v)[N]);

// extract integral data from the buffer, with fixed endianness
template <typename Int>
friend Message& operator >>(Message& msg, Int& v);

template <typename Int, size_t N>
friend Message& operator >>(Message& msg, Int (&v)[N]);

// default construction
Message() : buffer { "\xee\x33" }
{
buffer.unsetf(std::ios::skipws);
}

private:
std::stringstream mutable buffer;
};

我省略了处理 SFINAE/元编程的神秘细节。构造函数快速而肮脏,并展示了一种拥有固定消息 header (在本例中为 ee33)的简单方法。

现在你可以像这样使用它:

std::string serialize_message()
{
uint8_t ipv4[4] = { 127, 0, 0, 1 };
uint64_t ipv6[2] = { 0xdeadbeef00004b1d, 0xcafed00dcafebabe };
uint8_t alg = 2;

Message msg;
msg << ipv4 << ipv6 << alg;

// stringyfy
std::stringstream ss;
ss << msg;
return ss.str();
}

int main()
{
std::string const rep = serialize_message();
std::cout << rep << std::flush; // for demo (view in hex)

// reconstruct
Message roundtrip;
std::istringstream(rep) >> roundtrip;

uint8_t ipv4[4] = { 0 };
uint64_t ipv6[2] = { 0 };
uint8_t alg = 0;
roundtrip >> ipv4 >> ipv6 >> alg;

assert(rep == boost::lexical_cast<std::string>(roundtrip));
}

请注意 lexical_cast仅使用上面列出的流插入运算符。

我使用 Boost Spirit 的二进制文件 parsers 实现了这个和 generators .

输入类型到解析器/生成器类型的映射在详细命名空间中。

查看 Live On Coliru 。输出:

0000000: ee33 7f00 0001 dead beef 0000 4b1d cafe  .3..........K...
0000010: d00d cafe babe 02 .......

完整代码

#define LIVE_DANGEROUSLY
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/integer.hpp>
#include <sstream>

namespace detail
{
using namespace boost::spirit;
// meta programming helpers
template <int bits, typename endianness = struct big> struct int_traits;

template <typename endianness> struct int_traits<8, endianness/*ignored*/> {
typedef uint8_t type;
typedef qi::byte_type parser;
typedef karma::byte_type generator;
};

template <> struct int_traits<16, struct big> {
typedef uint16_t type;
typedef qi::big_word_type parser;
typedef karma::big_word_type generator;
};

template <> struct int_traits<32, struct big> {
typedef uint32_t type;
typedef qi::big_dword_type parser;
typedef karma::big_dword_type generator;
};

template <> struct int_traits<64, struct big> {
typedef uint64_t type;
typedef qi::big_qword_type parser;
typedef karma::big_qword_type generator;
};

template <> struct int_traits<16, struct little> {
typedef uint16_t type;
typedef qi::little_word_type parser;
typedef karma::little_word_type generator;
};

template <> struct int_traits<32, struct little> {
typedef uint32_t type;
typedef qi::little_dword_type parser;
typedef karma::little_dword_type generator;
};

template <> struct int_traits<64, struct little> {
typedef uint64_t type;
typedef qi::little_qword_type parser;
typedef karma::little_qword_type generator;
};
}

struct Message
{
friend std::ostream& operator <<(std::ostream& os, Message const& msg) {
msg.buffer.seekg(0, std::ios_base::beg);
return os << msg.buffer.rdbuf();
}

friend std::istream& operator >>(std::istream& is, Message const& msg) {
msg.buffer.clear(); // this will overwrite our header
msg.buffer.str("");
return is >> msg.buffer.rdbuf();
}

template <typename Int>
friend typename boost::enable_if<typename boost::is_integral<Int>::type, Message&>::type
operator <<(Message& msg, Int v)
{
msg.buffer.seekp(0, std::ios_base::end);
msg.append_int(v);
return msg;
}

template <typename Int, size_t N>
friend typename boost::enable_if<typename boost::is_integral<Int>::type, Message&>::type
operator <<(Message& msg, Int const (&v)[N])
{
msg.buffer.seekp(0, std::ios_base::end);
msg.append_int(v);
return msg;
}

template <typename Int>
friend typename boost::enable_if<typename boost::is_integral<Int>::type, Message&>::type
operator >>(Message& msg, Int& v)
{
msg.extract_int(v);
return msg;
}

template <typename Int, size_t N>
friend typename boost::enable_if<typename boost::is_integral<Int>::type, Message&>::type
operator >>(Message& msg, Int (&v)[N])
{
msg.extract_ints(v);
return msg;
}

Message() : buffer { "\xee\x33" }
{
buffer.unsetf(std::ios::skipws);
}

private:
std::stringstream mutable buffer;

template <typename Int, int bits=std::numeric_limits<Int>::digits + std::numeric_limits<Int>::is_signed>
void append_int(Int v)
{
namespace karma = boost::spirit::karma;
static const typename detail::int_traits<bits>::generator gen {};
karma::generate(std::ostreambuf_iterator<char>(buffer), gen, static_cast<typename detail::int_traits<bits>::type>(v));
}

template <typename Int, int bits=std::numeric_limits<Int>::digits + std::numeric_limits<Int>::is_signed>
void extract_int(Int& v)
{
namespace qi = boost::spirit::qi;
static const typename detail::int_traits<bits>::parser p {};
boost::spirit::istream_iterator f(buffer), l;
qi::parse(f, l, p, v);
}

template <typename Int, size_t N, int bits=std::numeric_limits<Int>::digits + std::numeric_limits<Int>::is_signed>
void append_int(Int const(&v)[N])
{
#ifdef LIVE_DANGEROUSLY // optimize, but implementation defined cast
namespace karma = boost::spirit::karma;
static const typename detail::int_traits<bits>::generator gen {};
auto data = reinterpret_cast<typename detail::int_traits<bits>::type const*>(&v[0]);
karma::generate(std::ostreambuf_iterator<char>(buffer), +gen, boost::make_iterator_range(data,data+N));
#else
for(auto& i:v)
append_int(i);
#endif
}

template <typename Int, size_t N, int bits=std::numeric_limits<Int>::digits + std::numeric_limits<Int>::is_signed>
void extract_ints(Int (&v)[N])
{
for(auto& i:v)
extract_int(i);
}
};

std::string serialize_message()
{
uint8_t ipv4[4] = { 127, 0, 0, 1 };
uint64_t ipv6[2] = { 0xdeadbeef00004b1d, 0xcafed00dcafebabe };
uint8_t alg = 2;

Message msg;
msg << ipv4 << ipv6 << alg;

// stringyfy
std::stringstream ss;
ss << msg;
return ss.str();
}

int main()
{
std::string const rep = serialize_message();
std::cout << rep << std::flush; // for demo (view in hex)

// reconstruct
Message roundtrip;
std::istringstream(rep) >> roundtrip;

uint8_t ipv4[4] = { 0 };
uint64_t ipv6[2] = { 0 };
uint8_t alg = 0;
roundtrip >> ipv4 >> ipv6 >> alg;

assert(rep == boost::lexical_cast<std::string>(roundtrip));
}

关于c++ - 将类序列化为一个定义的对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23460550/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com