|
|
  |
Инкрементирующийся define, c помощью препроцессор Си |
|
|
|
Jul 10 2016, 06:40
|
Профессионал
    
Группа: Свой
Сообщений: 1 468
Регистрация: 28-03-10
Из: Беларусь
Пользователь №: 56 256

|
Здравствуйте. Написан конечный автомат на конструкции switch - case. Для перехода к последующему действию просто инкрементируется указатель: Код switch(i) { case 0: ... i++; case 1: ... i++; ... case N: ... i++; } Действий порядка 100 и возникла проблема с добавлением новых case-обработчиков в середину конструкции, т.к. приходится увеличивать все последующие номера case. Хочется перенести эту задачу на плечи препроцессора и оформить конструкцию таким образом, чтобы название макроопределения case было у всех одинаковым и, в тоже время, каждое последующее значение было на 1 больше предыдущего: Код switch(i) { case INCNAME: ... i++; case INCNAME: ... i++; ... case INCNAME: ... i++; } Подскажите, пожалуйста, возможно ли это как-то реализовать? Спасибо.
|
|
|
|
|
Jul 10 2016, 06:49
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Так получится? Код int j = 0;
if(0){} else if((j++) && (i == j)) { // do 1 i++; } else if((j++) && (i == j)) { // do 2 i++; } ... else if((j++) && (i == j)) { // do N i++; } Код #define NEXT else if((j++) && (i==j))
if(0){} NEXT { } NEXT { }
|
|
|
|
|
Jul 10 2016, 07:27
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
"В лоб" задача решается применением enum. CODE typedef enum { IDLE, START, RUN, STOP } state_t;
state_t State;
switch (State) { case IDLE: ..... State = START; break;
case START: .... В середину такого конечного автомата легко добавляется любое количество новых состояний. Конечно, придумать около 100 названий для сотояний и не сорваться на использование цифр в этих названиях - задача непростая.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jul 10 2016, 07:30
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Alt.F4 @ Jul 10 2016, 12:40)  Действий порядка 100 и возникла проблема с добавлением новых case-обработчиков в середину конструкции, т.к. приходится увеличивать все последующие номера case. Хочется перенести эту задачу на плечи препроцессора и оформить конструкцию таким образом, чтобы название макроопределения case было у всех одинаковым и, в тоже время, каждое последующее значение было на 1 больше предыдущего: Значения после case должны быть разными. Иначе компилятор просто не станет компилить. Я в подобных случаях использую enum. При добавлении хоть в середину хоть в конец нет проблем с перенумерацией. enum { //фазы автомата состояний INCNAME_SOST0, INCNAME_SOST1, INCNAME_SOST2, ...}; Цитата(adnega @ Jul 10 2016, 12:49)  Так получится? else if((j++) && (i == j)) ... Не компилил, но почти уверен, что на такую конструкцию компилятор скажет: "Undefined behavior".
|
|
|
|
|
Jul 10 2016, 07:47
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(jcxz @ Jul 10 2016, 10:30)  Не компилил, но почти уверен, что на такую конструкцию компилятор скажет: "Undefined behavior". Компилируется даже без предупреждений. Единственное, в {} не должно быть i++. Лучше new_i++, а в самом конце i = new_i. Иначе автомат выстрелит очередью.
|
|
|
|
|
Jul 10 2016, 08:19
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(Alt.F4 @ Jul 10 2016, 09:40)  Подскажите, пожалуйста, возможно ли это как-то реализовать? Спасибо. Создаете в Excel одну строку с одним макроопределением. Потом протягиваете ее до 100 строк и получаете идеальный инкремент. Если нужно вставить что-то в середину, то в вставляете в Excel-е и снова протягиваете, получите новый инкремент. Потом Copy-Paste из Excel-а
А числа на макросы заменяются автоматически с использованием регулярного выражения.
|
|
|
|
|
Jul 10 2016, 08:38
|
Частый гость
 
Группа: Участник
Сообщений: 168
Регистрация: 14-02-10
Пользователь №: 55 490

|
Подход с enum, несомненно, правилен. Однако, при добавлении состояний, потребуется редактировать в двух местах. Код #define INCNAME __COUNTER__ Только надо предусмотреть ловушку какую на случай если __COUNTER__ был ранее использован. https://gcc.gnu.org/onlinedocs/cpp/Common-P...edefined-Macros
--------------------
#define TRUE (4==(2*2))
|
|
|
|
|
Jul 10 2016, 09:54
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(zltigo @ Jul 10 2016, 12:49)  Что говорит о том, что решение поставленной задачи только с помощью конечного автомата зашло в тупик  . Думайте о комбинации методов. Да нормально, есть прикладные протоколы с очень большими словарями. Только вот enum для этого негодное средство, поскольку при отладке невозможно узнать значение макроса и приходится все равно либо в комментариях либо прямым присваиванием вписывать числа.
|
|
|
|
|
Jul 10 2016, 10:26
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(AlexandrY @ Jul 10 2016, 12:54)  Да нормально, есть прикладные протоколы с очень большими словарями. Не путайте протоколы с их РЕАЛИЗАЦИЕЙ через анус. Сотня состояний конечного автомата это уже анус. Цитата(AlexandrY @ Jul 10 2016, 12:54)  Только вот enum для этого негодное средство, поскольку при отладке... Отладка сколь-нибудь серьезных ПРОТОКОЛОВ отладчиком есть абсолютно непригодное средство, ибо все проблемы взаимодействия оборудования раснообразных производителей начнутся не тогда, когда все лежит на столе. Так что логирование состояния конечного автомата обязательно, ну а при логировании разворачивание названий состояний в текст так же обязательно. Все это решается через общую функцию изменения состояния автомата. Например: Код void q921_set_state( LAPD_t *ldp, ulong new_state ) { ldp->state = new_state; dprintf( "\tL%1X:Set State`%s`(Cmd:%4X)\r", ldp->ch, q2_states[ldp->state], ldp->q921_cmd ); }
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jul 10 2016, 11:49
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(zltigo @ Jul 10 2016, 13:26)  Не путайте протоколы с их РЕАЛИЗАЦИЕЙ через анус. Сотня состояний конечного автомата это уже анус. Отладка сколь-нибудь серьезных ПРОТОКОЛОВ отладчиком есть абсолютно непригодное средство, ибо все проблемы взаимодействия оборудования раснообразных производителей начнутся не тогда, когда все лежит на столе. Прямо сейчас у меня в работе протокол с 41-им case в парсере. Линейная структура кода позволяет легко его форматировать и рефакторить. Регулярные выражения замены легче писать под такую структуру кода. Работает она быстрее. Короче одни плюсы. Протоколы только отладчиком через SWD и можно детально отладить. Логи для протоколов - это во первых медленно, во вторых менее информативно, в третьих сами вносят нежелательный эффект в виде роста стека задач и ухудшения реалтайма.
|
|
|
|
|
Jul 10 2016, 19:12
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(zltigo @ Jul 10 2016, 15:49)  Что говорит о том, что решение поставленной задачи только с помощью конечного автомата зашло в тупик  . Можно сделать завуалированный конечный автомат, без огромных перечислений enum. На стеке. Будет что-то типа задачи со своим стеком, очередное состояние - это собственно адрес. Вход в автомат - это переключение на его стек, выход (там где был break) - обратное переключение на исходный стек. Очень удобно отлаживать. Но конечно требуется некоторая дополнительная память (стек). Я так иногда делаю когда автомат получается ну очень сложным, да ещё с вложенными ветками.
|
|
|
|
|
Jul 11 2016, 08:13
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
QUOTE (AlexandrY @ Jul 10 2016, 14:49)  Прямо сейчас у меня в работе протокол с 41-им case в парсере. В Вашим? Да хоть 4141 case. Это ВАША РЕАЛИЗАЦИЯ некоего протокола, которая делатся через ... Попытайтесь найти спецификацию протокола в котором используется такое количество СОСТОЯНИЙ хоть на каком-то уровне. QUOTE Протоколы только отладчиком через SWD и можно детально отладить. ... поехав на край земли с "отладчиком" и сидя на столбе выяснять что это, вдруг, от того, что на противоположном конце земли кто-то, например, заапгрейдил Cisco не может связаться с Магаданом. Да, да внутрисхемный отладчик это очень "полезная" штука в таких ситуациях  .
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jul 11 2016, 08:14
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(AlexandrY @ Jul 11 2016, 13:01)  Хоть я и не совсем понимаю о чем вы говорите, поскольку не видно кода, но могу уверенно сказать, что ваш код не линейный, а размазанный. Т.е. часть логики кода отнесена куда-то в таблицы. А это еще хуже вермишели для понимания работы. Примерно так (код вне к.автомата - там где у ТС switch; код автомата - набор case-участков состояний к.автомата): 1. Выполняется код вне к.автомата. На месте switch() ставим переключение контекста на отдельный стек (в стеке сохранён контекст задачи автомата со всеми сохранёнными регистрами и адресом возврата). SwitchContext(адрес_структуры_контекста_со_стеком). Внутри функции SwitchContext() сохраняем текущий контекст на текущем стеке, переключаемся на стек к.автомата, восстанавливаем контекст из него и осуществляем возврат в код к.автомата. В коде к.автомата, выполнив очередную ветку case, опять вызываем SwitchContext(), которая сохранит контекст к.автомата на его стеке, переключится на исходный стек задачи, вызвавшей к.автомат и восстановит контекст из него и осуществит возврат. Код к. автомата будет выглядеть: Код код //STATE_0 ... //STATE_0 SwitchContext(); //к.автомат перешёл в сост.1 (и вышел из контекста к.автомата вернувшись в контекст вызывающей функции) код //STATE_1 ... //STATE_1 SwitchContext(); //к.автомат перешёл в сост.2 (и вышел из контекста к.автомата вернувшись в контекст вызывающей функции) код //STATE_2 ... //STATE_2 if () { //STATE_2 SwitchContext(); //к.автомат перешёл в сост.3 (и вышел из контекста к.автомата вернувшись в контекст вызывающей функции) код //STATE_3 ... //STATE_3 } else { SwitchContext(); //к.автомат перешёл в сост.4 (и вышел из контекста к.автомата вернувшись в контекст вызывающей функции) код //STATE_4 ... //STATE_4 } SwitchContext(); //к.автомат перешёл в сост.5 (и вышел из контекста к.автомата вернувшись в контекст вызывающей функции) код //STATE_5 ... //STATE_5 ... Как видите - код к.автомата получается линейным и легко читаемым даже при наличии ветвлений. А в традиционной реализации к.автомата при неск. ветвлениях по состояниям автомата уже получится малочитаемый вермишельный код. А представьте, если вдруг окажется необходимым из некоторых состояний к.автомата проходить цепочку других его состояний с возвратом потом в исходную точку: сост0 сост1 сост10 сост11 сост12 сост2 ... сост5 сост6 сост10 сост11 сост12 сост7 ... В традиционной реализации придётся ещё куда-то сохранять/восстаналивать состояния автомата. И вообще получится лес. А с переключением контекста, будет просто вызов функции.
|
|
|
|
|
Jul 11 2016, 08:34
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(zltigo @ Jul 11 2016, 14:22)  Уменьшение количества состояний чаще всего достигаеся увеличенем количества автоматов. Все сколь-нибудь вменяемые протоколы разбиты на уровни и обслуживаются на каждом уровне своими автоматоми. Если безмозгло свалить все уровни в одну кучу, то получим именно катострофический рост состояний при котором "41 case" от AlexandrY и "100" от Alt.F4 это даже еще не цветочки  Это да, можно и так. Но бывают даже относительно короткие автоматы в 10-20 состояний, но со сложной логикой ветвления, условными переходами между состояниями или необходимостью выполнения повторяющихся цепочек состояний в разных местах автомата. Я не говорю конкретно про разбор кадров протокола, вопрос был общего плана об к.автоматах.
|
|
|
|
|
Jul 11 2016, 08:48
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(x893 @ Jul 10 2016, 23:33)  Зря не смотрели в сторону http://dunkels.com/adam/pt/index.htmlи enum не нужны Protothread это не про протоколы. Поскольку там используется много неявных выходов из функции, то в сложных десериализаторах придется много кода потратить на сохранение контекста. Поскольку контекст десереализации не ограничивается только номером состояния как в Protothread. По мне лучше уж goto чем такие хитрые переходы. Цитата(jcxz @ Jul 11 2016, 11:14)  Код код //STATE_0 ... //STATE_0 SwitchContext(); //к.автомат перешёл в сост.1 (и вышел из контекста к.автомата вернувшись в контекст вызывающей функции) код //STATE_1 ... //STATE_1 SwitchContext(); //к.автомат перешёл в сост.2 (и вышел из контекста к.автомата вернувшись в контекст вызывающей функции) код //STATE_2 ... //STATE_2 if () { //STATE_2 SwitchContext(); //к.автомат перешёл в сост.3 (и вышел из контекста к.автомата вернувшись в контекст вызывающей функции) код //STATE_3 ... //STATE_3 } else { SwitchContext(); //к.автомат перешёл в сост.4 (и вышел из контекста к.автомата вернувшись в контекст вызывающей функции) код //STATE_4 ... //STATE_4 } SwitchContext(); //к.автомат перешёл в сост.5 (и вышел из контекста к.автомата вернувшись в контекст вызывающей функции) код //STATE_5 ... //STATE_5 ... В вашем псевдокоде я вижу только множественные SwitchContext() без аргументов. Это выглядит как полная бессмыслица. Стек - это в вашем понятии стековая структура данных или стек процессора? Стековые структуры обычно применяют для раскрытия рекурсии в парсерах. У меня так работают парсеры JSON, LUA и еще несколько. Но в протоколах нет рекурсий. Там есть именно разбор вариантных хидеров. Ничего лучше case для этого не придумано.
|
|
|
|
|
Jul 11 2016, 09:24
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(x893 @ Jul 11 2016, 12:09)  Смысл простой - использование __LINE__ для автогенерации switch/case Да понятен там смысл. Но влетать внутрь цикла, дескать C-и позволяет это хуже чем goto. Опять же при символьной отладке это будет выглядеть как хаотичная передача управления непонятно куда. TC же показал способ с вычисляемыми ключами. Ему Protothread никак не подойдет.
|
|
|
|
|
Jul 11 2016, 09:55
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(jcxz @ Jul 11 2016, 11:34)  Но бывают даже относительно короткие автоматы в 10-20 состояний, но со сложной логикой ветвления, условными переходами между состояниями или необходимостью выполнения повторяющихся цепочек состояний в разных местах автомата. Несомненно! По этой причине я тоже с самого начала писал, что просто не надо все всегда ТОЛЬКО на состояния автомата валить. В каждом конкретом случае надо смотреть, как разгружать собственно автомат от излишества состояний, ибо конечный автомат не есть панацея  . Совсем недавно в казалось-бы простой задаче начал в лоб писать автомат - началось погружение в пучину состояний. Разбил на два автомата - тоже не сильно понравилось, ибо такой подход нормально работает, когда протокол на уровни бьется. В результате был сделан один автомат на 13 состояний и три бита дополнительных глобальных статусов. Цитата(AlexandrY @ Jul 11 2016, 11:48)  Но в протоколах нет рекурсий... Это просто Ваши познания в протоколах невелики  . В тех же телекомуникационных протоколах в полученном фрейме может находится, или не находится, самая разнообразная информация. Причем, как относящаяся к одному объекту, так и к разным. А сам фрейм собираться из множества небольших кусочков. Вот и начинается рекурсивный разбор полей.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jul 11 2016, 11:11
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(zltigo @ Jul 11 2016, 12:55)  Это просто Ваши познания в протоколах невелики  . В тех же телекомуникационных протоколах в полученном фрейме может находится, или не находится, самая разнообразная информация. Причем, как относящаяся к одному объекту, так и к разным. А сам фрейм собираться из множества небольших кусочков. Вот и начинается рекурсивный разбор полей. Вы уж определитесь о чем хотите рассказать. Сначала у вас многоуровневые концепции, теперь опять рекурсивные. Фреймы из кусочков это связные списки, а не стековые структуры. Я не видел рекурсию на в TCP/IP, ни в Zigbee, ни в BLE, ни в USB... Ну ни где. А вы где усмотрели?
|
|
|
|
|
Jul 11 2016, 11:43
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
QUOTE (AlexandrY @ Jul 11 2016, 14:11)  Вы уж определитесь о чем хотите рассказать. Сначала у вас многоуровневые концепции, теперь опять рекурсивные. О том, что и написал в первом посте. Если количество состояний автомата начинает измеряться десятками и тем более сотнями, то конечный автомат пишется НЕПРАВИЛЬНО. Как с этим бороться - рецепты разные. В том числе и рекурсия в явном или неявном виде. QUOTE Я не видел рекурсию на в TCP/IP, ни в Zigbee, ни в BLE, ни в USB... Ну ни где. Вот я и говорю, что познания Ваши невелеки  . Есть целые пласты протоколов находящиеся далеко за пределами сознания бытовых компьютерщиков. Они, напрмер, живут здесь: https://en.wikipedia.org/wiki/ITU-T отличительная их особенность, что создавались и оттачивались они годами и десятилениями и служат не для того, что бы какая нибудь фирма могла быстро родить урода по собственному невеликому разумению и засрать рынок заставля конкурентов ковыряться в дерьме добиваясь совместмости и работоспособности. А совсем с противоположной целью. При этом типовой ITU-ный стиль описания протоколов это конечные автоматы. Так уж получилось, что большую часть своей жизни я занимался именно телекомуникационными протоколами. Отчего и образовался более, чем скептический взгляд на то, что рожают и изобилии околокомпьютерщики и что является, как например, TCP/IP в лучшем случае огрызком давно существующих стеков протоколов.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jul 11 2016, 13:24
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(Kabdim @ Jul 11 2016, 15:10)  Не могли бы Вы, zltigo, уточнить в каком именно протоколе ITU-T рекурсивный разбор полей? Как ТРЕБОВАНИЕ реализовывать что либо рекурсивно - естественно, в описании протоколов этого нет. Описание протроколов никак не описывает способов их реализации, хотя на верхнем уровне протокол описывается как конечный автомат и соответственно подталкивает на этот путь. В конкретной реализации протокола все уже на откуп того, кто реализовывет. Я использовал рекурсивные вызовы автомата уже на уровне MTP3. Цитата(AlexandrY @ Jul 11 2016, 16:07)  Он наверно имеет в виду DHCP где минимум 71 case в реализации. С каких пор студенческий самодуй DHСP стал иметь отношение к ITU? Разномастные огрызки разных RFC это вообще не стандарт. Цитата Протоколы ITU-T из-за их всеядности и резиновости просто кошмарное количество switch-case требуют. ITU-T протоколы не требуют "кошмарных" количеств "case". По причине того, что они изначально разделены и хорошо суктурированы по уровням. Если же практическую реализацию делать бездумно смешивая в одну кучу что попало, то тогда, конечно, количество состояний для обеспечия функционирования можно раздувать хоть до "71" хоть до тысяч. DHCP с 71 case, это как раз пример безмозглого подхода к делу  в стиле давай-давай, что тут думать трясти надо. Это обычно для "компьютерщиков", которые недоучившись и недопоняв сначала сотворив огрызок TCP/IP из того, что смогли понять в X.25 стеке, начали городить бездумно городить "протоколы" и дальше. В результате начальное "упрощение", выкидывание, объедиение и усечение уровней ведет при попытке реализовать что либо сложнее, чем "два байта переслать" к каше из уровней, множества заплаток и появлениям "DHCP где минимум 71 case". Я все понимаю, почему так все растет - бурно в 70x началась компьютеоизация и микроконтролизация. Все сделать сразу по уму и аккуратно было сложно, или воообще почти невозможно из-за отсутсвия те-же аппаратных ресурсов. Мозговых ресурсов тоже не хватало - хлынул поток студентов нахватавшихся только вершков. Все понятно. И то, что выросло, то выросло http://inet777.ru/esli-byi-stroiteli-stroi...vilizaciyu/8476
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jul 11 2016, 13:40
|
Профессионал
    
Группа: Свой
Сообщений: 1 123
Регистрация: 8-03-09
Из: Днепр
Пользователь №: 45 848

|
Задавал я аналогичный вопрос тутно реализация через boost меня ужаснула. Вместо С-препроцессора проще сделать свой для pre-build. Так и сделал. Так и доволен сделаным.
|
|
|
|
|
Jul 12 2016, 08:26
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(MrYuran @ Jul 12 2016, 10:06)  А я таки не понял, чем enum плох? Мало того, что все по порядку, так можно добавить последний элемент eNumOfItems и получить автоматически количество элементов, которое можно вставить в определения массивов или в циклы, etc. Правильное тактическое применение enum это когда назначаешь переменной символьное имя и проверяешь всегда у нее только символьное имя. А если надо enum сравнивать с int то это уже неправильное применение enum. О чем отладчики и намекают не показывая числовое значение переменной с типом enum. Цитата(zltigo @ Jul 11 2016, 16:24)  Все сделать сразу по уму и аккуратно было сложно, или воообще почти невозможно из-за отсутсвия те-же аппаратных ресурсов. Мозговых ресурсов тоже не хватало - хлынул поток студентов нахватавшихся только вершков. Все понятно. Это заблуждение разработчика видимо давно не имевшего практику разработки современных приложений. Нынче длинные switch-case редко встретишь потому что протоколы разрослись настолько, что им этот механизм стал мал. Строят целы базы данных в виде таблиц с сотнями и тысячами записей. Для такого switch-case просто физически писать уже невмоготу, потому при разборе протоколов переходят на движки с поиском по базе данных. Взгляните на ATT в BLE Это ухудшает конечно производительность, но и процессоры стали быстрее.
|
|
|
|
|
Jul 12 2016, 09:47
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
QUOTE (AlexandrY @ Jul 12 2016, 11:26)  Это заблуждение разработчика видимо давно не имевшего практику разработки современных приложений. Это ЗНАНИЕ разработчика, который ЗНАЕТ, что если разрабатывать и писать реализации через анус, то case разрастаются немеряно. Что свидетельвует о НЕПРАВИЛЬНОМ подходе к реализации. О чем сразу в первом посте и написал. О том, что "современные" приложения рожают через анус, потому, что и ума и времени нехватает, тоже писал. QUOTE ...писать уже невмоготу, потому при разборе протоколов переходят на движки с поиском по базе данных. Теперь, если вернуться к TC и case, то очень, очень полагаю, что ничего сколь-нибудь выдающищегося и затмевающего своей сложностью "ATT в BLE" и иже с ним он не пишет, а просто и банально из-за необдуманости реализации тонет в плодящихся состояних конечного автомата  .
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jul 13 2016, 05:15
|
Местный
  
Группа: Свой
Сообщений: 321
Регистрация: 23-12-11
Из: Уфа
Пользователь №: 69 041

|
Я бы так делать не стал, но все же... CODE switch (i) { #define INC_DEF 0 #define TMP_DEF INC_DEF+1 case INC_DEF: ... break;
#undef INC_DEF #define INC_DEF TMP_DEF #undef TMP_DEF #define TMP_DEF INC_DEF+1
case INC_DEF: ... break;
#undef INC_DEF #define INC_DEF TMP_DEF #undef TMP_DEF #define TMP_DEF INC_DEF+1 case INC_DEF: ... break;
#undef INC_DEF #define INC_DEF TMP_DEF #undef TMP_DEF #define TMP_DEF INC_DEF+1
case INC_DEF: ... break;
... }
|
|
|
|
|
Jul 13 2016, 06:36
|
Профессионал
    
Группа: Свой
Сообщений: 1 123
Регистрация: 8-03-09
Из: Днепр
Пользователь №: 45 848

|
Если надо что-то "проинкриментировать" - надо сгенерировать некую таблицу (массив const структур во флеш), или несколько таблиц с требуемой инф-ей. Опятьже - сделать свой препроцессор. ОНО будет читабельнее, логичнее без идиотизма когда средство важнее цели. Если конечно цель - не сделать "глухой" проект, в котором только писатель и разбирается. Цитата(Dog Pawlowa @ Jul 12 2016, 17:57)  Вообще то сам по себе инкремент состояния - это потенциальная угроза ошибки. Сейчас ручками вписываю конкретное текстовое состояние перехода (из enum, или enum+X-macro). Вот я по Вашей наводке так и делаю. Гранд мерси  enum - генерация последовательных индексов массива, и ониже - для инициализации массива констант-структур. (реализация сколько-угодно-уровневого меню).
Сообщение отредактировал k155la3 - Jul 13 2016, 06:55
|
|
|
|
|
Jul 14 2016, 05:55
|
Группа: Участник
Сообщений: 7
Регистрация: 27-05-05
Пользователь №: 5 449

|
Я использую таблицу состояний автомата именно в виде таблицы во флеш, а парсер этой таблицы ее просто сканирует и выполняет действия в соответствии "состояние-событие-выполнить действие-перейти в новое состояние". В этом случае вся логика автомата содержится в таблице и любое добавление, удаление или редактирование любой строки влияет только на действия именно этой строки.
|
|
|
|
|
Jul 14 2016, 09:04
|
Группа: Участник
Сообщений: 7
Регистрация: 27-05-05
Пользователь №: 5 449

|
Цитата(adnega @ Jul 14 2016, 12:53)  А кто придумывает номера для "состояний"? При добавлении новой записи нужно править поле "перейти в новое состояние" предыдущей записи? Или можно ставить только поля "событие" и "выполнить действие" а выполняться все будет линейно пока не дойдет до конца таблицы? Еще один enum Если при правке выясняется, что нужно перейти в другое состояние, то да, нужно исправлять. Если планируется, что все будет выполняться линейно, то это проще прописать в парсере один раз, тогда поле "перейти в состояние" в таблице отмирает.
|
|
|
|
|
Jul 14 2016, 09:14
|
Частый гость
 
Группа: Участник
Сообщений: 168
Регистрация: 14-02-10
Пользователь №: 55 490

|
Цитата(Andrew_Q @ Jul 14 2016, 09:55)  состояние-событие-выполнить действие-перейти в новое состояние Как быть с ветвлением, т.е. если новое состояние определяется исходом действия?
--------------------
#define TRUE (4==(2*2))
|
|
|
|
|
Jul 14 2016, 09:23
|
Профессионал
    
Группа: Свой
Сообщений: 1 123
Регистрация: 8-03-09
Из: Днепр
Пользователь №: 45 848

|
Цитата(adnega @ Jul 14 2016, 10:53)  А кто придумывает номера для "состояний"? При добавлении новой записи нужно править поле "перейти в новое состояние" предыдущей записи? Или можно ставить только поля "событие" и "выполнить действие" а выполняться все будет линейно пока не дойдет до конца таблицы? Чтобы не заморачиваться с ручной адресацией массива-таблицы, можно примерно так. (но таблиц нужно 2 - одна состояний, вторая - переходов, в которую включены указатели на функции-обработчики) IMHO Код enum FSM_StateGo { A1, A2, A3, A_Stop, B1, B2, B3, B_Stop, . . . . };
enum FSM_State { STATE_A, STATE_B . . . };
TRun_Table RT[] = { [A1] = { STATE_A, A2 . . . . . }, [A2] = { STATE_A, B2 . . . . . }, [A3] = { STATE_A, A1 . . . . . }, [A_STOP] = { STATE_A, A_STOP . . . . . }, [B1] = { STATE_B, A2 . . . . . }, [B2] = { STATE_B, B2 . . . . . }, [B3] = { STATE_B, A1 . . . . . }, [B_STOP] = { STATE_A, A_STOP . . . . . }, . . . . . };
Сообщение отредактировал k155la3 - Jul 14 2016, 09:36
|
|
|
|
|
Jul 14 2016, 09:29
|
Группа: Участник
Сообщений: 7
Регистрация: 27-05-05
Пользователь №: 5 449

|
Цитата(Владивольт @ Jul 14 2016, 14:14)  Как быть с ветвлением, т.е. если новое состояние определяется исходом действия? По исходу действия генерируем событие, в таблице описываем переход по этому событию в нужное состояние. У меня таблица выглядит так CODE __flash state_table_type STATE_MODBUS_NASTER_TABLE[]= // Таблица состояний, условий перехода и действий режима ModBus мастер { /* из состояния по событию выполнить в состояние в режим */
{ANY_STATE, NOT_NET_ACCESS, init_state_mashine, INIT, MODBUS_MASTER}, {ANY_STATE, TIMER_OVFL, init_state_mashine, INIT, MODBUS_MASTER}, {INIT, NET_ACCESS, request_ana_from_monoblock, WAIT_MONOBLOCK_ANA, MODBUS_MASTER}, {WAIT_MONOBLOCK_ANA, MONOBLOCK_DATA_RECEIVED,send_ana_to_modbus, WAIT_CHECK_MODBUS_ANA, MODBUS_MASTER}, {WAIT_CHECK_MODBUS_ANA, MODBUS_SENDED, receive_ana_modbus_check, NO_NEW_STATE, MODBUS_MASTER}, {WAIT_CHECK_MODBUS_ANA, MODBUS_RECEIVED, test_ana_modbus_check, NO_NEW_STATE, MODBUS_MASTER}, }
|
|
|
|
|
Jul 14 2016, 09:45
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
Цитата(Andrew_Q @ Jul 14 2016, 12:29)  __flash state_table_type STATE_MODBUS_NASTER_TABLE[]= Терминология MONOBLOCK и _ana_ не из оперы modbus. Всегда поражаюсь людям изобретающим велосипеды... Цитата PDU (Protocol Data Unit) — общая для всех физических уровней часть пакета MODBUS. Включает в себя код функции и данные пакета. ADU (Application Data Unit) — полный пакет MODBUS. Включает в себя специфичную для физического уровня часть пакета и PDU.
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Jul 14 2016, 09:50
|
Группа: Участник
Сообщений: 7
Регистрация: 27-05-05
Пользователь №: 5 449

|
Опечатка? Я хронический лентяй, программа работает на изделии с 2002 года. Исправлять лениво :-) Цитата(demiurg_spb @ Jul 14 2016, 14:45)  Терминология MONOBLOCK не из оперы modbus. Всегда поражаюсь людям изобретающим велосипеды... Моноблок - это блоки нашей разработки на кастомном протоколе. А сие поделие является шлюзом в Modbus. Вот, кстати, ссылка на автомат конечных состояний, описанный таблицей, управляемый событиями. http://chipenable.ru/index.php/programming...nizatsiya-progr
Сообщение отредактировал Andrew_Q - Jul 14 2016, 10:05
|
|
|
|
|
Jul 15 2016, 03:47
|
Гуру
     
Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823

