用于序列化/反序列化的标准C++代码

Pap*_*ter 10 c++ serialization buffer allocator

我已经使用硬件API很长一段时间了,几乎所有我工作过的API都有一个C接口.因此,在很多时候我使用裸news,不安全的缓冲和许多用C++代码包装的C函数.最后,C纯代码和C++纯代码之间的边界在我的脑海中搞砸了(我不知道澄清这个前沿是否有用).

现在,由于一些新的编码风格要求,我需要将所有怀疑不安全的代码重构为用C++编写的更安全的代码(假设C++代码更安全),最终目标是使用C++带来的工具.

所以,为了摆脱我的困惑,我正在寻求关于C/C++的几个主题的帮助.

memcpy VS std::copy

AFAIK memcpy是一个位于C库中的函数,因此它不是C++ ish; 另一方面std::copy是STL中的一个函数,所以它是纯C++.

  • 但是,这是真的吗?毕竟,如果数据是平凡可复制的,std::copy则会调用std::memcpy(进入cstring标题).
  • 将所有memcpy调用重构为std::copy调用将使代码更"纯C++"?

为了处理新的代码样式要求,我决定继续使用memcpy重构,有一些关于memcpy和的问题std::copy:

memcpy类型不安全,因为它适用于原始void指针,可以管理任何类型的指针,无论它的类型如何,但同时非常灵活,std::copy缺乏这种灵活性,确保类型安全.乍一看,memcpy是使用序列化和反序列化例程的最佳选择(这是我确实使用的真实情况),例如,通过自定义串行端口库发送一些值:

void send(const std::string &value)
{
    const std::string::size_type Size(value.size());
    const std::string::size_type TotalSize(sizeof(Size) + value.size());
    unsigned char *Buffer = new unsigned char[TotalSize];
    unsigned char *Current = Buffer;

    memcpy(Current, &Size, sizeof(Size));
    Current += sizeof(Size);

    memcpy(Current, value.c_str(), Size);

    sendBuffer(Buffer, TotalSize);

    delete []Buffer;
}
Run Code Online (Sandbox Code Playgroud)

上面的代码运行正常,但看起来很糟糕; 我们正在std::string通过该std::string::c_str()方法摆脱封装访问它的内部存储器,我们需要处理动态内存的分配和解除分配,使用指针并将所有值视为无符号字符(参见下一部分),问题是:有更好的方法吗?

我试图解决上述问题的第一次尝试std::copy并不能完全满足我:

void send(const std::string &value)
{
    const std::string::size_type Size(value.size());
    const std::string::size_type TotalSize(sizeof(Size) + value.size());

    std::vector<unsigned char> Buffer(TotalSize, 0);

    std::copy(&Size, &Size + 1, Buffer.begin());
    std::copy(value.begin(), value.end(), Buffer.begin() + sizeof(Size));

    sendBuffer(Buffer.data(), TotalSize);
}
Run Code Online (Sandbox Code Playgroud)

使用上面的方法,内存管理不再是一个问题,std::vector在范围的最后需要分配,存储和最终解除分配数据的责任,但是std::copy与指针算术和迭代器算术混合的调用是非常烦人的最后,我忽略std::vectorsendBuffer呼叫中的封装.

在之前的尝试之后,我用std::stringstreams 编写了一些东西,但结果更糟,现在,我想知道是否:

  • 有一种方法可以安全地序列化对象和值,而不会破坏封装,没有过分或混乱的指针/迭代器算术,没有动态内存管理,或者这只是一个不可能的目标?(是的,我听说过boost::serialization,但现在我不允许整合它).

和:

  • 什么是std::copy序列化/反序列化目的的最佳用途?(如果有的话).
  • std::copy复制容器或数组的理由是有限的,将它用于原始内存是一个糟糕的选择吗?

alloc/ freevs new/ deletevsstd::allocator

另一个重要的话题是内存分配.AFAIK的malloc/ free功能没有被禁止到C++范围,尽管它们从C是而且new/ delete运营商从C++范围和它们不是ANSI C.

  • 我是正确的?
  • new/ delete可以在ANSI C中使用吗?

假设我需要将所有C风格的代码重构为C++代码,我将摆脱所有alloc/ freespreaded arround一些遗留代码,我发现保留动态内存非常混乱,void类型不带任何代码有关大小的信息,因为使用void作为类型保留数据缓冲区是不可能的:

void *Buffer = new void[100]; // <-- How many bytes is each 'void'?
Run Code Online (Sandbox Code Playgroud)

由于缺少纯原始二进制数据指针,因此创建指针是一种常见做法unsigned char.在char以等于元件计数和大小.并且unsigned为了避免数据复制期间意外的签名无符号转换.也许这是一种常见的做法,但它是一团糟...... unsigned char不是int也不floatmy_awesome_serialization_struct我是否被迫选择某种虚拟指针来指向我想要的二进制数据void *而不是unsigned char *.

因此,当我需要一个动态缓冲区来进行序列化/反序列化时,我无法避免这些unsigned char *内容,以便重构为类型安全缓冲区管理; 但是当我愤怒地重构所有alloc/ freenew/ delete对时,我读到了关于std::allocator.

std::allocator允许保留的内存块的类型安全的方式,在第一次看到我敢打赌,这将是有用的,但有一个与分配之间没有太大的差别std::allocator<int>::allocate还是new int我这样想着,同样是为std::allocator<int>::deallocatedelete int.

现在,我已经失去了关于动态内存管理的北方,这就是为什么我要问:

  • 有一个很好的C++实践涉及用于序列化/反序列化目的的动态内存管理,以实现类型安全管理吗?
  • 是否可以避免使用const char *序列化/反序列化内存缓冲区?
  • 什么是std::allocator它的序列化/反序列化范围的基本原理和用途是什么?(如果有的话).

感谢您的关注!

bjh*_*end 2

我的经验是,C++ 中的类型安全不仅意味着编译器会抱怨类型不匹配。这意味着您通常不必关心数据的内存布局。事实上,C++标准对某些数据类型的内存布局只有很少的要求。

您的序列化基于直接内存访问,因此,恐怕不会有一个简单的“纯”C++ 解决方案,特别是没有通用的编译器/平台独立解决方案。