Использование read () непосредственно в C ++ std: vector - PullRequest
15 голосов
/ 06 мая 2010

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

Я хочу предложить реализацию чтения и записи с использованием вектора.

Сделать запись довольно легко, я могу просто пропустить &myvec[0] и избежать ненужного копирования.Я хотел бы сделать то же самое и читать непосредственно в вектор, а не читать в буфер символов, а затем копировать все это во вновь созданный вектор.

Теперь я знаю, сколько данных я хочу прочитать,и я могу выделить соответствующим образом (vec.reserve()).Я также могу прочитать в &myvec[0], хотя это, вероятно, очень плохая идея.Очевидно, что это не позволяет myvec.size возвращать что-либо разумное.Есть ли способ сделать это, что:

  1. Не полностью отвратительно с точки зрения безопасности / C ++
  2. Не включает две копии блока данных - один раз из ядрав пространство пользователя и один раз из буфера стиля C char * в вектор C ++.

Ответы [ 4 ]

22 голосов
/ 06 мая 2010

Используйте resize() вместо reserve(). Это правильно установит размер вектора - и после этого &myvec[0], как обычно, гарантированно указывает на непрерывный блок памяти.

Редактировать: Использование &myvec[0] в качестве указателя на базовый массив для чтения и записи безопасно и гарантированно работает по стандарту C ++. Вот что говорит Херб Саттер :

Так почему люди постоянно спрашивают, хранятся ли элементы std :: vector (или std :: array) непрерывно? Наиболее вероятная причина заключается в том, что они хотят знать, могут ли они выкашливать указатели на внутренние компоненты, чтобы делиться данными, либо для чтения, либо для записи, с другим кодом, который работает с массивами Си. Это допустимое использование, и оно достаточно важно для гарантии в стандарте.

1 голос
/ 06 мая 2010

Я просто добавлю краткое пояснение, потому что ответ уже был дан. resize () с аргументом, превышающим текущий размер, добавит элементы в коллекцию, а по умолчанию - инициализирует их. Если вы создаете

std::vector<unsigned char> v;

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

v.resize(someSize);

Все символы без знака будут инициализированы равными 0. Кстати, вы можете сделать то же самое с конструктором

std::vector<unsigned char> v(someSize);

Так что теоретически это может быть немного медленнее, чем необработанный массив, но если альтернативой является копирование массива в любом случае, лучше.

Резерв только подготавливает память, поэтому перераспределение не требуется, если в коллекцию добавлены новые элементы, но вы не можете получить доступ к этой памяти.

Вы должны получить информацию о количестве элементов, записанных в Ваш вектор. Вектор ничего об этом не узнает.

1 голос
/ 06 мая 2010

Если вы хотите, чтобы вектор отображал количество прочитанных данных, дважды наберите resize(). Однажды перед чтением, чтобы дать себе место для чтения. Еще раз после чтения, чтобы установить размер вектора равным количеству фактически прочитанных байтов. reserve() не годится, поскольку резервный вызов не дает вам доступа к памяти, выделенной для емкости.

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

С точки зрения производительности, если вы читаете из сокета в пользовательском режиме, скорее всего, вы можете легко обрабатывать данные так же быстро, как они поступают. Возможно, нет, если вы подключаетесь к другому компьютеру в гигабитной локальной сети или если на вашей машине часто используется 100% процессор или 100% пропускная способность памяти. Небольшое дополнительное копирование или установка мемов не имеет большого значения, если вы все равно собираетесь заблокировать вызов read.

Как и вы, я бы хотел избежать дополнительной копии в пользовательском пространстве, но не по соображениям производительности, просто потому, что если я этого не сделаю, мне не нужно писать код для него ...

1 голос
/ 06 мая 2010

Предполагая, что это структура POD, вызовите resize вместо reserve. Вы можете определить пустой конструктор по умолчанию, если вы действительно не хотите обнулять данные перед заполнением вектора.

Это несколько низкий уровень, но семантика построения структур POD нарочито мутная. Если memmove разрешено копировать-конструировать их, я не понимаю, почему чтение из сокета не должно.

РЕДАКТИРОВАТЬ: ах, байты, а не структура. Ну, вы можете использовать тот же трюк и определить структуру с помощью char и конструктора по умолчанию, который пренебрегает его инициализацией ... если я правильно догадываюсь, что вам все равно, и именно поэтому вы хотели вместо этого вызвать reserve resize на первом месте.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...