Перейти к содержанию
    

[Вроде решено] Массив указателей на функции. Указатель на массив

Если честно, немного извращенно. Прототреды и то проще. Еще проще написать функции линейно, и вызывать их по указателю. Состояние - ссылка на нужную функцию. И никаких трат на сохранение-восстановление контекста.

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

Когда состояний много, когда переходы между состояниями по разным условиям.

А особенно - если представьте:

Нужно чтобы после состояния S1 выполнялось несколько состояний S10-S11-S12 с последующим возвратом в основной поток к S2.

И такая-же последовательность нужна после скажем S3.

С классическим подходом (функциями или switch/case) придётся городить огород с сохранением переменной состояния автомата в отдельной переменной и восстановлением её потом.

Здесь же всё просто - просто пишем функцию. И всё.

То же самое - циклы с циклическим изменением состояний S1-S2-S3-...S1-.... Опять при таком подходе получается всё много нагляднее и проще чем классически.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я единственный раз использовал дополнительную переменную-состояние. Next_State. И только из-за того, что получались одинаковые куски кода. Как-то зашел разговор о культуре программирования. Я решил перелопатить код в соответствии с перечисленными участником форума правилами. В итоге перематерился. По сути я не программой занимался, а окультуриванием кода. Гламурность наводил. В особенности меня порой убивает запрет на GOTO. Хотя и понимаю, что GOTO порой опасно использовать.

Если где-то возникают трудности, значит нужно пересматривать подход в целом. Это правило меня никогда не подводило.

Честно сказать, я не программист в чистом виде. Увлекался электроникой, в определенный момент решил взяться за изучение МК. Так получилось, что моим выбором стали AVR-ки. Начинал с асма. На си переполз 3-4 года назад.

Изменено пользователем demiurg1978

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Получили линейный, хорошо читаемый код.

Немного спорно.

А при наличии нескольких задач-автоматов SwitchStack() перестает быть симметричной.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А при наличии нескольких задач-автоматов SwitchStack() перестает быть симметричной.

В смысле? Если нужно несколько независимых автоматов - для каждого свой стек и всё то же самое. В SwitchStack() просто добавится аргумент - указатель на рабочую переменную/стек.

Ну или своя SwitchStack() для каждого автомата.

 

Я единственный раз использовал дополнительную переменную-состояние. Next_State. И только из-за того, что получались одинаковые куски кода. Как-то зашел разговор о культуре программирования. Я решил перелопатить код в соответствии с перечисленными участником форума правилами. В итоге перематерился.

Это же тема - изначально про случаи сложных автоматов. Для простых достаточно switch/case.

А вот в случае сложных автоматов (а тем более если в них есть повторяющиеся или циклические участки) мой метод значительно упрощает код, он становится легче для анализа и отладки. Значит и матюкаться при его редактировании/отладке приходится меньше.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А вот в случае сложных автоматов (а тем более если в них есть повторяющиеся или циклические участки) мой метод значительно упрощает код, он становится легче для анализа и отладки. Значит и матюкаться при его редактировании/отладке приходится меньше.

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

Изменено пользователем demiurg1978

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Причины у всех свои. Кто захочет - тот применит. Я тут никого не заставляю. Всё уже более чем разжёвано.

И почему такое решение не может быть реализовано?? Вариант с switch/case при кол-ве состояний >=20 много хуже получается и это, имхо, очевидно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вариант с switch/case при кол-ве состояний >=20 много хуже получается и это, имхо, очевидно.

В этом случае никто не спорит. И я сам об этом сказал. Что при большом кол-ве состоянии switch-case нечитабелен. Использую функции и индексный переход.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В этом случае никто не спорит. И я сам об этом сказал. Что при большом кол-ве состоянии switch-case нечитабелен. Использую функции и индексный переход.

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

Ну например - использовал такой метод в ISR осуществляющем приоритетный арбитраж доступа к нескольким микросхемам (FLASH, FRAM) висящим на шине SPI, и управляющем транзакциями

