Russian Qt Forum
Апрель 25, 2024, 08:03 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: С++ парсинг бинарных структур из байтового массива  (Прочитано 10676 раз)
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« : Октябрь 19, 2014, 19:06 »

Всем доброго времени.

Какой самый "лучший" способ проверки принадлежности того или иного принятого QByteArray к тому или иному типу структуры?

Например, принимаю пакет/структуру с полями типа:

Цитировать

opcode=2 byte, len=1 byte, params = x bytes

где,
opcode - код операции;
len - размер поля params;
params - вариабельное поле, которое имеет разную длину и содержимое в зависимости от opcode


Я пока знаю два метода:

1) "Конструирования" заданной структуры из QByteArray
Код
C++ (Qt)
struct BasePacket
{
   enum { OpCodeOffset = 0, LenOffset = 2, ParamsOffset = 3; }
 
   BasePacket (QByteArray &packet): packet(packet)
   {
   }
 
   quint16 opCode() const
   {
       quint16 opcode = 0;
       // read the opcode field from packet from OpCodeOffset
       return opcode;
   }
 
   QByteArray packet;
};
 
struct FooPacket : public BasePacket
{
   enum { Foo_OpCode = 0x1234, Foo_PacketSize = xxx };
 
   bool isValid() const
   {
       if (packet.size() != Foo_PacketSize)
           return false;
       if (packet.at(LenOffset ) != (packet.size() - ParamsOffset))
           return false;
       if (opCode() != Foo_OpCode)
           return false;
       return true;
   }
};
 
struct BarPacket : public BasePacket
{
   enum { Bar_OpCode = 0x5678, Bar_PacketSize = yyy };
 
   bool isValid() const
   {
       if (packet.size() != Bar_PacketSize)
           return false;
       if (packet.at(LenOffset ) != (packet.size() - ParamsOffset))
           return false;
       if (opCode() != Bar_OpCode)
           return false;
       return true;
   }
};
 
 

Тогда в обработчиках для каждого пакета можно сделать проверки:
Код
C++ (Qt)
class BasePacketHandler
{
public:
   virtual bool canHandle(const QByteArray &packet) const = 0;
}
 
class FooPacketHandler : public BasePacketHandler
{
public:
   bool canHandle(const QByteArray &packet) const
   {
       return FooPacket(packet).isValid();
   }
}
 
class BarPacketHandler : public BasePacketHandler
{
public:
   bool canHandle(const QByteArray &packet) const
   {
       return BarPacket(packet).isValid();
   }
}
 

2) Создания набора статических методов:

Код
C++ (Qt)
struct BasePacket
{
   enum { OpCodeOffset = 0, LenOffset = 2, ParamsOffset = 3; }
 
   BasePacket (QByteArray &packet): packet(packet)
   {
   }
 
   static quint16 opCode(QByteArray &packet) {
       quint16 opcode = 0;
       // read the opcode field from packet from OpCodeOffset
       return opcode;
   }
 
   QByteArray packet;
};
 
struct FooPacket : public BasePacket
{
   enum { Foo_OpCode = 0x1234, Foo_PacketSize = xxx };
 
   static bool isValid(QByteArray &packet)
   {
       if (packet.size() != Foo_PacketSize)
           return false;
       if (packet.at(LenOffset ) != (packet.size() - ParamsOffset))
           return false;
       if (opCode(packet) != Foo_OpCode)
           return false;
       return true;
   }
};
 
struct BarPacket : public BasePacket
{
   enum { Bar_OpCode = 0x5678, Bar_PacketSize = yyy };
 
   static bool isValid(QByteArray &packet)
   {
       if (packet.size() != Bar_PacketSize)
           return false;
       if (packet.at(LenOffset ) != (packet.size() - ParamsOffset))
           return false;
       if (opCode(packet) != Bar_OpCode)
           return false;
       return true;
   }
};
 

Тогда в обработчиках для каждого пакета можно сделать проверки:
Код
C++ (Qt)
class BasePacketHandler
{
public:
   virtual bool canHandle(const QByteArray &packet) const = 0;
};
 
class FooPacketHandler : public BasePacketHandler
{
public:
   bool canHandle(const QByteArray &packet) const
   {
       return FooPacket::isValid(packet);
   }
};
 