|
Цитата(jcxz @ Jul 15 2016, 06:26)  А как быть если после некоторых состояний необходимо пройти через определённую цепочку состояний, а затем вернуться к исходной цепочке? Ваш пример похож на последовательность выполнения строк на бэйсике, соответственно используется что-то типа GOSUB  Ну и объединить цепочку состояний в одно состояние с дополнительным индексом ничего не мешает. Регулярность и простота автоматов - хорошо, но все же иногда нужно немного и усложнять реализацию.
--------------------
Уходя, оставьте свет...
|
|
|
|
|
Jul 15 2016, 05:11
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Dog Pawlowa @ Jul 15 2016, 09:47)  Ваш пример похож на последовательность выполнения строк на бэйсике, соответственно используется что-то типа GOSUB  Ну и объединить цепочку состояний в одно состояние с дополнительным индексом ничего не мешает. Регулярность и простота автоматов - хорошо, но все же иногда нужно немного и усложнять реализацию. Объединить? А что тогда мешает объединить все остальные состояния автомата в одно? Если ничего - то и автомат не нужен, так как всего одно состояние осталось. А если есть автомат - значит он для чего-то нужен и нельзя объединить состояния. А если нужен доплнительный индекс, то как потом возвращаться к основному? А если не один уровень вложенности, а больше? Стек хранения состояний городить?
|
|
|
|
|
Jul 15 2016, 06:02
|
Гуру
     
Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823

