Стандартный код C ++ для сериализации / десериализации

Я долгое время работал с аппаратными API, и почти все API, с которыми я работал, имели C-интерфейс. Итак, много раз я работал с голым news, небезопасная буферизация и многие функции C, обернутые кодом C ++. В конце концов, граница между чистым кодом C и чистым кодом C ++ была запутана в моем сознании (и я не знаю, полезно ли вообще разъяснять эту границу).

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

Итак, чтобы избавиться от всей моей путаницы, я прошу помощи по нескольким темам C / C ++.

memcpy против std::copy

насколько мне известно memcpy это функция, которая лежит в библиотеках C, так что это не C ++ ish; с другой стороны std::copy это функция в STL, так что это чистый C ++.

  • Но это правда? в конце концов, std::copy позвоню std::memcpycstring заголовок), если данные тривиально копируемы.
  • Рефакторинг всех 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;
}

Код выше работает отлично, но выглядит ужасно; мы избавляемся от 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);
}

При вышеуказанном подходе управление памятью больше не является проблемой, std::vector берет на себя ответственность за распределение, хранение и, наконец, освобождение данных в конце области, но смешивание вызовов std::copy с арифметикой указателей и итераторами арифметика довольно раздражает и, в конце концов, я игнорирую std::vector инкапсуляция в sendBuffer позвони в конце концов.

После предыдущих попыток я что-то кодировал с std::stringstreamНо результаты были еще хуже, и теперь мне интересно, если:

  • Существует ли способ сериализации объектов и значений безопасным способом, без нарушения инкапсуляции, без чрезмерной или запутанной арифметики указателей / итераторов и без динамического управления памятью, или это просто невозможная цель? (да, я слышал о boost::serialization, но пока я не могу интегрировать его).

А также:

  • Как лучше всего использовать std::copy для сериализации / десериализации? (если есть).
  • std::copy Объяснение ограничено для копирования контейнеров или массивов, и использование его для сырой памяти является плохим выбором?

alloc/free против new/delete против std::allocator

Другая большая тема — распределение памяти. AFAIK malloc/free функции не запрещены в области видимости C ++, хотя они из C. И new/delete операторы из области C ++, и они не ANSI C.

  • Я прав?
  • new/delete можно использовать в ANSI C?

Предполагая, что мне нужно реорганизовать весь код на основе C в код C ++, я избавляюсь от всех alloc/free Мы распространили несколько устаревшего кода, и я обнаружил, что резервирование динамической памяти довольно запутанно, тип void не несет никакой информации о размере, из-за чего невозможно зарезервировать буфер данных, используя void в качестве типа:

void *Buffer = new void[100]; // <-- How many bytes is each 'void'?

Из-за отсутствия указателей на чистые необработанные двоичные данные является обычной практикой создания указателей на unsigned char, char чтобы равняться количеству и размеру элементов. И unsigned во избежание неожиданных подписанных беззнаковых преобразований во время копирования данных. Может быть, это обычная практика, но это беспорядок … unsigned char не int ни float ни my_awesome_serialization_struct если я вынужден выбрать какой-то фиктивный указатель на двоичные данные, я предпочту void * вместо unsigned char *,

Поэтому, когда мне нужен динамический буфер для целей сериализации / десериализации, я никак не могу избежать unsigned char * вещи для того, чтобы преобразовать в тип безопасного управления буфером; но когда я гневно-рефакторинг все alloc/free пар в new/delete пары, которые я читал о std::allocator,

std::allocator позволяет зарезервировать блоки памяти типобезопасным способом, на первый взгляд, я уверен, что это будет полезно, но нет большой разницы между распределением с помощью std::allocator<int>::allocate или же new int или так я думал, то же самое было для std::allocator<int>::deallocate а также delete int,

А теперь я потерял север в отношении динамического управления памятью, поэтому я спрашиваю:

  • Есть хорошая практика C ++, включающая динамическое управление памятью для целей сериализации / десериализации, которая предоставляет безопасное управление типами?
  • Можно ли избежать использования const char * для сериализации / десериализации буферов памяти?
  • Каково обоснование std::allocator и как он используется в области сериализации / десериализации? (если есть).

Спасибо за внимание!

10

Решение

Мой опыт показывает, что безопасность типов в C ++ означает не только то, что компилятор жалуется на несоответствия типов. Это скорее означает, что вам вообще не нужно заботиться о расположении памяти в ваших данных. На самом деле, стандарт C ++ предъявляет очень мало требований к разметке памяти определенных типов данных.

Ваша сериализация основана на прямом доступе к памяти, поэтому, боюсь, не будет простого «чистого» решения C ++ и, в частности, не будет общего решения, независимого от компилятора / платформы.

1

Другие решения

Других решений пока нет …