class BarPacketHandler : public BasePacketHandler
{
public:
   bool canHandle(const QByteArray &packet) const
   {
       return BarPacket::isValid(packet);
   }
};
 

Что предпочтительнее? или есть иной простой способ как сделать желаемое ? Улыбающийся

ЗЫ: естественно интересуют всякие там паттерны; чисто Си-шное решение с switch/case  и прочим не предлагать.

ЗЫЗЫ: вроде как использование статических методов не дает оверхеда (нет копирования), но с другой стороны метод isValid() можно сделать inline...

ЗЫЗЫЗЫ: уже ломаю мозг неделю Улыбающийся
« Последнее редактирование: Октябрь 19, 2014, 19:27 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
vulko
Гость
« Ответ #1 : Октябрь 20, 2014, 09:38 »

Зачем изобретаешь велосипед?

Цитировать
opcode - код операции;
len - размер поля params;
params - вариабельное поле, которое имеет разную длину и содержимое в зависимости от opcode

len тут совершенно не нужен. по opcode можно точно определить размер структуры упакованной в массив байт.

Зачем такая дикая имплементация со структурой внутри которой QByteArray не понимаю.

Мой совет таков, не городи фигню и сделай switch/case по opcode.
Причины просты:
1. это работает и работает быстро
2. не нужно плодить сто миллионов классов для каждого типа сообщения
3. меньше писать кода, меньше потенциальных ошибок, меньше проблем

Ну и наконец в каком-то месте все равно будет switch/case. Ну или if {} else if {} else if...
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #2 : Октябрь 20, 2014, 09:39 »

Код
C++ (Qt)
template <qint32 packID, qint32 packSize>
struct CPacket {
CPacket( QByteArray & ba ) : mError(false), mBA(ba)
{
  int id = 0, size = 0;
  QDataStream ds(ba);
  ds >> id >> size;
  mError = (id != packID) ||  (size != packSize) || (mBA.size() - sizeof(qint32) * 2 != packSize);
}
 
bool mError;
QByteArray & mBA;
};
 
int main(int argc, char *argv[])
{
typedef CPacket <'PAC1', 32> MyPacket;
QByteArray ba;
MyPacket p(ba);
if (!p.mError) return 1;
return 0;
}
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #3 : Октябрь 20, 2014, 11:02 »

Дальше насаживаем этот template на виртуальный базовый

Код
C++ (Qt)
template <qint32 packID, qint32 packSize>
struct CPacket : public BasePacket {
...
// BasePacket virtuals
virtual int OpCode( void ) const { return packID; }
virtual int DataSize( void ) const { return packSize; }
...
};
 
Создаем хвабрику..

Код
C++ (Qt)
typedef std::map <int, BasePacket *> TPacketMap;
TPacketMap mPacketMap;
 
void Add2Factory( BasePacket * p )
{
mPacketMap[p->OpCode()] = p;
}
 
Add2Factory(new CPacket <'PAC1', 32> ());
Add2Factory(new CPacket <'PAC2', 15> ());
Add2Factory(new CPacket <'PAC3', 20> ());
...
Использование: по коду операции находим нужный класс
Код
C++ (Qt)
BasePacket * Lookup( int opCode )
{
TPacketMap::iterator it = mPacketMap.find(opCode);
return (it == mPacketMap.end()) ? 0 : it->second;
}
Т.е. классов все равно "много", но их не надо размазывать каждый, один раз на создании и все
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #4 : Октябрь 20, 2014, 11:12 »

2 vulko,

Цитата: vulko
len тут совершенно не нужен. по opcode можно точно определить размер структуры упакованной в массив байт.

Не, это не от меня зависит. Этот формат описан в спецификации.

Цитата: vulko
Зачем такая дикая имплементация со структурой внутри которой QByteArray не понимаю.

Чтобы можно было потом сделать:

Код
C++ (Qt)
 
QByteArray data1; // приняли пакет FooPacket
FooPacket foo(data1);
QString name = foo.name();
 
QByteArray data2; // приняли пакет BarPacket
BarPacket bar(data2);
quint8 type = bar.type();
Address address = bar.address();
 
 

Дело в том, что у каждого пакета разное содержимое поля params.
Например, у пакета FooPacket это поле содержит параметр Name; у ракета BarPacket два параметра: тип и адрес и т.п.

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