(старт/стоп DMA-пересылок) записи, чтения к этим микросхемам по запросам от нескольких пользовательских каналов. Каждая транзакция состояла из нескольких шагов (подать команду разрешения записи, стартовать передачу блока, завершить обмен, опросить статус чипа и т.п.), которые разбивались на ещё более мелкие шаги. Эти транзакции собирались в ещё более крупные пакеты операций. Каждый такой уровень - это отдельный автомат со своей переменной состояния. Эти автоматы реализовал switch/case-ом.

В этом-же ISR работал процесс осциллографирования, который в реальном времени перегонял данные из кольцевого буфера во FRAM, во FLASH формируя осциллограммы в определённом формате, осуществляя синхронизацию, передискретизацию потока и многое другое. Вот именно его и реализовал вышеописанным способом.

Реализовать в виде задачи ОС это было неоптимально, из-за требований к скорости работы и других требований.

Этот процесс даже описанный указанным способом занимает около 24кБ в исходнике, а в виде классического switch/case - страшно даже подумать. И нереально отладить.

Выкладывать сюда эту реализацию не буду, так как во-первых - коммерческий проект; во-вторых - исходник этот - почти сотня КБ - смысла нет, всё равно никто не станет разбираться в таком объёме, если даже сможет ;)

 

Так же использовал такой метод при опросе через nRF24L01+ через короткие окна (неск. мсек) удалённого устройства, где тоже весь протокол обмена был описан в виде многоуровневого автомата, и протокол довольно объёмный, так что в виде классического автомата реализовывать - слишком громоздко, а в виде задачи ОС - по требованиям скорости не проходило, так как было нужно много мелких переключений задач.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

занимает около 24кБ в исходнике, а в виде классического switch/case - страшно даже подумать.

Оптимизацию включать пробовали? ;) Еще лет 10 назад тот ИАР для того же AVR при оптимизации заменял switch-case на массив функций.

 

Теперь сравниваем вызов функции по индексу, который используется автором, с Вашей системой.

Выход из функции и вызов ее опять (или вызов следующей при смене состояния) = восстановить регистры, а затем их сохранить.

Ваш вариант = сохранить регистры, а потом восстановить их опять.

 

Разница - да, она есть, и заключается вот в чем - в одном случае человек в поезде лицом в направлении движения, а в другом - спиной в направлении движения. А поезд тот же, и везет точно так же. Нету разницы!

 

Экономии программной памяти в огромном количестве - нет! Увеличения быстродействия - нет! (потому что восстановление/сохранение контекста не оптимизировано). Может вызов функции по индексу и требует пары лишних байт, зато позволяет легко организовать иерархию автоматов.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Что-то мне это баловство со стеком не нравится. Хорошо, сохранили контекст. Убежали в другой обработчик. А тут бац. Ситуация в корне поменялась. И тут мы прибегаем в обработчик, где в сохраненных регистрах старые данные. Да такая система, что обезьяна с гранатой. Никогда не знаешь, что произойдет и куда прилетит...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Какие старые данные в каких регистрах???

Вы что такое "переключение контекста" понимаете? Как например это используется в ОС понимаете?

Советую подучить теорию.

 

Оптимизацию включать пробовали? ;) Еще лет 10 назад тот ИАР для того же AVR при оптимизации заменял switch-case на массив функций.

А Вы читать, то что я написал - пробовали? Что такое "исходник" понимаете?

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

 

И не знаю где там IAR заменяет что-то на массив функций, я по коду IAR для ARM вижу, что на месте switch() такого автомата IAR генерит огромную последовательность из

десятков пар инструкций CMP/BEQ. Вне зависимости - включена оптимизация или нет.

Так что даже по скорости вариант с переключением контекста будет быстрее.

 

Разница - да, она есть, и заключается вот в чем - в одном случае человек в поезде лицом в направлении движения, а в другом - спиной в направлении движения. А поезд тот же, и везет точно так же. Нету разницы!