|
Цитата(jcxz @ Jul 15 2016, 08:11)  Объединить? А что тогда мешает объединить все остальные состояния автомата в одно? Если ничего - то и автомат не нужен, так как всего одно состояние осталось. А если есть автомат - значит он для чего-то нужен и нельзя объединить состояния. Почему это нельзя объединить? Ну не будьте уж таким догматиком. Одну и ту же задачу могут выполнять автомат с одной переменной состояния, которое может принимать, допустим 16 значений и фрагмент программы, использующий 16 битовых флагов. И выглядеть они будут похоже, всего вместо case будет if, ну и присвоение чуть по другому. А между этими двумя реализациями целый диапазон возможных решений. Цитата(jcxz @ Jul 15 2016, 08:11)  А если нужен дополнительный индекс, то как потом возвращаться к основному? А если не один уровень вложенности, а больше? Стек хранения состояний городить? Усложняете. Вот кода, описывающего одно состояние с дополнительными промежуточными состояниями проверки индикации. Код void fSelfTest(void) { if (old_status-status) { StatusFirstTimeReady("Tst"); SET_TIMER(N500ms); } if (event==evTimer) { switch (status_var) { case 0: OnWorkRed1(); break; case 1: OnChemLed(); break; case 2: OnWaterLed(); break; case 3: ToggleWorkColor(); OffChemWaterLeds(); case 4: status= sWaiting; break; default: status_var=-1; } status_var++; } } Что касается стека... Стек, не стек, но что мешает запоминать предыдущее состояние и при необходимости туда возвращаться? Если интерфейс пользователя - один большой автомат, некоторые функции вызываются из разных меню, возвращаться нужно именно в предыдущее состояние. Можно назвать это одноуровневым стеком.
--------------------
Уходя, оставьте свет...
|
|
|
|
|
Jul 15 2016, 07:00
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
QUOTE (Dog Pawlowa @ Jul 15 2016, 09:02)  Что касается стека... Стек, не стек, но что мешает запоминать предыдущее состояние и при необходимости туда возвращаться? Если интерфейс пользователя - один большой автомат, некоторые функции вызываются из разных меню, возвращаться нужно именно в предыдущее состояние. Можно назвать это одноуровневым стеком. Вот и дошли до того, что при создании реальных автоматов стек состояний (как бы Вы его не назвали другими словами ) тоже можно и нужно использовать. Что тоже уменьшает безмерное разрастание состояний. У меня почти уже в привычке использовать в минимальной "базе" три состояния - текущее, предыдущее и то, на которое сейчас текущее будет измененно. Так же огромное знвчение стека в диагностике работы в РЕАЛЬНЫХ условиях - как дошли до ошибки.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jul 15 2016, 07:32
|
Гуру
     
Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823