Т.е. идея в том, что только конкретные классы пакетов знают как извлекать свои параметры из RAW пакета. Улыбающийся


2 Igors,

Я не могу использовать  QDataStream, т.к. принимаю пакеты не из Qt-шного приложения. Но должен их парсить методами Qt. Улыбающийся

Цитата: Igors
Т.е. классов все равно "много", но их не надо размазывать каждый, один раз на создании и все

В смысле? Мне не нужно ничего создавать. Мне нужно просто "распарсить" принятый пакет (QByteArray) и в зависимости от его принадлежности
извлечь необходимые параметры. Улыбающийся

Тут по идее должна быть список с конкретными обработчиками, каждый из которых "реагирует" только на определенный пакет.

Код
C++ (Qt)
 
class BasePacketHandler
{
public:
   virtual bool canHandle(const QByteArray &packet) const = 0;
};
 
class FooPacketHandler : public BasePacketHandler
{
public:
   bool canHandle(const QByteArray &packet) const
   {
       // как тут правильнее парсить?
       return true/false;
   }
};
 
class BarPacketHandler : public BasePacketHandler
{
public:
   bool canHandle(const QByteArray &packet) const
   {
       // как тут правильнее парсить?
       return true/false;
   }
};
 
QList<BasePacketHandler *> handlers = QList<BasePacketHandler *>() << new FooPacketHandler << new BarPacketHandler;
 
void Manager::handleIncomingPacket(const QByteArray &packet)
{
   foreach (BasePacketHandler *handler, handlers) {
       if (handler->canHandle(packet) {
           // do something
       }
   }
}
 
 

Но тут главный вопрос: как красиво парсить QByteArray и приводить к определенному типу чтобы можно было
легко изъять нужные параметры из QByteArray.
« Последнее редактирование: Октябрь 20, 2014, 11:36 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #5 : Октябрь 20, 2014, 12:03 »

С таким сталкивался при реализации чтения/записи в universal binary json.
Можете посмотреть как это сделано у меня.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Октябрь 20, 2014, 12:26 »

Код
C++ (Qt)
class BasePacketHandler
{
public:
   virtual bool canHandle(const QByteArray &packet) const = 0;
};
 
class FooPacketHandler : public BasePacketHandler
{
public:
   bool canHandle(const QByteArray &packet) const
   {
       // как тут правильнее парсить?
       return true/false;
   }
};
 
Полагаем что canHandle решает "мой пакет или нет", а валидный ли он - уже др вопрос. Тогда сваливаем этот метод в базовый класс
Код
C++ (Qt)
class BasePacketHandler
{
public:
   virtual bool canHandle(const QByteArray &packet) const
   {
       return (packet.size() >= 3) && (*(unsigned short *) &packet[0] == OpCode());
   }
};
 
А OpCode - подскоком из темплейта (см выше)
Записан
vulko
Гость
« Ответ #7 : Октябрь 20, 2014, 15:33 »

Чтобы можно было потом сделать:

Код
C++ (Qt)
QByteArray data1; // приняли пакет FooPacket
FooPacket foo(data1);
QString name = foo.name();
 
QByteArray data2; // приняли пакет BarPacket
BarPacket bar(data2);
quint8 type = bar.type();
Address address = bar.address();
 

Дело в том, что у каждого пакета разное содержимое поля params.
Например, у пакета FooPacket это поле содержит параметр Name; у ракета BarPacket два параметра: тип и адрес и т.п.

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

Т.е. идея в том, что только конкретные классы пакетов знают как извлекать свои параметры из RAW пакета. Улыбающийся

Я понял в чем идея. Но я не вижу ни одной причины чтобы заниматься такой фигней...
Проверить пакет на валидность можно ведь и без излишней ООПтимизации.
Ну есть у вас 20 классов, по одному на каждый тип пакета. Один фиг каждый из них при проверке на валидность проверять будет тупо ID пакета (опкод он у вас называется).
При этом у вас куча однообразного кода, который будет занимать много места.

Да, типо ООП. Да, типа шаблоны применил.
Только профита никакого. Если нужно красивое внешнее АПИ, не проблема его сделать. Но зачем изобретать такой велосипед для простой задачи, которая где-то внутри проекта находится, я не понимаю.

Это ведь не просто много времени на копи паст и переделывание классов наследников - это потенциальный гемор при тестировании.
Записан
vulko
Гость
« Ответ #8 : Октябрь 20, 2014, 15:37 »

Но тут главный вопрос: как красиво парсить QByteArray и приводить к определенному типу чтобы можно было
легко изъять нужные параметры из QByteArray.

QByteArray data;
QDataStream in(&data, QDataStream::ReadOnly);
int opcode;
int len;
char* data;
in >> opcode;
in >> len;
in.readRawData(data, len)); (или readRawBytes, не помню точно...)