Следуя Вашей аналогии: в Вашем случае человек добирается до цели кучей разных поездов, перепрыгивая из одного в другой; в моём - едет на одном поезде и какая разница как он там сидит - спиной или лицом - главное то, что ему удобно и он может в это время полезными делами заниматься, а не прыганьем из одного поезда в другой. :laughing:

 

Экономии программной памяти в огромном количестве - нет! Увеличения быстродействия - нет! (потому что восстановление/сохранение контекста не оптимизировано). Может вызов функции по индексу и требует пары лишних байт, зато позволяет легко организовать иерархию автоматов.

Ещё раз - попробуйте прочитать хотя-бы мои сообщения, на которые Вы пытаетесь отвечать!

Цель не стояла - увеличение быстродействия или тем более - экономия пары байт программной памяти (что вообще полный бред в современных условиях).

Цель: кардинальное улучшение читаемости и соответственно - удобства написания/отладки кода больших сложных автоматов.

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

Попробуйте когда-нить открыть листинг скомпилённой программы. Увидите, что вызов/возврат из функции занимает далеко не пару байт, даже думаю на AVR.

Каждая такая функция это: её вызов (косвенный, который дороже прямого вызова + адресация в массиве указателей) + сохранение регистров на входе в неё + восстановление регистров на выходе + возврат. Сколько это байт будет на AVR?

В моём случае в таком месте будет только: прямой вызов функции. Всё! Сколько это байт будет на AVR?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Цель: кардинальное улучшение читаемости и соответственно - удобства написания/отладки кода больших сложных автоматов.

Банальный макрос #define SwitchStack(N) } void Step##N##() {

делает то же самое "улучшение" читаемости и удобство. Все равно место вызова Вашей функции не берется с потолка.

 

 

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

...Каждая такая функция это: её вызов (косвенный, который дороже прямого вызова + адресация в массиве указателей) + сохранение регистров на входе в неё + восстановление регистров на выходе + возврат. Сколько это байт будет на AVR?

Немало, тут я полностью согласен. Но вызов функции по индексу - это один раз на всю программу, преамбула/постамбула тоже одни на всех. Ваш вызов функции против моего вызова функции. Ваше переключение контекста гарантированно дольше, так как должно покрыть все используемые всей функцией регистры, мое переключение (вызов-возврат) в среднем короче, потому что адаптировано к конкретному куску кода, выделенному в отдельную функцию.

 

Еще что-то есть? ;)

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ваш вызов функции против моего вызова функции. Ваше переключение контекста гарантированно дольше

 

Простите, что вставляю свои 5 коп, но приводя исходники и рассказывая идею, jcxz чётко заявлял об ARM: а в нём конвейерная организации. В AVR тоже есть конвейер? Контекст сохраняется одинаково?

Так что, речь может идти только о удобочитаемости исходников.

 

И что останавливает от проверки одного и того же кода для ARM и AVR. Кстати, нужен только симулятор (а они есть и недурные, хоть в том же IAR) ядер.

Изменено пользователем Obam

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Банальный макрос #define SwitchStack(N) } void Step##N##() {

делает то же самое "улучшение" читаемости и удобство. Все равно место вызова Вашей функции не берется с потолка.

Ну да, а изменение значения переменной-состояния автомата где?

А если эта переменная меняется условно, в зависимости от результата выполнения предыдущего состояния автомата?

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

Покажите как Вы с помощью этого макроса реализуете условное изменение состояния автомата? А цикл?

 

Немало, тут я полностью согласен. Но вызов функции по индексу - это один раз на всю программу, преамбула/постамбула тоже одни на всех.

Это почему это сохранение/восстановление контекста одно на всех? Разве в соглашениях вызова функций для AVR сохранение контекста делает вызывающий функцию,

а не сама функция? В любом случае - разница даже в десятки байт - ни о чём, смысла нет обсуждать.

 

Ваше переключение контекста гарантированно дольше, так как должно покрыть все используемые всей функцией регистры, мое переключение (вызов-возврат) в среднем короче, потому что адаптировано к конкретному куску кода, выделенному в отдельную функцию.

