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

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

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

Сообщений: 11445


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

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

Эмм.. так Command и Event и так отдельно. Или что имеется ввиду?
Сейчас они наследуются от одного класса с виртуалами, сделать от разных. Тогда можно иметь больше общих методов в базовых классах. И контейнеры обработчиков разные, тогда можно их мапами
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #16 : Октябрь 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 также статическими:

Код
C++ (Qt)
static bool FooCommand::isValid(const QByteArray &data);
static QString FooCommand::name(const QByteArray &data);
 
static bool BarCommand::isValid(const QByteArray &data);
static Type BarCommand::type(const QByteArray &data);
 

6. Возможно, что и Reply можно сделать виртуальным, а вместо хранения QByteArray от request/response хранить указатели на конкретные классы от HciCommand / HciEvent ?
Код
C++ (Qt)
class ResetReply : public Reply
{
   ResetCommand *request;
   ResetCommandCompleteEvent *response;
}
 

Возможно, имплементацию получения параметров от Request/Response можно сделать напрямую в каждом конкретном Reply вместо HciCommand / HciEvent
 (где вместо указателей на HciCommand / HciEvent просто хранить принятые сырые массивы).
Код
C++ (Qt)
class FooReply : public Reply
{
   QByteArray request;
   QByteArray response;
 
   QString name() const { return response... }; // извлекаем нужный параметр из какого-то там поля из response
}
 
class BarReply : public Reply
{
   QByteArray request;
   QByteArray response;
 
   QString type() const { return response... }; // извлекаем нужный параметр из какого-то там поля из response
}
 

В этом случае когда Reply завершится достаточно просто скастить его к нужному типу (к FooReply  или BarReply ) и дернуть желаемые методы получения того или иного параметра..
Код
C++ (Qt)
MyClass::onFooComplete(Reply *reply)
{
   // я точно знаю, что слот onFooComplete только для FooReply, поэтому могу кастить
   FooReply *reply = qobject_cast<FooReply *>(reply);
 
   QString name = reply->name();
}
 

вместо того, что есть сейчас:
Код
C++ (Qt)
MyClass::onFooComplete(Reply *reply)
{
   // я точно знаю, что слот onFooComplete только для FooReply, поэтому могу наложить FooCompleteEvent на response
   QByteArray response = reply->response();
 
   FooCompleteEvent ev(response);
 
   QString name = ev.name();
}
 

Но все-равно нужно каким-то образом реализовывать методы canHandleRequest/canHandleResponse для обработчиков..

В общем.. такая вот засада.. Много вопросов, а что выбрать в итоге - непонятно. Улыбающийся
« Последнее редактирование: Октябрь 21, 2014, 13:17 от kuzulis » Записан

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

1. Создаем команду (например Reset) и отправляем ее в менеджер (делаем exec(cmd))

Такое ощущение что ты недавно прочитал какую-то статью про шаблоны, и теперь хочешь шаблонизировать свой код...
Ну бог с ним, хочется тебе (new ResetCommand).exec() или что-то подобное, почему бы и нет. В коде конечно это выглядит очень ООПэшненько и современно Улыбающийся


2. Менеджер ищет нужный обработчик, который успешно вернет canHandleRequest(cmd). Этот обработчик также будет обрабатывать и response (когда оно придет).
Разве менеджер не может выполнить команду? Впрочем если это топ-менеджер, ему нельзя самому выполнять команды. Нужно обязательно найти того кто его сделает.
Гораздо логичнее было бы использовать менеджер не для поиска нужного обработчика, а для непосредственно обработки команды. Обозвать его RequestManager. А вот обработчиком можно сделать ResponceManager, который не будет пытаться брать на себя все обязанности и займется конкретно ответом.


Приходит какой-то response, менеджер перечисляет обработчики и находит тот, который успешно делает canHandleResponse(resp).
Далее, в контейнере (QHash<Reply *. Handler *>) ищется первое совпадение с Handler* и возвращается соответствующий ему Reply.
По факту тот же switch-case.
Нахрена тут хэш мэп?! Менеджер же уже определил кто успешно делает canHandleResponse(resp).


Далее в Reply запихивается response, Reply изымается из контейнера (если надо, это не важно), и финализируется (т.е. емитим сигнал finished и прочее - неважно).
Шокированный


