Кому-то - баян, кому-то, возможно, покажется достойным применения. Итак: некоторые библиотеки (боже, как бесит это слово!) работают в поле событийного интерфейса путём назначения коллбэков, простейший пример тому - Wire.onReceive(handler). Этот подход к реализации событий многим известен и многими практикуется, однако, наряду с достоинствами - у этого подхода есть и определённые недостатки, хотя бы тот факт, что указатель на функцию-обработчик надо где-то хранить (а это место в оперативной памяти).
В дополнение к этому - существует ещё один вполне себе годный подход, который применяется при объявлении той же функции yield, а именно - позднее связывание. Если совсем короче, то - любой функции можно назначить альяс (синоним, заглушку, если хотите), и если функция не определена - то на стадии линковки все вызовы такой функции заменятся на вызовы заглушки. Что нам это даёт? Как минимум - упрощение кода, вместо:
1 | if (onReceiveHandler) onReceiveHandler(bla, bla,bla); |
Достаточно написать просто
1 | onReceiveHandler(bla,bla,bla); |
Из ограничений, навскидку - тот факт, что таким образом реализованная событийность будет всегда ссылаться на одну функцию, вне зависимости от экземпляра класса. Но и это - не всегда минус, скажем так.
Как это работает и как это сделать? Всё просто - объявляете функцию:
2 | void MY_RECEIVE( int bytesReceived); |
Потом - делаете её альяс:
2 | static void __nohandler( int dummy){} |
Потом - указываете, что функция - позднего связывания и имеет заглушку:
1 | void MY_RECEIVE( int ) __attribute__ ((weak, alias( "__nohandler" ))); |
Всё - теперь вы в любом месте проекта сможете позвать MY_RECEIVE (например, с параметром 10 - MY_RECEIVE(10); ) - и не париться, куда попадёт вызов: если желающий принимать это событие определит функцию, а именно - напишет в исходном коде:
1 | void MY_RECEIVE( int bytesReceived) |
3 | Serial .print(F( "bytes received: " )); |
4 | Serial .println(bytesReceived); |
то ваш вызов функции MY_RECEIVE попадёт именно в реализованный подписчиком обработчик. Если же функции MY_RECEIVE нигде не определено - её вызов переадресуется на заглушку, а именно - на функцию __nohandler. Что, кстати, даёт нам ещё один инструмент - обработка событий по умолчанию ;)
Надеюсь, данная поверхностная заметка будет кому-либо полезна.
А я, дэбил, целую очередь сообщений напридумывал. А все так просто.... Спасибо, плюсую.
Ща набегут! Ждите :))) Дет уже попробовал писать что-то общее, теперь вон просит тему снести, у меня тоже опыт был.
Ща набегут! Ждите :))) Дет уже попробовал писать что-то общее, теперь вон просит тему снести, у меня тоже опыт был.
Да пофик, собсно. Это я больше для себя, чтоб от маразму-то не забыть. Главное потом - вспомнить, где лежит это напоминание, а то и это забыть - невелико дело :) :) :)
Так шо пущай пинають :)
Мне то чо, я на вечерний пузырь сёдня уже наразгружался ящегов. Но красивые решения оценить смогу. Клапа не паймёть, а Логика бойтесь.
Ща набегут! Ждите :))) Дет уже попробовал писать что-то общее, теперь вон просит тему снести
Пока Вы спали мня Клапа убедил, что я полный кретин. Он лучше знает.
Вот только, что такое событийное программирование, и как правильно его организовать на Ардуине, не все знают. И чаще всего получается событийное программирование на костылях.
Вот только, что такое событийное программирование, и как правильно его организовать на Ардуине, не все знают. И чаще всего получается событийное программирование на костылях.
Лично у меня нет цели - дать всем узнать. Тут всё проще - я просто разместил объяву :)
Пока Вы спали мня Клапа убедил, что я полный кретин.
Значит, я в хорошей компании!
Надо нам с Вами по такому слуаю накатить за ... ну, хоть за денадцатый пин!
Надо нам с Вами по такому слуаю накатить за ... ну, хоть за двенадцатый пин!
От такова я никада не отказывалса.
Надо нам с Вами по такому слуаю накатить за ... ну, хоть за двенадцатый пин!
От такова я никада не отказывалса.
А jeka_tm давно уже всех звал собраться.
http://arduino.ru/forum/otvlechennye-temy/sobiraemsya-v-pivnushke
ну да, к чёрту подробности, город какой?
ну да, к чёрту подробности, город какой?
Москва.
Как бы это не странно для некоторых звучало , но событийное программирование полностью похоже на программирование с помощью прерываний. И там и там используются обработчики. Но для событийного можно делать не только аппаратное прерывание, но и программное.
Клапа не паймёть, а Логика бойтесь.
)))
Это атрибут функции, так понимаю, из свежих расширений языка? Любопытно, никогда такого не встречал на практике. Но не думаю что сильно полезно, в ООП методы при наследовании аналогично позволяют реализовать. И если не требуется иметь совсем чистый код на Си, то проще (и сильно гибче ) делать класс. Ну к примеру в либке базовый класс (надергаю немного из подвернувшегося кода, его Клапа сильно мечтал видеть ;)
01
class
BaseKBD
02
{
03
virtual
void
SetStrob(
byte
n,
byte
s);
04
05
protected
:
06
byte
Strob;
07
void
SelectLongPress(
byte
evnt,
byte
key){KeyEvent(evnt, key); }
08
09
public
:
10
......
11
12
byte
Process()
13
{
14
15
SetStrob(Strob, HIGH);
16
Strob++;
17
if
(Strob>StrobCount)
18
Strob=0;
19
SetStrob(Strob, LOW);
20
21
22
SelectLongPress(0,1);
23
}
24
};
А в скетче наследуем класс от библиотечного
1
class
KBD :
public
BaseKBD
2
{
3
// работаем напрямую с портом
4
void
SetStrob(
byte
n,
byte
s) { s?sbi(PORTC,n):cbi(PORTC,n); }
5
..............
6
};
Получим что SelectLongPress можна свою задать, а можна и нет. А SetStrob надо обязательно определить, иначе не компилируется, но в базовом он не определен, хотя и использован.
ПС. Такое ощущение, что учу рыбу плавать. Кто сюда заглянет - и так знают это, а кто не знает - в эту тему и не глянет )))
Не, не костыль, просто фича. И еще один аргумент за то что на Си можна сделать все что и на плюсах. Нынче в контролерах не только чистые сишники колупаются, но и нечистые ;)
нихрена не понял, но чую, что СИЁ можно использовать как обработчик ошибок )))
Прилеплюсь со своим вариантом, который кажется мне попроще в понимании.
В src\plugin.* расположен код заглушек функций, в plugin.ino - перекрывающий их код. Захотели свою плагин-функцию сделать - написали и подкинули plugin.ino к основному файлу проекта, разонравилась функцональность - стерли или заменили на другой. Планирую использовать эту штуку для сетевого агента, к которому пользователь может добавить свою небольшую функциональность (отбить SOS, например), не залезая в основной код. За идею - благодарю.
src\plugin.h
1
#pragma once
2
void
__attribute__((weak)) blinkLed();
src\plugin.cpp
1
#include <Arduino.h>
2
3
void
__attribute__((weak)) blinkLed() {
4
digitalWrite(13, HIGH);
5
delay(100);
6
digitalWrite(13, LOW);
7
delay(100);
8
}
plugin.ino
1
void
blinkLed(
void
) {
2
digitalWrite(13, HIGH);
3
delay(1000);
4
digitalWrite(13, LOW);
5
delay(1000);
6
}
plugin_test.ino
1
#include "src/plugin.h"
2
3
void
setup
() {
4
pinMode(13, OUTPUT);
5
}
6
7
void
loop
() {
8
blinkLed();
9
}
Перекомпиливал старый код в новом Arduino IDE 1.8.9 и обнаружил, что мой простой способ (тот, что выше) более не хочет работать.
Случайным образом (как те сто тыщ обезъян) сгенерировал новый пример (из plugin.cpp и plugin.c необходимо выбрать только один).
src\plugin.h
1
#pragma once
2
3
extern
"C"
void
userBlink();
src\plugin.c
01
#include <Arduino.h>
02
03
static
void
__defaultBlink() {
04
digitalWrite(13, HIGH);
05
delay(100);
06
digitalWrite(13, LOW);
07
delay(100);
08
09
}
10
11
void
userBlink() __attribute__ ((weak, alias(
"__defaultBlink"
)));
src\plugin.cpp
01
#include <Arduino.h>
02
03
extern
"C"
{
04
void
userBlink(
int
) __attribute__ ((weak, alias(
"__defaultBlink"
)));
05
static
void
__defaultBlink();
06
}
07
08
static
void
__defaultBlink() {
09
digitalWrite(13, HIGH);
10
delay(100);
11
digitalWrite(13, LOW);
12
delay(100);
13
}
plugin.ino
1
// userBlink() function can be omitted, and defaultBlink() will be used
2
void
userBlink() {
3
digitalWrite(13, HIGH);
4
delay(1000);
5
digitalWrite(13, LOW);
6
delay(1000);
7
}
plugin_test.ino
1
#include "src/plugin.h"
2
3
void
setup
() {
4
pinMode(13, OUTPUT);
5
}
6
7
void
loop
() {
8
userBlink();
9
}
Если кто-то расскажет, почему в arduino\hardware\arduino\avr\cores\arduino\hooks.c связывание и реализация прокатывает без extern "C", а у меня - нет (хотя я уже докатился до прямой копипасты оттуда и переименовывания своего plugin.cpp в plugin.c) - облагодарю "поклоном до земли (R)"
P.S. с 10050-го раза IDE сдалось копипасте, пример подправил. Файл реализации должен или иметь расширение .c или иметь внутри себя блоки extern "C" для функций, которые описаны в аналогичных блоках заголовочного файла.