Да ладно?! А переменные, которые Ваши состояния автомата используют их нет? Без переменных? В моём случае - они хранятся в регистрах и сохраняются/восстанавливаются одним и тем же кодом, а у Вас - для каждого состояния придётся их на входе в каждое состояние заново читать, а после - записывать.

Если уж Вас так волнует количество байт кода, то как раз мой вариант в любом случае будет короче.

Попробуйте на практике реализовать для автомата хотя бы с десятком состояний.

 

Еще что-то есть? ;)

Всё уже было. Если Вы не поняли - очевидно рано ещё, со временем может поймёте.

А пока - никто Вас не заставляет изучать что-то новое, включать мозг, продолжайте и дальше жить в своём однобайтовом мирке...

 

Так что, речь может идти только о удобочитаемости исходников.

Именно об этом и шла речь в первую очередь.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

>> Покажите как Вы с помощью этого макроса реализуете условное изменение состояния автомата? А цикл?

А как Вы со своей функцией реализуете условное изменение состояния автомата? - Никак. Вы хотели линейности кода - вот оно.

 

>> Ну да, а изменение значения переменной-состояния автомата где?

>> А если эта переменная меняется условно, в зависимости от результата выполнения предыдущего состояния автомата?

Так еще проще, чем у Вас - присвоил новое значение и вышел. Естественное структурирование автомата. А у Вас - if (..) goto ?

 

>>В N-й раз уже говорю, но похоже не доходит :(( - Мой метод оптимален для сложных автоматов,

Вы в автоподпись добавьте - крут, как яйца ;)

 

>> с большим числом состояний

"Большим" - это сколько? У меня сотни - без проблем.

 

>> условными переходами между состояниями, циклическим выполнением каких-то шагов автомата

1. Бывают автоматы без условных переходов и циклического выполнения?

2. Вот не знал, что есть проблемы реализовать переходы (условные и безусловные) и циклы с массивами функций.

 

>>общих последовательностей шагов (которые моим методом оформляются в простые функции).

Вы наверное и сами функции изобрели? ;)

Общие последовательности шагов реализуются добавлением одной переменной, которая указывает куда выйти после окончания последовательности, она есть и в Вашей реализации.

 

>> Это почему это сохранение/восстановление контекста одно на всех? Разве в соглашениях вызова функций для AVR сохранение контекста делает вызывающий функцию,

>>а не сама функция? В любом случае - разница даже в десятки байт - ни о чём, смысла нет обсуждать.

Да, тут я погорячился, восстановление контекста одно на всех, а вот сохранение конечно раздельное.

 

>> А переменные, которые Ваши состояния автомата используют их нет? Без переменных? В моём случае - они хранятся в регистрах и сохраняются/восстанавливаются одним и тем же кодом,

>> а у Вас - для каждого состояния придётся их на входе в каждое состояние заново читать, а после - записывать.

А у Вас этой записи нет?! А в стек и обратно Вы переменные не гоняете? ;) Мои переменные лежат там спокойно и работа с ними идет, когда нужно, а Вы туда-сюда, туда-сюда.

Я же написал уже, что у меня запись и восстановление оптимизированы по месту, а у Вас всегда пишутся и читаются все регистры, которые задействованы во всей функции поддержки автомата.

А Вы не поняли и недостаток пытаетесь выдать за достоинство.

 

>> Если уж Вас так волнует количество байт кода, то как раз мой вариант в любом случае будет короче.

>> Попробуйте на практике реализовать для автомата хотя бы с десятком состояний.

Это называется "на слабо" :) Да, наверное есть случаи, когда Ваш метод имеет смысл. Спасибо, я попробую.

 

 

>>Всё уже было. Если Вы не поняли - очевидно рано ещё, со временем может поймёте.

>>Но некоторые товарищи погрязли в однобайтном мирке, похоже никогда не видели исходников размером хотя-бы несколько сотен строк и похоже объяснять что-то бесполезно....

Спасибо, что снизошли!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...