|
Цитата(zltigo @ Jul 15 2016, 10:00)  е и то, на которое сейчас текущее будет изменено. А как Вы заглядываете в будущее оперируете с этим значением ? У меня обычно просто - присвоил текущему и вышел, а анализ смены состояния в начале вызова обработчика автомата.
--------------------
Уходя, оставьте свет...
|
|
|
|
|
Jul 15 2016, 08:20
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Dog Pawlowa @ Jul 15 2016, 12:02)  Что касается стека... Стек, не стек, но что мешает запоминать предыдущее состояние и при необходимости туда возвращаться? Если интерфейс пользователя - один большой автомат, некоторые функции вызываются из разных меню, возвращаться нужно именно в предыдущее состояние. Можно назвать это одноуровневым стеком. Конечно ничто не мешает. Только код становится уже плохо читаемым, слишком громоздким с такой реализацией на классических автоматах. В этом случае я делаю псевдозадачу (которую можно считать к.автоматом). Только она не вытесняется принудительно другими задачами как обычная задача ОС, а вытеснение задачи происходит по логике её работы, при переходе к очередному состоянию автомата. А состояния автомата при этом - это значения PC, которые сохраняются в стеке при вытеснении задачи и на которые происходит переход при активации задачи. И код становится читаемым даже при наличии сложных ветвлений и вложенных состояний автомата.
|
|
|
|
|
Jul 15 2016, 08:27
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
QUOTE (Dog Pawlowa @ Jul 15 2016, 10:32)  А как Вы заглядываете в будущее оперируете с этим значением ? Хотим изменить текущее состояние автомата - "будущее" это то значение на которое изменим. "Прошлое" это сохраненное котрое было до текущего. QUOTE У меня обычно просто - присвоил текущему и вышел... А присвоение текущему уже в конце обработчика - так и правильнее, ибо состояние сменилось, когда ВСЕ действия сопутствующие смене состояния уже произведены. Таким образом в процессе переключения типично известны ТРИ состояния прошлое-настояшее-будущее.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|