有没有可能在 C + + 中序列化和反序列化一个类?

有没有可能在 C + + 中序列化和反序列化一个类?

到现在为止,我已经使用 Java 三年了,序列化/反序列化在这种语言中是相当微不足道的。C + + 有类似的特性吗?是否有处理序列化的本机库?

举个例子会有帮助的。

174649 次浏览

Boost::serialization库可以非常优雅地处理这个问题。我在几个项目中使用过它。有一个示例程序,展示了如何使用它,给你

唯一原生的方法是使用流。这基本上就是 Boost::serialization库所做的全部工作,它通过建立一个框架来扩展 stream 方法,该框架将对象写入到一种类似文本的格式,并从相同的格式读取它们。

对于内置类型,或者定义了 operator<<operator>>的您自己的类型,这相当简单; 请参阅 C + + 常见问题解答了解更多信息。

推进是一个很好的建议。但是如果你想自己卷,这并不难。

基本上,您只需要一种方法来构建对象图,然后将它们输出到某种结构化存储格式(JSON、 XML、 YAML 等等)。构建图形非常简单,只需使用一个标记递归得体对象算法,然后输出所有标记的对象。

我写过一篇文章,描述了一个基本的(但仍然强大的)序列化系统。

就“内置”库而言,<<>>被专门保留用于序列化。

您应该覆盖 <<以将对象输出到某个序列化上下文(通常是 iostream) ,并覆盖 >>以从该上下文读取数据。每个对象负责输出其聚合的子对象。

只要对象图不包含循环,此方法就可以正常工作。

如果是这样,那么您将不得不使用一个库来处理这些循环。

我推荐 Google 协议缓冲。 我曾有机会在一个新项目上测试驱动库,它非常容易使用。 该库针对性能进行了大量优化。

Protobuf 不同于这里提到的其他序列化解决方案,因为它不序列化对象,而是根据规范为序列化对象生成代码。

我建议查看经常用作序列化基础的抽象工厂

我在另一个关于 C + + 工厂的 SO 问题中回答了这个问题。请参阅 那里,如果一个灵活的工厂是感兴趣的。我试图描述一种从 ET + + 到使用宏的老方法,这种方法对我来说非常有效。

ET + + 是一个将旧的 MacApp 移植到 C + + 和 X11的项目。在努力的过程中,EricGamma 等人开始思考 设计模式。ET + + 包含运行时序列化和自省的自动方法。

您可以检查 协议,在 amef 中 C + + 编码的一个例子是这样的,

    //Create a new AMEF object
AMEFObject *object = new AMEFObject();


//Add a child string object
object->addPacket("This is the Automated Message Exchange Format Object property!!","adasd");


//Add a child integer object
object->addPacket(21213);


//Add a child boolean object
object->addPacket(true);


AMEFObject *object2 = new AMEFObject();
string j = "This is the property of a nested Automated Message Exchange Format Object";
object2->addPacket(j);
object2->addPacket(134123);
object2->addPacket(false);


//Add a child character object
object2->addPacket('d');


//Add a child AMEF Object
object->addPacket(object2);


//Encode the AMEF obejct
string str = new AMEFEncoder()->encode(object,false);

用爪哇语解码就像,

    string arr = amef encoded byte array value;
AMEFDecoder decoder = new AMEFDecoder()
AMEFObject object1 = AMEFDecoder.decode(arr,true);

Protocol 的实现包含了 C + + 和 Java 的编解码器,有趣的是它可以保留对象类的名称值对表示形式, 我在上一个项目中需要一个类似的协议,当我偶然发现这个协议时,我实际上已经根据我的需求修改了基本库。希望这个能帮到你。

我建议使用其他海报所描述的提升序列化。这里有一个关于如何使用它的很好的详细教程,它很好地补充了升级教程: http://www.ocoudert.com/blog/2011/07/09/a-practical-guide-to-c-serialization/

序列化 是一个很好的选择,但是我遇到了一个新项目: 麦片,我发现它更加优雅!我强烈建议调查一下。

我知道这是一个旧的职位,但它是第一次出现时,搜索 c++ serialization

我鼓励任何有 C + + 11访问权限的人看一看 麦片,这是一个用于序列化的只有 C + + 11头文件的库,支持二进制、 JSON 和 XML 的开箱即用。燕麦片被设计成易于扩展和使用,并具有类似的语法来助推: : 序列化。

如果你想要简单和最好的性能,不关心向后的数据兼容性,试试 HPS,它是轻量级的,比 Boost 等更快,比 Protobuf 等更容易使用。

例如:

std::vector<int> data({22, 333, -4444});
std::string serialized = hps::serialize_to_string(data);
auto parsed = hps::parse_from_string<std::vector<int>>(serialized);

下面是一个简单的序列化程序库,它只有头文件,c11和 有序列化基本类型的示例。

Https://github.com/goblinhack/simple-c-plus-plus-serializer

#include "c_plus_plus_serializer.h"


