Событийное программирование при помощи "слабых" функций (позднего связывания)
- Войдите на сайт для отправки комментариев
Кому-то - баян, кому-то, возможно, покажется достойным применения. Итак: некоторые библиотеки (боже, как бесит это слово!) работают в поле событийного интерфейса путём назначения коллбэков, простейший пример тому - Wire.onReceive(handler). Этот подход к реализации событий многим известен и многими практикуется, однако, наряду с достоинствами - у этого подхода есть и определённые недостатки, хотя бы тот факт, что указатель на функцию-обработчик надо где-то хранить (а это место в оперативной памяти).
В дополнение к этому - существует ещё один вполне себе годный подход, который применяется при объявлении той же функции yield, а именно - позднее связывание. Если совсем короче, то - любой функции можно назначить альяс (синоним, заглушку, если хотите), и если функция не определена - то на стадии линковки все вызовы такой функции заменятся на вызовы заглушки. Что нам это даёт? Как минимум - упрощение кода, вместо:
if(onReceiveHandler) onReceiveHandler(bla, bla,bla);
Достаточно написать просто
onReceiveHandler(bla,bla,bla);
Из ограничений, навскидку - тот факт, что таким образом реализованная событийность будет всегда ссылаться на одну функцию, вне зависимости от экземпляра класса. Но и это - не всегда минус, скажем так.
Как это работает и как это сделать? Всё просто - объявляете функцию:
extern "C" { void MY_RECEIVE(int bytesReceived); }
extern "C" { static void __nohandler(int dummy){} }
Потом - указываете, что функция - позднего связывания и имеет заглушку:
void MY_RECEIVE(int) __attribute__ ((weak, alias("__nohandler")));
Всё - теперь вы в любом месте проекта сможете позвать MY_RECEIVE (например, с параметром 10 - MY_RECEIVE(10); ) - и не париться, куда попадёт вызов: если желающий принимать это событие определит функцию, а именно - напишет в исходном коде:
void MY_RECEIVE(int bytesReceived) { Serial.print(F("bytes received: ")); Serial.println(bytesReceived); }
то ваш вызов функции MY_RECEIVE попадёт именно в реализованный подписчиком обработчик. Если же функции MY_RECEIVE нигде не определено - её вызов переадресуется на заглушку, а именно - на функцию __nohandler. Что, кстати, даёт нам ещё один инструмент - обработка событий по умолчанию ;)
Надеюсь, данная поверхностная заметка будет кому-либо полезна.
А я, дэбил, целую очередь сообщений напридумывал. А все так просто.... Спасибо, плюсую.
Ща набегут! Ждите :))) Дет уже попробовал писать что-то общее, теперь вон просит тему снести, у меня тоже опыт был.
Ща набегут! Ждите :))) Дет уже попробовал писать что-то общее, теперь вон просит тему снести, у меня тоже опыт был.
Да пофик, собсно. Это я больше для себя, чтоб от маразму-то не забыть. Главное потом - вспомнить, где лежит это напоминание, а то и это забыть - невелико дело :) :) :)
Так шо пущай пинають :)
Мне то чо, я на вечерний пузырь сёдня уже наразгружался ящегов. Но красивые решения оценить смогу. Клапа не паймёть, а Логика бойтесь.
Ща набегут! Ждите :))) Дет уже попробовал писать что-то общее, теперь вон просит тему снести
Пока Вы спали мня Клапа убедил, что я полный кретин. Он лучше знает.
Вот только, что такое событийное программирование, и как правильно его организовать на Ардуине, не все знают. И чаще всего получается событийное программирование на костылях.
Вот только, что такое событийное программирование, и как правильно его организовать на Ардуине, не все знают. И чаще всего получается событийное программирование на костылях.
Лично у меня нет цели - дать всем узнать. Тут всё проще - я просто разместил объяву :)
Пока Вы спали мня Клапа убедил, что я полный кретин.
Значит, я в хорошей компании!
Надо нам с Вами по такому слуаю накатить за ... ну, хоть за денадцатый пин!
Надо нам с Вами по такому слуаю накатить за ... ну, хоть за двенадцатый пин!
От такова я никада не отказывалса.
Надо нам с Вами по такому слуаю накатить за ... ну, хоть за двенадцатый пин!
От такова я никада не отказывалса.
А jeka_tm давно уже всех звал собраться.
http://arduino.ru/forum/otvlechennye-temy/sobiraemsya-v-pivnushke
ну да, к чёрту подробности, город какой?
ну да, к чёрту подробности, город какой?
Москва.
Как бы это не странно для некоторых звучало , но событийное программирование полностью похоже на программирование с помощью прерываний. И там и там используются обработчики. Но для событийного можно делать не только аппаратное прерывание, но и программное.
Клапа не паймёть, а Логика бойтесь.
)))
Это атрибут функции, так понимаю, из свежих расширений языка? Любопытно, никогда такого не встречал на практике. Но не думаю что сильно полезно, в ООП методы при наследовании аналогично позволяют реализовать. И если не требуется иметь совсем чистый код на Си, то проще (и сильно гибче ) делать класс. Ну к примеру в либке базовый класс (надергаю немного из подвернувшегося кода, его Клапа сильно мечтал видеть ;)
А в скетче наследуем класс от библиотечного
Получим что SelectLongPress можна свою задать, а можна и нет. А SetStrob надо обязательно определить, иначе не компилируется, но в базовом он не определен, хотя и использован.
ПС. Такое ощущение, что учу рыбу плавать. Кто сюда заглянет - и так знают это, а кто не знает - в эту тему и не глянет )))
Не, не костыль, просто фича. И еще один аргумент за то что на Си можна сделать все что и на плюсах. Нынче в контролерах не только чистые сишники колупаются, но и нечистые ;)
нихрена не понял, но чую, что СИЁ можно использовать как обработчик ошибок )))
Прилеплюсь со своим вариантом, который кажется мне попроще в понимании.
В src\plugin.* расположен код заглушек функций, в plugin.ino - перекрывающий их код. Захотели свою плагин-функцию сделать - написали и подкинули plugin.ino к основному файлу проекта, разонравилась функцональность - стерли или заменили на другой. Планирую использовать эту штуку для сетевого агента, к которому пользователь может добавить свою небольшую функциональность (отбить SOS, например), не залезая в основной код. За идею - благодарю.
src\plugin.h
src\plugin.cpp
plugin.ino
plugin_test.ino
Перекомпиливал старый код в новом Arduino IDE 1.8.9 и обнаружил, что мой простой способ (тот, что выше) более не хочет работать.
Случайным образом (как те сто тыщ обезъян) сгенерировал новый пример (из plugin.cpp и plugin.c необходимо выбрать только один).
src\plugin.h
src\plugin.c
src\plugin.cpp
plugin.ino
plugin_test.ino
Если кто-то расскажет, почему в arduino\hardware\arduino\avr\cores\arduino\hooks.c связывание и реализация прокатывает без extern "C", а у меня - нет (хотя я уже докатился до прямой копипасты оттуда и переименовывания своего plugin.cpp в plugin.c) - облагодарю "поклоном до земли (R)"
P.S. с 10050-го раза IDE сдалось копипасте, пример подправил. Файл реализации должен или иметь расширение .c или иметь внутри себя блоки extern "C" для функций, которые описаны в аналогичных блоках заголовочного файла.