далее MyStruct *struct = (MyStruct*) data;
либо парсить поочередно, в зависимости от того кто и как писал данные в QByteArray.
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #9 : Октябрь 20, 2014, 16:16 »

2 vulko,

Да елки, ну не применить в моем случае QDataStream, ну никак. Улыбающийся

Цитировать
далее MyStruct *struct = (MyStruct*) data;

Тоже не вариант: нужно делать "pragma pack" и прочую фигню. Гемор еще больший.

Цитировать
При этом у вас куча однообразного кода, который будет занимать много места.

Зато каждый на своем месте Улыбающийся.

2 Igors,

Эмм.. я привел только маленькую часть задачи.. Поэтому наверное оно как-то кажется непонятным..

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

То что идет привязка к OpCode - это просто так получилось из частного случая (просто для примера).
В реальности я не могу привязаться к OpCode т.к. пакет содержит еще дополнительные поля.. т.е.:

Команда: [opcode = 2byte] + [len = 1 byte] + [params = xxx bytes]
Евент:     [eventcode = 1 byte] + [len = 1 byte] + [params = xxx bytes]

При этом, event поле params будет содержать: [num = 1 byte] + [opcode = 2 byte] + [return params = xxx bytes]

далее, содержимое под-поля [return params] зависит от opcode и может иметь разный тип данных.


Например,

команда Reset:

Req  : 030c00 - зарос
Resp : 0e0401030c00 - ответ

где: 030c00:

- 030c - opcode
- 00 - len (нет параметров)

где: 0e0401030c00

- 0e - код евента что это евент завершения команды
- 04 - len длина поля параметры
- 01030c00 - параметры:
    - 01 - некий код (неважно)
    - 030c - opcod команды на которую этот евент
    - 00 - статус выполнения команды

т.е. сейчас реализовано по такому типу:

Код
C++ (Qt)
const ResetCmd cmd;
Reply *reply = manager->exec(cmd);
connect(reply, SIGNAL(finished(), this, SLOT(onResetComplete()));
 
Foo::onResetComplete()
{
   Reply *reply = qobject_cast<Reply *>(sender());
 
   QByteArray req = reply->request(); // RAW команда которая была послана
   QByteArray resp = reply->response(); // RAW евент (ответ), который был получен на команду
 
   // естественно, что этот слот - это уже готовый (обработанный результат), который обрабатывает Manager.
   // и в этом Manager вся логика и обработчики
 
   // но теперь я хочу получить параметры ответа resp. сразу и без проблем, т.к. я точно знаю его тип
   ResetCompleteEvent ev(resp);
 
   quint status = ev.status();
   QString name = ev.name();
 
   // далее могу обработать status (или иные параметры) как мне надо
   if (status == xxx) {
       // команда успешна
   } else if (status == yyy) {
       // команда сфейлилась
   } else ...
 
   emit deviceDiscovered(name);
   // и прочее
 
}
 

т.е. по-любому нужно плодить классы для каждой из команд/события. Может быть, ДА, где -то применить шаблоны.

т.е. не прокатит просто так switch/case и прочее..

PS: В общем, портирую Bluez на Windows (HCI стек) для своего проекта Веселый
« Последнее редактирование: Октябрь 20, 2014, 16:21 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Октябрь 20, 2014, 18:05 »

т.е. по-любому нужно плодить классы для каждой из команд/события. Может быть, ДА, где -то применить шаблоны.
Да понятно, случай-то нередкий. Создается по экземпляру каждого обработчика и складывается в контейнер. Когда пришел QByteArray - перебираем все обработчики контейнера, какой-то ответит "да, это мой пакет". Если никто не ответит - ну инвалидный пакет. Да, каждый обработчик свой класс, ничего страшного.

То есть все сводится к тому как удобно реализовать эти классы. Напр создаем базовый класс с чистыми виртуалами и наследуемся. Это вполне нормально, но общности между различными обработчиками может оказаться мало. Напр поместив поле OpCode в базовый класс - что тогда с пакетами Event?

Поэтому делают "просто классы" - с темплейтами/наследованием или без, как получится. Структуры данных все равно надо где-то определять. А потом пристегивают общую виртуальную базу (иначе не выскочить на нужный обработчик). Напр

где: 0e0401030c00

- 0e - код евента что это евент завершения команды
- 04 - len длина поля параметры
- 01030c00 - параметры:
    - 01 - некий код (неважно)
    - 030c - opcod команды на которую этот евент
    - 00 - статус выполнения команды
Ну и рисуете такую структуру, удастся сэкономить на template или наследованием на подобной - хорошо, нет - переживем. Создаете методы чтения/записи из QByteArray. А дальше добавляете базовые виртуалы, напр canHandle который просто вызывает метод чтения и возвращает код ошибки. Обычно удается все на хедерах (без .cpp файлов)
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #11 : Октябрь 20, 2014, 19:20 »

Цитата: Igors
Да понятно, случай-то нередкий. Создается по экземпляру каждого обработчика и складывается в контейнер. Когда пришел QByteArray - перебираем все обработчики контейнера, какой-то ответит "да, это мой пакет". Если никто не ответит - ну инвалидный пакет. Да, каждый обработчик свой класс, ничего страшного.

Ага.

Цитата: Igors
То есть все сводится к тому как удобно реализовать эти классы. Напр создаем базовый класс с чистыми виртуалами и наследуемся. Это вполне нормально, но общности между различными обработчиками может оказаться мало. Напр поместив поле OpCode в базовый класс - что тогда с пакетами Event?
 

Сейчас обработчики имеют по два абстрактных метода:

Код
C++ (Qt)
class HciReplyHandler
{
public:
   virtual bool canHandleRequest(const QByteArray &request) const = 0;
   virtual bool canHandleResponse(const QByteArray &response) const = 0;
};
 

метод exec() менеджера такой:

Код
C++ (Qt)
HciReply *HciAccessManager::execute(const QByteArray &request)
{
   foreach (HciReplyHandler *handler, m_handlers) { // перебираем зарегистрированные обработчики
       Q_ASSERT(handler);
 
       if (!handler->canHandleRequest(request)) // проверяем - а чей, собственно запрос?
           continue;
 
       HciReply *reply = new HciReply(handler->replyType(), this);
       if (reply->type() == HciReply::Pending) {
           reply->setRequest(request);
           m_pendingReplies.insert(reply, handler); // засовываем Reply и его обработчик в хеш
           m_transport->writeCommandPacket(request);
       }
 
       return reply;
   }
   return 0;
}
 

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

Слот отбаботки ответа менеджера такой (т.е. этот слот дергается по приходу события):

Код
C++ (Qt)
void HciAccessManager::response(const QByteArray &packet)
{
   foreach (HciReplyHandler *handler, m_handlers) { // перебираем обработчики и ищем подходяший
       Q_ASSERT(handler);
 
       if (!handler->canHandleResponse(packet)) // а чей, собственно ответ?
           continue;
 
       // ищем Reply по обработчику (тут, конечно, косячно все т.к. к одному обработчику могут принадлежать несколько Reply)
       // но в данном случае - пофик, т.к. оно найдет первый попавшийся и нам этого достаточно, т.к. без разницы :)
       HciReply *reply = m_pendingReplies.key(handler);
       Q_ASSERT(reply);
 
       reply->setResponse(packet);
       if (handler->replyType() == HciReply::Pending)
           m_pendingReplies.take(reply);
 
       finalizeEventReply(reply);
       return;
   }
}
 

Реализация команды/события/обработчика Reset для примера такая:

Код
C++ (Qt)
HciResetCommand::HciResetCommand()
   : HciCommand(HciCommand::ControllerBasebandGroup, HciCommand::ResetCode)
{
   packet.resize(packet.size() + TotalParametersLength);
   setTotalParametersLength(TotalParametersLength);
}
 
HciResetCommand::HciResetCommand(const QByteArray &packet)
   : HciCommand(packet)
{
}
 
bool HciResetCommand::isValid() const
{
   return isComplete()
           && (opCode() == HciCommand::opCode(HciCommand::ControllerBasebandGroup, HciCommand::ResetCode));
}
 
HciResetCommandCompleteEvent::HciResetCommandCompleteEvent(const QByteArray &packet)
   : HciCommandCompleteEvent(packet)
{
}
 
quint8 HciResetCommandCompleteEvent::status() const
{
   return packet.at(parametersOffset() + ReturnParametersOffset + StatusOffset);
}
 
bool HciResetCommandCompleteEvent::isValid() const
{
   return isComplete()
           && (packet.size() == TotalPackeSize)
           && (opCode() == HciCommand::opCode(HciCommand::ControllerBasebandGroup, HciCommand::ResetCode));
}
 
bool HciResetReplyHandler::canHandleRequest(const QByteArray &request) const
{
   return HciResetCommand(request).isValid();
}
 
bool HciResetReplyHandler::canHandleResponse(const QByteArray &response) const
{
   return HciResetCommandCompleteEvent(response).isValid();
}
 

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

ArchLinux x86_64 / Win10 64 bit
vulko
Гость
« Ответ #12 : Октябрь 21, 2014, 08:17 »

2 vulko,

Да елки, ну не применить в моем случае QDataStream, ну никак. Улыбающийся

Удивительно, что QByteArray есть, а QDataStream нельзя применить...)))
Это всего лишь удобный wrapper для QByteArray (и не только), созданный для удобной сериализации/десереализации данных.


