Google协议缓冲并使用std :: string作为任意二进制数据

时间:2012-02-21 06:45:41

标签: c++ stl binary-data

相关问题: vector <unsigned char> vs string for binary data

我的代码使用vector<unsigned char>来获取任意二进制数据。但是,我的很多代码都必须与Google的协议缓冲区代码连接。协议缓冲区使用std::string表示任意二进制数据。这使得很多丑陋的分配/复制/免费周期只是为了在Google协议缓冲区和我的代码之间移动数据。它也使很多情况需要两个构造函数(一个采用向量和一个字符串)或两个函数将函数转换为二进制有线格式。

代码在内部处理原始结构,因为结构是内容可寻址的(通过哈希存储和检索),签名等等。因此,这不仅仅是Google协议缓冲区的界面问题。对象也在代码的其他部分以原始形式处理。

我能做的一件事就是将我的所有代码切换为使用std::string来获取任意二进制数据。我能做的另一件事是尝试找出更有效的方法来存储和检索我的向量到Google协议缓冲区对象。我想我的另一个选择是为字符串创建标准的,简单但缓慢的转换函数并始终使用它们。这样可以避免繁琐的代码重复,但从性能角度来看会更糟糕。

有什么建议吗?我缺少哪些更好的选择?

这是我要避免的:

if(SomeCase)
{
    std::vector<unsigned char> rawObject(objectdata().size());
    memcpy(&rawObject.front(), objectdata().data(), objectdata().size());
    DoSometingWith(rawObject);
}

当原始数据已经存在时,分配,复制,处理,免费是完全没有意义的。

2 个答案:

答案 0 :(得分:4)

有两种方法可以避免我所知道并且已经使用过的复制。

传统的方法确实是将指针/引用传递给已知实体。虽然这样可以正常工作并且最小化,但问题在于它会将您与特定的表示联系起来,这需要在必要时进行转换(如您所见)。

我用LLVM发现的另一种方式:

这个想法非常简单:两个都指向一个T*数组的开头T和指示元素数量的size_t

什么是神奇的是它们完全隐藏了实际的存储空间,无论是stringvector,动态或静态分配的C阵列......都没关系。所提供的界面完全统一,不涉及任何副本。

唯一需要注意的是,他们取得内存的所有权(Ref!),因此如果你不小心,可能会出现微妙的错误。尽管如此,如果你只在瞬态操作中使用它们(例如在一个函数中)并且不存储它们以供以后使用,通常会很好。

我发现它们在缓冲区操作方面非常方便,特别是由于免费的切片操作。范围比迭代器对更容易操作。


我还有第三种方法,但直到现在才用于严肃的代码。我们的想法是vector<unsigned char>是一种非常低级的表示。通过提升抽象层并使用一个Buffer类,您可以完全封装内存存储的确切方式,以便在您的代码方面成为非问题。

然后,随意选择一个需要较少转换的内存表示。

答案 1 :(得分:1)

要避免使用此代码(您提供此代码),

if(SomeCase)
{
    std::vector<unsigned char> rawObject(objectdata().size());
    memcpy(&rawObject.front(), objectdata().data(), objectdata().size());
    DoSometingWith(rawObject);
}

大概objectDatastd::string,请考虑

typedef unsigned char      Byte;
typedef std::vector<Byte>  ByteVector;

然后例如

if( someCase )
{
    auto const& s = objectData;
    doSomethingWith( ByteVector( s.begin(), s.end() ) );
}