Использование read() непосредственно в С++ std: vector
Я завершаю функциональность сокета linux для пользовательского пространства в некоторых С++ для встроенной системы (да, это, вероятно, снова изобретает колесо).
Я хочу предложить реализацию чтения и записи с использованием вектора.
Выполнение записи довольно просто, я могу просто передать &myvec[0]
и избежать ненужного копирования. Я хотел бы сделать то же самое и читать прямо в вектор, а не читать в буфере char, а затем копировать все это во вновь созданный вектор.
Теперь я знаю, сколько данных я хочу прочитать, и я могу выделить соответственно (vec.reserve()
). Я также могу читать &myvec[0]
, хотя это, вероятно, ОЧЕНЬ ПЛОХАЯ ИДЕЯ. Очевидно, что это не позволяет myvec.size возвращать что-либо разумное. Есть ли способ сделать это, чтобы:
- Полностью не чувствует себя в безопасности с точки зрения безопасности/С++.
- Не включает две копии блока данных - один раз из ядра в пользовательское пространство и один раз из буфера стиля C
char *
в вектор С++.
Ответы
Ответ 1
Используйте resize()
вместо reserve()
. Это установит размер вектора правильно - и после этого &myvec[0]
, как обычно, гарантированно укажет на ограниченный блок памяти.
Изменить: использование &myvec[0]
в качестве указателя на базовый массив для чтения и записи безопасно и гарантированно работает по стандарту С++. Вот, что сказать Herb Sutter :
Итак, почему люди постоянно спрашивают, хранятся ли элементы std::vector (или std:: array) смежно? Наиболее вероятная причина заключается в том, что они хотят знать, могут ли они кашлять указатели на внутренности, чтобы делиться данными, как читать, так и писать, с другим кодом, который имеет дело с массивами C. Это правильное использование и достаточно важное для гарантии в стандарте.
Ответ 2
Предполагая, что это структура POD, вызовите resize
, а не reserve
. Вы можете определить пустой конструктор по умолчанию, если вы действительно не хотите, чтобы данные были обнулены перед заполнением вектора.
Это несколько низкий уровень, но семантика построения структур POD преднамеренно мутная. Если memmove
разрешено копировать-конструировать их, я не понимаю, почему чтение сокетов не должно быть.
EDIT: ah, bytes, а не struct. Ну, вы можете использовать один и тот же трюк и определить структуру с помощью всего лишь char
и конструктора по умолчанию, который пренебрегает его инициализацией... если я правильно догадываюсь о том, что вам небезразлично, и почему вы хотели называть reserve
вместо этого resize
в первую очередь.
Ответ 3
Если вы хотите, чтобы вектор отражал количество прочитанных данных, дважды вызовите resize()
. Однажды перед чтением, чтобы дать себе пространство для чтения. Еще раз после чтения задайте размер вектора для количества фактически прочитанных байтов. reserve()
не является хорошим, поскольку вызов резерва не дает вам разрешения на доступ к памяти, выделенной для емкости.
Первый resize()
будет обнулять элементы вектора, но это вряд ли приведет к значительным издержкам производительности. Если это так, вы можете попробовать предложение Potatoswatter, или вы можете отказаться от размера вектора, отражающего размер прочитанных данных, а вместо этого просто resize()
один раз, а затем повторно использовать его точно так же, как и выделенный буфер в C.
По производительности, если вы читаете сокет в пользовательском режиме, скорее всего, вы можете легко обрабатывать данные так быстро, как это происходит. Возможно, нет, если вы подключаетесь к другой машине в гигабитной локальной сети или если на вашем компьютере часто работает 100% процессор или 100% пропускная способность памяти. Немного дополнительного копирования или memsetting не имеет большого значения, если вы в конечном итоге собираетесь блокировать вызов read
в любом случае.
Как и вы, я бы хотел избежать дополнительной копии в пространстве пользователя, но не по соображениям производительности, потому что, если я этого не сделаю, мне не нужно писать код для этого...
Ответ 4
Я просто добавлю короткое разъяснение, потому что ответ уже дан. resize() с аргументом больше текущего размера добавит элементы в коллекцию, а default - инициализирует их. Если вы создаете
std::vector<unsigned char> v;
а затем измените размер
v.resize(someSize);
Все символы без знака будут инициализированы на 0. Btw Вы можете сделать то же самое с конструктором
std::vector<unsigned char> v(someSize);
Таким образом, теоретически это может быть немного медленнее, чем исходный массив, но если альтернативой является копирование массива в любом случае, это лучше.
Резерв только подготавливает память, так что нет необходимости перераспределения, если новые элементы добавляются в коллекцию, но вы не можете получить доступ к этой памяти.
Вам нужно получить информацию о количестве элементов, написанных для вашего вектора. Вектор ничего не знает об этом.