Буферы протокола Google и использование std :: string для произвольных двоичных данных - PullRequest
2 голосов
/ 21 февраля 2012

Вопрос по теме: vectorvs строка для двоичных данных .

Мой код использует 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 ]

4 голосов
/ 21 февраля 2012

Существует два способа избежать копирования, о которых я знаю и которые видел в использовании.

Традиционным способом действительно является передача указателя / ссылки на известную сущность. Хотя это работает нормально и с минимальной суетой, проблема в том, что он связывает вас с данным представлением, что в случае необходимости влечет за собой преобразования (как вы уже видели).

Другой способ, который я обнаружил с помощью LLVM:

Идея поразительно проста: оба держат T*, указывающий на начало массива T, и size_t, указывающий количество элементов.

Что волшебно, так это то, что они полностью скрывают фактическое хранилище, будь то string, vector, динамически или статически размещенный C-массив ... это не имеет значения. Представленный интерфейс является полностью унифицированным и не требует копирования.

Единственное предостережение в том, что они не овладевают памятью (Ref!), Поэтому могут появиться тонкие ошибки, если вы не позаботитесь о них. Тем не менее, это нормально, если вы используете их только во временных операциях (например, внутри функции) и не сохраняете их для дальнейшего использования.

Мне показалось, что они невероятно удобны при работе с буфером, особенно благодаря бесплатным операциям нарезки. С диапазонами гораздо проще работать, чем с парами итераторов.


Есть и третий способ, который я испытал, но до сих пор никогда не использовал в серьезном коде. Идея состоит в том, что vector<unsigned char> является представлением очень низкого уровня. Подняв уровень абстракции и используя, скажем, класс Buffer, вы можете полностью инкапсулировать точный способ хранения памяти, чтобы она не стала проблемой для вашего кода.

И затем, не стесняйтесь, выбирайте одно представление памяти, которое требует меньшего преобразования.

1 голос
/ 21 февраля 2012

Чтобы избежать этого кода (который вы представляете),

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

где предположительно objectData является std::string, рассмотрим

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

, а затем, например,

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