Цитировать
далее MyStruct *struct = (MyStruct*) data;

Тоже не вариант: нужно делать "pragma pack" и прочую фигню. Гемор еще больший.

Совсем не обязательно. Зависит от того как паковали.

А даже если нужно будь изменить паковку структуры, то никакого гемора нет.
Гемор это как раз плодить никому не нужные классы в низкоуровневом АПИ.

Код:
#pragma pack(push, 1)
struct MyStruct
{
    // ...
};
#pragma pack(pop)
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #13 : Октябрь 21, 2014, 09:16 »

По поводу хеша. Чаще не берут обработчик из контейнера, а делают его копию, т.к. он может быть задействован 2 и более раз. Тогда мапируйтесть как хотите - можно и 2 хеша, прямой и обратный

Насчет размазывания классов обработчиков. Ну да, выглядит неск занудно, но вполне цивильно. Все же не лучше ли выделить Command и Event отдельно? Ну будет 2 контейнера, ничего плохого. Зато многие операции можно слить в базовый класс(ы) и схлопнуть текст (хоть с виртуалами, хоть с темплейтпми).
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #14 : Октябрь 21, 2014, 11:09 »

2 vulko,

Цитата: vulko
Удивительно, что QByteArray есть, а QDataStream нельзя применить...)))
Это всего лишь удобный wrapper для QByteArray (и не только), созданный для удобной сериализации/десереализации данных.

Ничего удивительного, почитайте для чего нужен QDataStream и когда можно, а когда нельзя его использовать.

Цитата: vulko
Совсем не обязательно. Зависит от того как паковали.

Кого как паковали? Работа идет с массивами данных / пакетами. Никого никуда не паковали.

Цитата: vulko
Гемор это как раз плодить никому не нужные классы в низкоуровневом АПИ.
Код:
#pragma pack(push, 1)
struct MyStruct
{
    // ...
};
#pragma pack(pop)

А вот это уже как раз-таки гемор.


2 Igors ,

Цитата: Igors
По поводу хеша. Чаще не берут обработчик из контейнера, а делают его копию, т.к. он может быть задействован 2 и более раз. Тогда мапируйтесть как хотите - можно и 2 хеша, прямой и обратный

Тогда уж слишком много new/delete да и дополнительный код нужен. Сейчас также любой обработчик можно задействовать сколько угодно раз.
Просто есть небольшой нюанс в обратном нахождении нужного Reply к которому относится Response (т.к. при прямом нахождении сопоставлении Request-а и Reply нет проблем - там Reply создается).

Цитировать
Все же не лучше ли выделить Command и Event отдельно? Ну будет 2 контейнера, ничего плохого. Зато многие операции можно слить в базовый класс(ы) и схлопнуть текст (хоть с виртуалами, хоть с темплейтпми).

Эмм.. так Command и Event и так отдельно. Или что имеется ввиду?
Записан

ArchLinux x86_64 / Win10 64 bit
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.391 секунд. Запросов: 22.