class Custom {
public:
int a;
std::string b;
std::vector c;


friend std::ostream& operator<<(std::ostream &out,
Bits my)
{
out << bits(my.t.a) << bits(my.t.b) << bits(my.t.c);
return (out);
}


friend std::istream& operator>>(std::istream &in,
Bits my)
{
in >> bits(my.t.a) >> bits(my.t.b) >> bits(my.t.c);
return (in);
}


friend std::ostream& operator<<(std::ostream &out,
class Custom &my)
{
out << "a:" << my.a << " b:" << my.b;


out << " c:[" << my.c.size() << " elems]:";
for (auto v : my.c) {
out << v << " ";
}
out << std::endl;


return (out);
}
};


static void save_map_key_string_value_custom (const std::string filename)
{
std::cout << "save to " << filename << std::endl;
std::ofstream out(filename, std::ios::binary );


std::map< std::string, class Custom > m;


auto c1 = Custom();
c1.a = 1;
c1.b = "hello";
std::initializer_list L1 = {"vec-elem1", "vec-elem2"};
std::vector l1(L1);
c1.c = l1;


auto c2 = Custom();
c2.a = 2;
c2.b = "there";
std::initializer_list L2 = {"vec-elem3", "vec-elem4"};
std::vector l2(L2);
c2.c = l2;


m.insert(std::make_pair(std::string("key1"), c1));
m.insert(std::make_pair(std::string("key2"), c2));


out << bits(m);
}


static void load_map_key_string_value_custom (const std::string filename)
{
std::cout << "read from " << filename << std::endl;
std::ifstream in(filename);


std::map< std::string, class Custom > m;


in >> bits(m);
std::cout << std::endl;


std::cout << "m = " << m.size() << " list-elems { " << std::endl;
for (auto i : m) {
std::cout << "    [" << i.first << "] = " << i.second;
}
std::cout << "}" << std::endl;
}


void map_custom_class_example (void)
{
std::cout << "map key string, value class" << std::endl;
std::cout << "============================" << std::endl;
save_map_key_string_value_custom(std::string("map_of_custom_class.bin"));
load_map_key_string_value_custom(std::string("map_of_custom_class.bin"));
std::cout << std::endl;
}

产出:

map key string, value class
============================
save to map_of_custom_class.bin
read from map_of_custom_class.bin


m = 2 list-elems {
[key1] = a:1 b:hello c:[2 elems]:vec-elem1 vec-elem2
[key2] = a:2 b:there c:[2 elems]:vec-elem3 vec-elem4
}

我使用以下模板来实现序列化:

template <class T, class Mode = void> struct Serializer
{
template <class OutputCharIterator>
static void serializeImpl(const T &object, OutputCharIterator &&it)
{
object.template serializeThis<Mode>(it);
}


template <class InputCharIterator>
static T deserializeImpl(InputCharIterator &&it, InputCharIterator &&end)
{
return T::template deserializeFrom<Mode>(it, end);
}
};


template <class Mode = void, class T, class OutputCharIterator>
void serialize(const T &object, OutputCharIterator &&it)
{
Serializer<T, Mode>::serializeImpl(object, it);
}


template <class T, class Mode = void, class InputCharIterator>
T deserialize(InputCharIterator &&it, InputCharIterator &&end)
{
return Serializer<T, Mode>::deserializeImpl(it, end);
}


template <class Mode = void, class T, class InputCharIterator>
void deserialize(T &result, InputCharIterator &&it, InputCharIterator &&end)
{
result = Serializer<T, Mode>::deserializeImpl(it, end);
}

这里的 T是你想序列化的类型 Mode是一个用来区分不同类型序列化的虚拟类型,例如同一个整数可以被序列化为小端点、大端点、变量等等。

默认情况下,Serializer将任务委托给正在序列化的对象。对于内置类型,您应该为 Serializer制作一个模板专门化。

还提供了方便的函数模板。

例如,无符号整数的小端序列化:

struct LittleEndianMode
{
};


template <class T>
struct Serializer<
T, std::enable_if_t<std::is_unsigned<T>::value, LittleEndianMode>>
{
template <class InputCharIterator>
static T deserializeImpl(InputCharIterator &&it, InputCharIterator &&end)
{
T res = 0;


for (size_t i = 0; i < sizeof(T); i++)
{
if (it == end) break;
res |= static_cast<T>(*it) << (CHAR_BIT * i);
it++;
}


return res;
}


template <class OutputCharIterator>
static void serializeImpl(T number, OutputCharIterator &&it)
{
for (size_t i = 0; i < sizeof(T); i++)
{
*it = (number >> (CHAR_BIT * i)) & 0xFF;
it++;
}
}
};

然后连载:

std::vector<char> serialized;
uint32_t val = 42;
serialize<LittleEndianMode>(val, std::back_inserter(serialized));

反序列化:

uint32_t val;
deserialize(val, serialized.begin(), serialized.end());

由于抽象迭代器逻辑,它应该与任何迭代器(如流迭代器) ,指针等工作。

C + + 14(推荐 C + + 17)

  • 没有宏
  • 没有需要显式编写的代码

小样