Название: С++ парсинг бинарных структур из байтового массива Отправлено: kuzulis от Октябрь 19, 2014, 19:06 Всем доброго времени.
Какой самый "лучший" способ проверки принадлежности того или иного принятого QByteArray к тому или иному типу структуры? Например, принимаю пакет/структуру с полями типа: Цитировать opcode=2 byte, len=1 byte, params = x bytes где, opcode - код операции; len - размер поля params; params - вариабельное поле, которое имеет разную длину и содержимое в зависимости от opcode Я пока знаю два метода: 1) "Конструирования" заданной структуры из QByteArray Код
Тогда в обработчиках для каждого пакета можно сделать проверки: Код
2) Создания набора статических методов: Код
Тогда в обработчиках для каждого пакета можно сделать проверки: Код
Что предпочтительнее? или есть иной простой способ как сделать желаемое ? :) ЗЫ: естественно интересуют всякие там паттерны; чисто Си-шное решение с switch/case и прочим не предлагать. ЗЫЗЫ: вроде как использование статических методов не дает оверхеда (нет копирования), но с другой стороны метод isValid() можно сделать inline... ЗЫЗЫЗЫ: уже ломаю мозг неделю :) Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: vulko от Октябрь 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... Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: Igors от Октябрь 20, 2014, 09:39 Код
Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: Igors от Октябрь 20, 2014, 11:02 Дальше насаживаем этот template на виртуальный базовый
Код Создаем хвабрику.. Код Использование: по коду операции находим нужный класс Код Т.е. классов все равно "много", но их не надо размазывать каждый, один раз на создании и все Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: kuzulis от Октябрь 20, 2014, 11:12 2 vulko,
Цитата: vulko len тут совершенно не нужен. по opcode можно точно определить размер структуры упакованной в массив байт. Не, это не от меня зависит. Этот формат описан в спецификации. Цитата: vulko Зачем такая дикая имплементация со структурой внутри которой QByteArray не понимаю. Чтобы можно было потом сделать: Код
Дело в том, что у каждого пакета разное содержимое поля params. Например, у пакета FooPacket это поле содержит параметр Name; у ракета BarPacket два параметра: тип и адрес и т.п. Т.е. эти все разбиения на разные классы для того, чтобы можно было "тупо" наложить класс на пакет, проверить его (пакета валидность, т.е. тот факт, что пакет имеет именно текущий тип), а потом изъять (распарсить) необходимые параметры из пакета. Т.е. идея в том, что только конкретные классы пакетов знают как извлекать свои параметры из RAW пакета. :) 2 Igors, Я не могу использовать QDataStream, т.к. принимаю пакеты не из Qt-шного приложения. Но должен их парсить методами Qt. :) Цитата: Igors Т.е. классов все равно "много", но их не надо размазывать каждый, один раз на создании и все В смысле? Мне не нужно ничего создавать. Мне нужно просто "распарсить" принятый пакет (QByteArray) и в зависимости от его принадлежности извлечь необходимые параметры. :) Тут по идее должна быть список с конкретными обработчиками, каждый из которых "реагирует" только на определенный пакет. Код
Но тут главный вопрос: как красиво парсить QByteArray и приводить к определенному типу чтобы можно было легко изъять нужные параметры из QByteArray. Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: Fregloin от Октябрь 20, 2014, 12:03 С таким сталкивался при реализации чтения/записи в universal binary json.
Можете посмотреть как это сделано у меня. Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: Igors от Октябрь 20, 2014, 12:26 Код
Код А OpCode - подскоком из темплейта (см выше) Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: vulko от Октябрь 20, 2014, 15:33 Чтобы можно было потом сделать: Код
Дело в том, что у каждого пакета разное содержимое поля params. Например, у пакета FooPacket это поле содержит параметр Name; у ракета BarPacket два параметра: тип и адрес и т.п. Т.е. эти все разбиения на разные классы для того, чтобы можно было "тупо" наложить класс на пакет, проверить его (пакета валидность, т.е. тот факт, что пакет имеет именно текущий тип), а потом изъять (распарсить) необходимые параметры из пакета. Т.е. идея в том, что только конкретные классы пакетов знают как извлекать свои параметры из RAW пакета. :) Я понял в чем идея. Но я не вижу ни одной причины чтобы заниматься такой фигней... Проверить пакет на валидность можно ведь и без излишней ООПтимизации. Ну есть у вас 20 классов, по одному на каждый тип пакета. Один фиг каждый из них при проверке на валидность проверять будет тупо ID пакета (опкод он у вас называется). При этом у вас куча однообразного кода, который будет занимать много места. Да, типо ООП. Да, типа шаблоны применил. Только профита никакого. Если нужно красивое внешнее АПИ, не проблема его сделать. Но зачем изобретать такой велосипед для простой задачи, которая где-то внутри проекта находится, я не понимаю. Это ведь не просто много времени на копи паст и переделывание классов наследников - это потенциальный гемор при тестировании. Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: vulko от Октябрь 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. Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: kuzulis от Октябрь 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 - статус выполнения команды т.е. сейчас реализовано по такому типу: Код
т.е. по-любому нужно плодить классы для каждой из команд/события. Может быть, ДА, где -то применить шаблоны. т.е. не прокатит просто так switch/case и прочее.. PS: В общем, портирую Bluez на Windows (HCI стек) для своего проекта :D Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: Igors от Октябрь 20, 2014, 18:05 т.е. по-любому нужно плодить классы для каждой из команд/события. Может быть, ДА, где -то применить шаблоны. Да понятно, случай-то нередкий. Создается по экземпляру каждого обработчика и складывается в контейнер. Когда пришел QByteArray - перебираем все обработчики контейнера, какой-то ответит "да, это мой пакет". Если никто не ответит - ну инвалидный пакет. Да, каждый обработчик свой класс, ничего страшного. То есть все сводится к тому как удобно реализовать эти классы. Напр создаем базовый класс с чистыми виртуалами и наследуемся. Это вполне нормально, но общности между различными обработчиками может оказаться мало. Напр поместив поле OpCode в базовый класс - что тогда с пакетами Event? Поэтому делают "просто классы" - с темплейтами/наследованием или без, как получится. Структуры данных все равно надо где-то определять. А потом пристегивают общую виртуальную базу (иначе не выскочить на нужный обработчик). Напр где: 0e0401030c00 Ну и рисуете такую структуру, удастся сэкономить на template или наследованием на подобной - хорошо, нет - переживем. Создаете методы чтения/записи из QByteArray. А дальше добавляете базовые виртуалы, напр canHandle который просто вызывает метод чтения и возвращает код ошибки. Обычно удается все на хедерах (без .cpp файлов)- 0e - код евента что это евент завершения команды - 04 - len длина поля параметры - 01030c00 - параметры: - 01 - некий код (неважно) - 030c - opcod команды на которую этот евент - 00 - статус выполнения команды Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: kuzulis от Октябрь 20, 2014, 19:20 Цитата: Igors Да понятно, случай-то нередкий. Создается по экземпляру каждого обработчика и складывается в контейнер. Когда пришел QByteArray - перебираем все обработчики контейнера, какой-то ответит "да, это мой пакет". Если никто не ответит - ну инвалидный пакет. Да, каждый обработчик свой класс, ничего страшного. Ага. Цитата: Igors То есть все сводится к тому как удобно реализовать эти классы. Напр создаем базовый класс с чистыми виртуалами и наследуемся. Это вполне нормально, но общности между различными обработчиками может оказаться мало. Напр поместив поле OpCode в базовый класс - что тогда с пакетами Event? Сейчас обработчики имеют по два абстрактных метода: Код
метод exec() менеджера такой: Код
Где m_pendingReplies - это хеш, где для каждого созданного Reply присваивается соответствующий ему обработчик. Для того чтобы в слоте обработки (см. далее), можно было найти Reply по его обработчику (ничего лучше я пка не придумал).. Слот отбаботки ответа менеджера такой (т.е. этот слот дергается по приходу события): Код
Реализация команды/события/обработчика Reset для примера такая: Код
Блин, больше ничего толковее я не придумал. :( Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: vulko от Октябрь 21, 2014, 08:17 2 vulko, Да елки, ну не применить в моем случае QDataStream, ну никак. :) Удивительно, что QByteArray есть, а QDataStream нельзя применить...))) Это всего лишь удобный wrapper для QByteArray (и не только), созданный для удобной сериализации/десереализации данных. Цитировать далее MyStruct *struct = (MyStruct*) data; Тоже не вариант: нужно делать "pragma pack" и прочую фигню. Гемор еще больший. Совсем не обязательно. Зависит от того как паковали. А даже если нужно будь изменить паковку структуры, то никакого гемора нет. Гемор это как раз плодить никому не нужные классы в низкоуровневом АПИ. Код: #pragma pack(push, 1) Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: Igors от Октябрь 21, 2014, 09:16 По поводу хеша. Чаще не берут обработчик из контейнера, а делают его копию, т.к. он может быть задействован 2 и более раз. Тогда мапируйтесть как хотите - можно и 2 хеша, прямой и обратный
Насчет размазывания классов обработчиков. Ну да, выглядит неск занудно, но вполне цивильно. Все же не лучше ли выделить Command и Event отдельно? Ну будет 2 контейнера, ничего плохого. Зато многие операции можно слить в базовый класс(ы) и схлопнуть текст (хоть с виртуалами, хоть с темплейтпми). Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: kuzulis от Октябрь 21, 2014, 11:09 2 vulko,
Цитата: vulko Удивительно, что QByteArray есть, а QDataStream нельзя применить...))) Это всего лишь удобный wrapper для QByteArray (и не только), созданный для удобной сериализации/десереализации данных. Ничего удивительного, почитайте для чего нужен QDataStream и когда можно, а когда нельзя его использовать. Цитата: vulko Совсем не обязательно. Зависит от того как паковали. Кого как паковали? Работа идет с массивами данных / пакетами. Никого никуда не паковали. Цитата: vulko Гемор это как раз плодить никому не нужные классы в низкоуровневом АПИ. Код: #pragma pack(push, 1) А вот это уже как раз-таки гемор. 2 Igors , Цитата: Igors По поводу хеша. Чаще не берут обработчик из контейнера, а делают его копию, т.к. он может быть задействован 2 и более раз. Тогда мапируйтесть как хотите - можно и 2 хеша, прямой и обратный Тогда уж слишком много new/delete да и дополнительный код нужен. Сейчас также любой обработчик можно задействовать сколько угодно раз. Просто есть небольшой нюанс в обратном нахождении нужного Reply к которому относится Response (т.к. при прямом нахождении сопоставлении Request-а и Reply нет проблем - там Reply создается). Цитировать Все же не лучше ли выделить Command и Event отдельно? Ну будет 2 контейнера, ничего плохого. Зато многие операции можно слить в базовый класс(ы) и схлопнуть текст (хоть с виртуалами, хоть с темплейтпми). Эмм.. так Command и Event и так отдельно. Или что имеется ввиду? Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: Igors от Октябрь 21, 2014, 11:44 Тогда уж слишком много new/delete да и дополнительный код нужен. Сейчас также любой обработчик можно задействовать сколько угодно раз. При таком подходе обработчик не может ничего хранить в себе. Если известен момент "окончания сессии" - то просто грохнуть "контейнер копий". Ну здесь конечно по задачеЭмм.. так Command и Event и так отдельно. Или что имеется ввиду? Сейчас они наследуются от одного класса с виртуалами, сделать от разных. Тогда можно иметь больше общих методов в базовых классах. И контейнеры обработчиков разные, тогда можно их мапамиНазвание: Re: С++ парсинг бинарных структур из байтового массива Отправлено: kuzulis от Октябрь 21, 2014, 13:07 Цитата: Igors Сейчас они наследуются от одного класса с виртуалами, сделать от разных. Тогда можно иметь больше общих методов в базовых классах. И контейнеры обработчиков разные, тогда можно их мапами Нет, HciCommand и HciEvent - это два разных базового класса. От HciCommand наследуется ResetCommand, а от HciEvent наследуется ResetCommandCompleteEvent (если я правильно понял твой комментарий). Есть также две дополнителных сущности: * Reply - что-то типа хендла операции, к которому можно приконнектится и прочее (по аналогии с QNetworkReply). Этот Reply наследуется от QObject и содержит QByteArray как от request (HciCommand) так и от response (HciEvent). * ReplyHandler - это базовый обработчик с виртуалами которые должны обрабатывать (проверять, что могут это делать) как исходяшие запросы так и входяшие ответы. Для каджого типа запроса/ответа - свой обработчик, который принимает только свои пакеты. Т.е. алгоритм такой: 1. Создаем команду (например Reset) и отправляем ее в менеджер (делаем exec(cmd)) 2. Менеджер ищет нужный обработчик, который успешно вернет canHandleRequest(cmd). Этот обработчик также будет обрабатывать и response (когда оно придет). Далее, создается Reply в который засовываем команду (на хранение), а далее засоваваем этот Reply с указателем на текущий обработчик в контейнер (QHash<Reply *. Handler *>). Это надо для того чтобы потом (когда придет response и сработает тот-же обработчик) найти Reply. 3. Приходит какой-то response, менеджер перечисляет обработчики и находит тот, который успешно делает canHandleResponse(resp). Далее, в контейнере (QHash<Reply *. Handler *>) ищется первое совпадение с Handler* и возвращается соответствующий ему Reply. 4. Далее в Reply запихивается response, Reply изымается из контейнера (если надо, это не важно), и финализируется (т.е. емитим сигнал finished и прочее - неважно). 5. В методах конкретных обработчиков для проверки (мой пакет / чужой пакет) просто на сырой QByteArray накладывается соответствующий класс HciCommand/HciEvent. Например, в ResetReplyHandler это будет проверка ResetCommand(data).isValid() и ResetCommandCompleteEvent(data).isValid() (для canHandleRequest/canHandleResponse соответственно). Т.е. изначальный мой вопрос был как корректнее можно унифицировать эту проверку, т.к. можно ее также делать с помощью статических методов: ResetCommand::isValid(data) / ResetCommandCompleteEvent::isValid(data) вместо "наложения" класса на QByteArray. Хотя, также пришлось бы делать и другие методы (для извлечения соотв. свойств из QByteArray) для классов - наследников HciCommand и HciEvent также статическими: Код
6. Возможно, что и Reply можно сделать виртуальным, а вместо хранения QByteArray от request/response хранить указатели на конкретные классы от HciCommand / HciEvent ? Код
Возможно, имплементацию получения параметров от Request/Response можно сделать напрямую в каждом конкретном Reply вместо HciCommand / HciEvent (где вместо указателей на HciCommand / HciEvent просто хранить принятые сырые массивы). Код
В этом случае когда Reply завершится достаточно просто скастить его к нужному типу (к FooReply или BarReply ) и дернуть желаемые методы получения того или иного параметра.. Код
вместо того, что есть сейчас: Код
Но все-равно нужно каким-то образом реализовывать методы canHandleRequest/canHandleResponse для обработчиков.. В общем.. такая вот засада.. Много вопросов, а что выбрать в итоге - непонятно. :) Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: vulko от Октябрь 21, 2014, 13:50 1. Создаем команду (например Reset) и отправляем ее в менеджер (делаем exec(cmd)) Такое ощущение что ты недавно прочитал какую-то статью про шаблоны, и теперь хочешь шаблонизировать свой код... Ну бог с ним, хочется тебе (new ResetCommand).exec() или что-то подобное, почему бы и нет. В коде конечно это выглядит очень ООПэшненько и современно :) 2. Менеджер ищет нужный обработчик, который успешно вернет canHandleRequest(cmd). Этот обработчик также будет обрабатывать и response (когда оно придет). Разве менеджер не может выполнить команду? Впрочем если это топ-менеджер, ему нельзя самому выполнять команды. Нужно обязательно найти того кто его сделает.Гораздо логичнее было бы использовать менеджер не для поиска нужного обработчика, а для непосредственно обработки команды. Обозвать его RequestManager. А вот обработчиком можно сделать ResponceManager, который не будет пытаться брать на себя все обязанности и займется конкретно ответом. Приходит какой-то response, менеджер перечисляет обработчики и находит тот, который успешно делает canHandleResponse(resp). По факту тот же switch-case.Далее, в контейнере (QHash<Reply *. Handler *>) ищется первое совпадение с Handler* и возвращается соответствующий ему Reply. Нахрена тут хэш мэп?! Менеджер же уже определил кто успешно делает canHandleResponse(resp). Далее в Reply запихивается response, Reply изымается из контейнера (если надо, это не важно), и финализируется (т.е. емитим сигнал finished и прочее - неважно). :oВ методах конкретных обработчиков для проверки (мой пакет / чужой пакет) просто на сырой QByteArray накладывается соответствующий класс HciCommand/HciEvent. "Вы повысили скилл в индусский кодинг на +5" :)Например, в ResetReplyHandler это будет проверка ResetCommand(data).isValid() и ResetCommandCompleteEvent(data).isValid() (для canHandleRequest/canHandleResponse соответственно). А если серьезно, то гораздо логичнее определить тип данных в массиве сперва, а потом уже выбирать нужный класс для респонса или реквеста и создавать из массива объект. Логичнее это, просто хотя бы потому, что если сторонний разработчик посмотрит на такой код, он вообще ничего не поймет что за фигня тут происходит. Не по фен шую это, перебирать типы объектов, прежде чем их создавать. Нужно сперва определить какой тип объекта нужен, а потом его создавать. Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: kuzulis от Октябрь 21, 2014, 14:32 Цитата: vulko Такое ощущение что ты недавно прочитал какую-то статью про шаблоны, и теперь хочешь шаблонизировать свой код... Ну бог с ним, хочется тебе (new ResetCommand).exec() или что-то подобное, почему бы и нет. В коде конечно это выглядит очень ООПэшненько и современно Не, ну а в чем проблема то:? Код
предлагаешь тут команды вручную и массива клепать? ;D Код
Цитата: vulko Разве менеджер не может выполнить команду? Впрочем если это топ-менеджер, ему нельзя самому выполнять команды. Нужно обязательно найти того кто его сделает. Ухаха, обхохочешься. Он и выполняет: шлет ее в у-во. Но также он ищет обработчика чтобы привязать его к Reply чтобы потом обработать ответ и запихнуть его в нужный Reply. Цитата: vulko По факту тот же switch-case. Тот же да не тот же. Цитата: vulko Нахрена тут хэш мэп?! Да пофик, просто для примера. Цитата: vulko Менеджер же уже определил кто успешно делает canHandleResponse(resp). Да, он нажел указатель того обработчика который это сделает. Но теперь нужно найти тот Response которому нужно запихнуть результат. Это по дизайну. Цитата: kuzulis Далее в Reply запихивается response, Reply изымается из контейнера (если надо, это не важно), и финализируется (т.е. емитим сигнал finished и прочее - неважно). Что вызвало такое удивление? Цитата: vulko А если серьезно, то гораздо логичнее определить тип данных в массиве сперва, а потом уже выбирать нужный класс для респонса или реквеста и создавать из массива объект. Неужели? Каким же образом определять "тип данных" ? Цитата: vulko Логичнее это, просто хотя бы потому, что если сторонний разработчик посмотрит на такой код, он вообще ничего не поймет что за фигня тут происходит. Не по фен шую это, перебирать типы объектов, прежде чем их создавать. Нужно сперва определить какой тип объекта нужен, а потом его создавать. Никто не создает объектов. Ищется обработчик для соответствующего request/response и "биндится" к Reply. И все как раз-таки и понятно, в отличии от switch/case Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: vulko от Октябрь 21, 2014, 15:03 Цитировать предлагаешь тут команды вручную и массива клепать? нет, я предлагаю начать с того чтобы подумать над нормальной архитектурой. то что ты пытаешься сделать, больше похоже на попытку впихнуть побольше шаблонов и ненужных контейнеров. архитектура должна быть предельно проста, понятна и удовлетворять задаче. и вот уже "вокруг" архитектуры, когда она ясна, можно соорудить какие-то шаблоны, контейнеры и т.п. Цитировать Ухаха, обхохочешься. Зачем привязывать обработчика к Reply? Какой отношение к Reply-ю имеет обработчик? Это обработчик на выходе должен выдавать объект типа Reply, а не создавать сперва Reply, на кой то хрен привязывать к нему обработчика, который потом его инициализирует.Он и выполняет: шлет ее в у-во. Но также он ищет обработчика чтобы привязать его к Reply чтобы потом обработать ответ и запихнуть его в нужный Reply. Или ты прежде чем в туалет (отдельный от ванной) пойти, берешь сперва мыло в ванной, чтобы потом пойти снова в ванную и помыть там руки? Цитировать Неужели? Каким же образом определять "тип данных" ? читай документацию к устройству, там написано чего оно шлет.ну а если имеется ввиду "как же мне запихать все в кривую архитектуру?", то я уже не раз говорил. для таких случаев есть switch-case или if else if. Цитировать Никто не создает объектов. Ищется обработчик для соответствующего request/response и "биндится" к Reply. ты сперва опиши суть вопроса или проблемы нормально. непонятно что есть, что должно быть.И все как раз-таки и понятно, в отличии от switch/case ну а если все понятно, то к чему вопросы на форуме?) Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: Igors от Октябрь 21, 2014, 15:17 5. В методах конкретных обработчиков для проверки (мой пакет / чужой пакет) просто на сырой QByteArray накладывается соответствующий класс HciCommand/HciEvent. То есть структура полей не имеет, но "знает" что по такому-то смещению в QByteArray лежит то-то? Ну пока не видно резонов от этого отказываться.Например, в ResetReplyHandler это будет проверка ResetCommand(data).isValid() и ResetCommandCompleteEvent(data).isValid() (для canHandleRequest/canHandleResponse соответственно). Т.е. изначальный мой вопрос был как корректнее можно унифицировать эту проверку, т.к. можно ее также делать с помощью статических методов: ResetCommand::isValid(data) ... Так а что статические методы - все равно это руками "позвать каждый". Давайте конкретнее, напр "вот 2 класса которые неприятно "плодятся", отличаются только проверкой(ами)". А то советы будут слишком общими.[off]Не тратьте слова на того мудака [off] Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: vulko от Октябрь 21, 2014, 15:35 Джедай, ротик прикрой и за языком следи!
Слишком много постов с дурацкими советами! Ничего не знаешь, но пытаешься строить из себя эксперта. Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: kuzulis от Октябрь 21, 2014, 18:18 Цитата: Igors То есть структура полей не имеет, но "знает" что по такому-то смещению в QByteArray лежит то-то? Ну пока не видно резонов от этого отказываться. Да, каждая конкретная структура/класс унаследованная от HciCommand или HciEvent содержит только raw QByteArray и знает только свои специфичные смешения по которым лежат свои специфичные данные. Поэтому получение нужных параметров (как и проверка валидности) сводится только по дерганью конкретных методов этих классов которые "тупо" читают из массива по смещениям. И все. Т.е. "наложив" по очереди каждый класс на QByteArray можно узнать для кого этот QByteArray предназначен. Цитата: Igors Так а что статические методы - все равно это руками "позвать каждый". Да, то-же самое. Просто разнича в том, что нет оверхеда, т.к. нет конструктора копирования как при "наложении" класса на QByteArray. Цитата: Igors Давайте конкретнее, напр "вот 2 класса которые неприятно "плодятся", отличаются только проверкой(ами)". А то советы будут слишком общими. Все классы разные - имеют разное кол-во параметров. Для HciCommands (и всех их наследников) можно привязаться только к первому opcode, но следующие поля параметров могут быть какими угодно. Для HciEvents (и всех их наследников) можно привязаться только к первому полю EventCode, но следующие поля параметров могут быть какими угодно. Например, в евентах, которые являются ответами на команды, в поле params будет содержаться и opcode той команды на которую девайс ответил (см. выше пример с дампом). Но могут быть и спонтанные евенты, которые отличаются как EventCode так и содержимым. Т.е. плодятся как-бы все классы :) Название: Re: С++ парсинг бинарных структур из байтового массива Отправлено: Igors от Октябрь 21, 2014, 19:51 Если действие уникально, то это просто метод класса, это нормально. Обобщать/схлопывать надо то что повторяется. Тут не пойму: говорите что HciCommand и HciEvent разные классы - тогда почему не слить OpCode/EventCode в базовые классы? Вообще где возникает проблема повторов/избыточности? И возникает ли она?
Во-вторых, может сделать команды более вумными? Напр сейчас есть Manager. Он получил экземпляр команды, но ему все время надо учитывать "а что это за команда", методы одной не имеют смысла для другой. Может лучше дать эту работу самому классу команды (напр метод exec)? Она-то точно знает что делать. Если ей понадобятся какие-то данные - подать их залив нужные указатели в структуру (часто с именем типа "контекст") |