浅复制到协议缓冲区的字节字段中

时间:2017-04-28 20:59:03

标签: c++ protocol-buffers

假设我有一个带字节字段的proto:

#include <iostream>
#include <string>
using namespace std;

void readDate (char array[]);
void GetDate (char array[]);

const int size = 11;

int main ()
{
    char original[size];

    readDate(original); // call readDate
    GetDate(original); // call GetDate

    return 0;
}

void readDate (char array[])
{
    string month;
    cout << "Enter a date (Ex: Jan/20/2003): ";
    cin.getline (array, size); // read size characters into the character array
    cin >> month;
}

void getDate (char array[])
{   string month;
    string Jan, Feb, Mar, Apr, May, Jun, July, Aug, Sept, Oct, Nov, Dec;

    cout << "Date in converted format is: ";

    if (month == Jan) {
        cout << "01";
        }
    else if (month == Feb) {
        cout << "02";
        }
    else if (month == Mar) {
        cout << "03";
        }
    else if (month == Apr) {
        cout << "04";
    }
    else if (month == May) {
        cout << "05";
    }
    else if (month == Jun) {
        cout << "05";
    }
    else if (month == July) {
        cout << "07";
    }
    else if (month == Aug) {
        cout << "08";
    }
    else if (month == Sept) {
        cout << "09";
    }
    else if (month == Oct) {
        cout << "10";
    }
    else if (month == Nov) {
        cout << "11";
    }
    else if (month == Dec) {
        cout << "12";
    }

    cout << array[3] << array[4] << array[6] << array[7] << array[8] << array[9] << endl;
}

我无法控制的API为我提供了指向源数据及其大小的指针。我希望在没有深度复制的情况下从这些数据中创建一个message MyProto { optional bytes data = 1; } 我认为这很容易做到,但似乎不可能。 MyProto可以轻松进行深度复制。 Protobuf提供了一个set_data函数,但它需要一个指向set_allocated_data的指针,这对我没有帮助,因为(除非我弄错了)没有办法让std::string没有深深地抄袭它。

std::string

有没有办法正确填充这个原型(以便以后可以序列化)而无需将源数据深度复制到字节字段中?

我知道我可以立即手动进行序列化,但如果可能的话,我宁愿不这样做。

1 个答案:

答案 0 :(得分:5)

好问题。选项包括:

  1. 如果您可以更改.proto文件,请考虑为StringPiece实施ctype字段选项,Google等同于即将推出的C ++ 17 string_view。这就是谷歌在内部处理这种情况的方式。 FieldOptions消息已有semantics for StringPiece,但Google尚未开源实施。

    message MyProto {
        bytes data = 1 [ctype = STRING_PIECE];
    }
    

    有关实施指南,请参阅this discussion。您可以忽略竞技场分配的注释,这不适用于您的情况。值得向谷歌询问ETA。

  2. 使用不同的协议缓冲区实现,可能仅适用于此特定消息类型。 protobuf-cprotobluff是看起来很有前途的C语言实现。

  3. 将缓冲区提供给第三方API。我从评论中看出你不能,但我把它包括在内是为了完整。

    ::str::string* buf = myProto->mutable_data();
    buf->resize(size);
    api(buf->data(), size); // data is contiguous per c++11 std
    
  4. 非标准:通过覆盖字符串实例中的数据来中断封装。 C ++有一些粗糙的功能,可以让你有足够的绳索来挂起自己。此选项不安全,取决于您的std::string实施和其他因素。

    // NEVER USE THIS IN PRODUCTION
    void string_jam(::std::string * target, void * buffer, size_t len) {
      /* On my system, std::string layout
       *   0: size_t capacity
       *   8: size_t size
       *  16: char * data (iff strlen > 22 chars) */
      assert(target->size() > 22);
      size_t * size_ptr = (size_t*)target;
      size_ptr[0] = len; // Overwrite capacity
      size_ptr[1] = len; // Overwrite length
    
      char ** buf_ptr = (char**)(size_ptr + 2); 
      free(*buf_ptr); // Free the existing buffer
      *buf_ptr = (char*)buffer; // Jam in our new buffer
    }
    

    注意:这可能会让你被解雇。这对于测试来衡量性能影响是否有用,如果你确实进行了零复制路由,但是不要在prod中进行。

  5. 如果选择#1选项,那么如果您可以发布源代码就会很棒,因为许多其他人都会从这个功能中受益。祝你好运。