сигнатура функции библиотеки * hellip;
void rawSend( const vector<unsigned char>* data );
заставляет вас создавать std::vector
ваших данных, что, по сути, означает, что они навязывают ненужную неэффективность. Нет смысла требовать от клиента кода для сборки std::vector
. Тот, кто спроектировал это, не знает, что делает, и было бы разумно не использовать их программное обеспечение.
Функция подписи библиотеки & hellip;
vector<unsigned char>* rawRead(unsigned bufferSize = DEFAULT_BUFFER_SIZE, string* hostFrom = NULL);
хуже: он не просто требует от вас создания std::string
, если вы хотите указать & ldquo; hostFrom & rdquo; (что бы это ни значило на самом деле), но это излишне требует от вас освобождения результата vector
. По крайней мере, если есть смысл использовать тип результата. Которого, конечно же, может и не быть.
Вы не должны использовать библиотеку с такими отвратительными сигнатурами функций. Вероятно, любая случайно выбранная библиотека будет намного лучше. Гораздо проще в использовании.
Как существующий код использования & hellip;
void send_message(NLSocket* socket, uint16_t opcode, const void* rawData, size_t rawDataSize)
{
vector<unsigned char> buffer;
buffer.reserve(sizeof(uint16_t) + rawDataSize);
buffer.push_back(opcode >> 8);
buffer.push_back(opcode & 0xFF);
const unsigned char* base(reinterpret_cast<const unsigned char*>(rawData));
buffer.insert(buffer.end(), base, base + rawDataSize);
socket->rawSend(&buffer);
}
работает:
Вызов reserve
- это случай преждевременной оптимизации. Он пытается заставить vector
сделать только одно выделение буфера (выполненное в этот момент) вместо, возможно, двух или более. Гораздо лучшим лекарством от очевидной неэффективности построения vector
является использование более разумной библиотеки.
buffer.push_back(opcode >> 8)
помещает старшие 8 бит (предполагаемого) 16-битного количества opcode
в начало вектора. Размещая верхнюю часть, наиболее значимая часть, во-первых, называется форматом big endian . Ваш код чтения на другом конце должен принимать формат с прямым порядком байтов. И точно так же, если бы этот отправляющий код использовал формат little endian , тогда код чтения должен был бы принимать формат little endian. Таким образом, это всего лишь решение о формате данных, но, учитывая решение, код на обоих концах должен его придерживаться.
Вызовы buffer.push_back(opcode & 0xFF)
размещают младшие 8 бит opcode
после старших бит, как правильно для старшего байта.
Декларация const unsigned char* base(reinterpret_cast<const unsigned char*>(rawData))
просто называет правильно введенный указатель на ваши данные, называя его base
. Тип const unsigned char*
подходит, поскольку он разрешает байтовый уровень адресную арифметику . Исходный формальный тип аргумента const void*
не допускает адресную арифметику.
buffer.insert(buffer.end(), base, base + rawDataSize)
добавляет данные к вектору. Выражение base + rawDataSize
является адресной арифметикой, которую включило предыдущее объявление.
socket->rawSend(&buffer)
- последний вызов метода SillyLibrary & rsquo; s rawSend
.
Как обернуть вызов в функцию SillyLibrary rawRead
.
Во-первых, определите имя для типа данных байта (всегда хорошая идея, чтобы называть вещи):
typedef unsigned char Byte;
typedef ptrdiff_t Size;
Обратитесь к документации о том, как освободить / уничтожить / удалить (при необходимости) результат функции SillyLibrary:
void deleteSillyLibVector( vector<Byte> const* p )
{
// perhaps just "delete p", but it depends on the SillyLibrary
}
Теперь для операции отправки, включающей std::vector
, была просто боль. Для операции приема все наоборот. Создание динамического массива и его безопасная и эффективная передача в виде результата функции - это как раз то, для чего была разработана std::vector
.
Однако операция отправки была всего лишь одним вызовом.
Для операции приема это возможно , в зависимости от конструкции SillyLibrary, вам необходимо выполнить цикл для выполнения количества входящих вызовов до тех пор, пока вы не получите все данные. Вы не предоставляете достаточно информации для этого. Но код ниже показывает нижний уровень чтения , который может вызвать ваш код цикла, накапливая данные в vector
:
Size receive_append( NLSocket& socket, vector<Byte>& data )
{
vector<Byte> const* const result = socket.raw_read();
if( result == 0 )
{
return 0;
}
struct ScopeGuard
{
vector<Byte>* pDoomed;
explicit ScopeGuard( vector<Byte>* p ): pDoomed( p ) {}
~ScopeGuard() { deleteSillyLibVector( pDoomed ); }
};
Size const nBytesRead = result->size();
ScopeGuard cleanup( result );
data.insert( data.end(), result->begin(), result->end() );
return nBytesRead;
}
Обратите внимание на использование деструктора для очистки, что делает это исключение более безопасным.В данном конкретном случае единственным возможным исключением является std::bad_alloc
, что в любом случае довольно фатально.Но общая техника использования деструктора для очистки, для безопасности исключений, очень стоит знать и использовать как нечто само собой разумеющееся (хотя обычно не требуется определять какой-либо новый класс, но при работе с классом SillyLibraryвозможно, придется это сделать).
Наконец, когда ваш код зацикливания определил, что все данные находятся под рукой, он может интерпретировать данные в вашем vector
.Я оставляю это как упражнение, хотя это в основном то, что вы просили.И это потому, что я уже написал почти как целую статью здесь.
Отказ от ответственности: нецензурный код.
Приветствия & hth.,