В методах конкретных обработчиков для проверки (мой пакет / чужой пакет) просто на сырой QByteArray накладывается соответствующий класс HciCommand/HciEvent.
Например, в ResetReplyHandler это будет проверка ResetCommand(data).isValid() и ResetCommandCompleteEvent(data).isValid() (для canHandleRequest/canHandleResponse соответственно).
"Вы повысили скилл в индусский кодинг на +5" Улыбающийся
А если серьезно, то гораздо логичнее определить тип данных в массиве сперва, а потом уже выбирать нужный класс для респонса или реквеста и создавать из массива объект.
Логичнее это, просто хотя бы потому, что если сторонний разработчик посмотрит на такой код, он вообще ничего не поймет что за фигня тут происходит.
Не по фен шую это, перебирать типы объектов, прежде чем их создавать. Нужно сперва определить какой тип объекта нужен, а потом его создавать.
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


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

Цитата: vulko
Такое ощущение что ты недавно прочитал какую-то статью про шаблоны, и теперь хочешь шаблонизировать свой код...
Ну бог с ним, хочется тебе (new ResetCommand).exec() или что-то подобное, почему бы и нет. В коде конечно это выглядит очень ООПэшненько и современно

Не, ну а в чем проблема то:?

Код
C++ (Qt)
MyClass::sendFooCommand()
{
   FooCommand cmd(param1, param2, param3);
   Reply *reply = manager->exec(cmd);
   connect(reply, SIGNAL(finished()), this, SLOT(onFooCommandFinished()));
}
 
MyClass::onFooCommandFinished()
{
   Reply *reply = qobject_cast<Reply *>(sender());
 
   FooCompleteEvent ev(reply->response());
 
   if (ev.status() == Success) {
       // do something
   }
 
   QString someName = ev.name() == Success) {
       // do something
   }
 
   reply->deleteLater();
}
 
MyClass::sendBarCommand()
{
   BarCommand cmd(param11, param22, param33);
   Reply *reply = manager->exec(cmd);
   connect(reply, SIGNAL(finished()), this, SLOT(onBarCommandFinished()));
}
 
MyClass::onBarCommandFinished()
{
   Reply *reply = qobject_cast<Reply *>(sender());
 
   BarCompleteEvent ev(reply->response());
 
   if (ev.status() == Success) {
       // do something
   }
 
   Type someType = ev.type();
   // do something
 
   reply->deleteLater();
}
 

предлагаешь тут команды вручную и массива клепать?  Смеющийся
Код
C++ (Qt)
MyClass::sendBarCommand()
{
   QByteArray cmd;
   cmd.append(bar_header);
   cmd.append(bar_body);
   cmd.append(bar_tail);
 
   requestManager->write(cmd);
   // И? что дальше?
}
 


Цитата: vulko
Разве менеджер не может выполнить команду? Впрочем если это топ-менеджер, ему нельзя самому выполнять команды. Нужно обязательно найти того кто его сделает.

Ухаха, обхохочешься.
Он и выполняет: шлет ее в у-во. Но также он ищет обработчика чтобы привязать его к Reply чтобы потом обработать ответ и запихнуть его в нужный Reply.

Цитата: vulko
По факту тот же switch-case.

Тот же да не тот же.

Цитата: vulko
Нахрена тут хэш мэп?!

Да пофик, просто для примера.

Цитата: vulko
Менеджер же уже определил кто успешно делает canHandleResponse(resp).

Да, он нажел указатель того обработчика который это сделает. Но теперь нужно найти тот Response которому нужно запихнуть результат.
Это по дизайну.

Цитата: kuzulis
Далее в Reply запихивается response, Reply изымается из контейнера (если надо, это не важно), и финализируется (т.е. емитим сигнал finished и прочее - неважно).

Что вызвало такое удивление?

Цитата: vulko
А если серьезно, то гораздо логичнее определить тип данных в массиве сперва, а потом уже выбирать нужный класс для респонса или реквеста и создавать из массива объект.

Неужели? Каким же образом определять "тип данных" ?

Цитата: vulko
Логичнее это, просто хотя бы потому, что если сторонний разработчик посмотрит на такой код, он вообще ничего не поймет что за фигня тут происходит.
Не по фен шую это, перебирать типы объектов, прежде чем их создавать. Нужно сперва определить какой тип объекта нужен, а потом его создавать.

Никто не создает объектов. Ищется обработчик для соответствующего request/response и "биндится" к Reply.
И все как раз-таки и понятно, в отличии от switch/case
Записан

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

Цитировать
предлагаешь тут команды вручную и массива клепать?

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

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

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


Цитировать
Ухаха, обхохочешься.
Он и выполняет: шлет ее в у-во. Но также он ищет обработчика чтобы привязать его к Reply чтобы потом обработать ответ и запихнуть его в нужный Reply.
Зачем привязывать обработчика к Reply? Какой отношение к Reply-ю имеет обработчик? Это обработчик на выходе должен выдавать объект типа Reply, а не создавать сперва Reply, на кой то хрен привязывать к нему обработчика, который потом его инициализирует.
Или ты прежде чем в туалет (отдельный от ванной) пойти, берешь сперва мыло в ванной, чтобы потом пойти снова в ванную и помыть там руки?


Цитировать
Неужели? Каким же образом определять "тип данных" ?
читай документацию к устройству, там написано чего оно шлет.
ну а если имеется ввиду "как же мне запихать все в кривую архитектуру?", то я уже не раз говорил. для таких случаев есть switch-case или if else if.


Цитировать
Никто не создает объектов. Ищется обработчик для соответствующего request/response и "биндится" к Reply.
И все как раз-таки и понятно, в отличии от switch/case
ты сперва опиши суть вопроса или проблемы нормально. непонятно что есть, что должно быть.

ну а если все понятно, то к чему вопросы на форуме?)
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


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

5. В методах конкретных обработчиков для проверки (мой пакет / чужой пакет) просто на сырой QByteArray накладывается соответствующий класс HciCommand/HciEvent.
Например, в ResetReplyHandler это будет проверка ResetCommand(data).isValid() и ResetCommandCompleteEvent(data).isValid() (для canHandleRequest/canHandleResponse соответственно).
То есть структура полей не имеет, но "знает" что по такому-то смещению в QByteArray лежит то-то? Ну пока не видно резонов от этого отказываться.

Т.е. изначальный мой вопрос был как корректнее можно унифицировать эту проверку, т.к. можно ее также делать с помощью статических методов: ResetCommand::isValid(data) ...
Так а что статические методы - все равно это руками "позвать каждый". Давайте конкретнее, напр "вот 2 класса которые неприятно "плодятся", отличаются только проверкой(ами)". А то советы будут слишком общими.

[off]Не тратьте слова на того мудака [off]
Записан
vulko
Гость
« Ответ #21 : Октябрь 21, 2014, 15:35 »

Джедай, ротик прикрой и за языком следи!

Слишком много постов с дурацкими советами! Ничего не знаешь, но пытаешься строить из себя эксперта.
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


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

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

Да, каждая конкретная структура/класс унаследованная от HciCommand или HciEvent содержит только raw QByteArray и знает
только свои специфичные смешения по которым лежат свои специфичные данные.

Поэтому получение нужных параметров (как и проверка валидности) сводится только по дерганью конкретных методов
этих классов которые "тупо" читают из массива по смещениям. И все.

Т.е. "наложив" по очереди каждый класс на QByteArray можно узнать для кого этот QByteArray предназначен.

Цитата: Igors
Так а что статические методы - все равно это руками "позвать каждый".

Да, то-же самое. Просто разнича в том, что нет оверхеда, т.к. нет конструктора копирования как при "наложении" класса на QByteArray.

Цитата: Igors
Давайте конкретнее, напр "вот 2 класса которые неприятно "плодятся", отличаются только проверкой(ами)". А то советы будут слишком общими.

Все классы разные - имеют разное кол-во параметров.

Для HciCommands (и всех их наследников) можно привязаться только к первому opcode, но следующие поля параметров могут быть какими угодно.

Для HciEvents (и всех их наследников) можно привязаться только к первому полю EventCode, но следующие поля параметров могут быть какими угодно.
Например, в евентах, которые являются ответами на команды, в поле params будет содержаться и opcode той команды на которую девайс ответил (см. выше пример с дампом).
Но могут быть и спонтанные евенты, которые отличаются как EventCode так и содержимым.

Т.е. плодятся как-бы все классы Улыбающийся
« Последнее редактирование: Октябрь 21, 2014, 18:33 от kuzulis » Записан

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

Сообщений: 11445


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

Если действие уникально, то это просто метод класса, это нормально. Обобщать/схлопывать надо то что повторяется. Тут не пойму: говорите что HciCommand и HciEvent разные классы - тогда почему не слить OpCode/EventCode в базовые классы?  Вообще где возникает проблема повторов/избыточности? И возникает ли она?

Во-вторых, может сделать команды более вумными? Напр сейчас есть Manager. Он получил экземпляр команды, но ему все время надо учитывать "а что это за команда", методы одной не имеют смысла для другой. Может лучше дать эту работу самому классу команды (напр метод exec)? Она-то точно знает что делать. Если ей понадобятся какие-то данные - подать их залив нужные указатели в структуру (часто с именем типа "контекст")
Записан
Страниц: 1 [2]   Вверх
  Печать  
 
Перейти в:  


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