Guten Tag,
ich weiß nicht ob es Euch auch immer wieder so geht das ihr in Euren
Programmen eine Flanken Erkennung braucht?
Ich benötige das immer wieder.
Im Prinzip ist dies kein Problem. Aber damit nicht jedes mal wieder das
Rad neu erfunden wird und vor Allem damit die Lesbarkeit gegeben ist
habe ich mir ein kleines Makro gebastelt.
1
#define RISING_AGE( DATA, BIT ) \
2
({ \
3
static uint8_t mem;\
4
uint8_t ret = 0;\
5
if ( (DATA & (1<<BIT))&&(mem==0))\
6
{\
7
mem=1;\
8
ret=1;\
9
}\
10
else\
11
{\
12
mem=0;\
13
ret=0;\
14
}\
15
ret;/* return value of Macro */ \
16
})
17
#define FALLING_AGE( DATA, BIT ) \
18
({ \
19
static uint8_t mem;\
20
uint8_t ret = 0;\
21
if ( (!(DATA & (1<<BIT)))&&(mem==0))\
22
{\
23
mem=1;\
24
ret=1;\
25
}\
26
else\
27
{\
28
mem=0;\
29
ret=0;\
30
}\
31
ret;/* return value of Macro */ \
32
})
Der Aufruf ist dann ganz Simple
1
if(RISING_AGE(pdo_Power[2],0))
2
{
3
// mach was.....
4
}
Nun zu meiner Frage.
Könnte man so was in c++ besser machen? Wäre es da möglich einfach eine
Funktion zu instanziieren?
Wäre das Ressourcen schonender?
Oder gibt es eine bessere Möglichkeit um die Lesbarkeit aufrecht zu
erhalten?
Lg
> if (RISING_AGE(pdo_Power[2],0))
Lass mal dein Makro expandieren und schau dir den Code dann an. Versuche
darin zu Debuggen und Fehler zu finden.
Endlich sind die Letzten von ASM runter und jetzt so was?
Boäh schrieb:> Lass mal dein Makro expandieren und schau dir den Code dann an. Versuche> darin zu Debuggen und Fehler zu finden.>> Endlich sind die Letzten von ASM runter und jetzt so was?
Kannst du mir das näher erklären?
Was meinst du mit expandieren?
Lg
P.S.: Auch in C keine Macros schreiben, die wie Funktionen aussehen ...
P.P.S: ... mach eine inline Funktion draus und vertrau dem Compiler
(ggf. zusammen mit LTO).
struppi schrieb:> Die Frage war ja wie kann man es schöner machen?
Das war Deine Frage, ja. Mir persönlich wäre es aber viel wichtiger, die
Sache nicht nur schön, sondern vor allem richtig zu machen. Deine Makros
haben nämlich leider ein paar Designfehler, und können so nicht in jedem
Fall korrekt funktionieren.
In C++ könnte man das etwa so machen (ungetestet):
Wilhelm M. schrieb:> mach eine inline Funktion draus und vertrau dem Compiler> (ggf. zusammen mit LTO).
Klingt interessant. Wenn innerhalb der Funktion eine Variable static
deklariert ist, wird dann bei öfteren Verwendung diese Variable öfter
angelegt?
Sheeva P. schrieb:> Edit: korrekt formatiert sieht einfach besser aus... ;-)
Danke für deine Mühe.
Was meisnst du mit korrekt formatiert?
Lg
struppi schrieb:> Sheeva P. schrieb:>> Edit: korrekt formatiert sieht einfach besser aus... ;-)>> Danke für deine Mühe.> Was meisnst du mit korrekt formatiert?
Daß das Forum mir beim ersten Posten meines Beitrages die Formatierung
zerschossen hat und ich sie deswegen mit einem Edit korrigieren mußte.
Sheeva P. schrieb:> struppi schrieb:>> Sheeva P. schrieb:>>> Edit: korrekt formatiert sieht einfach besser aus... ;-)>>>> Danke für deine Mühe.>> Was meisnst du mit korrekt formatiert?>> Daß das Forum mir beim ersten Posten meines Beitrages die Formatierung> zerschossen hat und ich sie deswegen mit einem Edit korrigieren mußte.
A Ok ich verstehe:)
kennst du dich mit inline aus?
Wilhelm M. schrieb:> P.P.S: ... mach eine inline Funktion draus und vertrau dem Compiler> (ggf. zusammen mit LTO).
Inline-Funktionen dürfen IIRC keine nicht-konstanten statischen
Variablen enthalten, die Variablem "mem" müßten dann also außerhalb der
Funktionen liegen. Ich glaube, die Makros fallen da auch auf die Nase.
;-)
Nein, das Problem an dem Code des TO ist, dass keine Flankenerkennung
durchgeführt wird. Es wird der statische Zustand ermittelt. Denn es
handelt sich um anonyme Blöcke, die statics werden also bei jedem Aufruf
mit 0 initialisiert.
Ein
Wilhelm M. schrieb:> Denn es> handelt sich um anonyme Blöcke, die statics werden also bei jedem Aufruf> mit 0 initialisiert.
Kann man das bitte näher erläutern.
Neugieriger schrieb:> Die Klasse sollte sich aber nur um Flanken kümmern, und nichts anderes:>> *Single Responsibility Principle!*
Schick! Trotzdem halte ich es für eine gute Idee, den Status fest an die
Port- und Registerkombination zu binden, um damit Fehler wie diesen hier
1
ed.update((PORTA&(1<<PA0)));
2
ed.update((PORTB&(1<<PB1)));
zu vermeiden. Das ändert aber nichts daran, daß Dein Code viel hübscher
ist als meiner. Ich sollte wieder mehr C++ coden. ;-)
Wilhelm M. schrieb:> Nein, das Problem an dem Code des TO ist, dass keine Flankenerkennung> durchgeführt wird. Es wird der statische Zustand ermittelt. Denn es> handelt sich um anonyme Blöcke, die statics werden also bei jedem Aufruf> mit 0 initialisiert.
Warum funktioniert es dann?
Warum Funktioniert den dann dieser CODE??
https://www.mikrocontroller.net/attachment/67964/debounce.c
Ich bin ein Anfänger bitte um nähere Erklärungen was das Static und
staic in Inline Funktionen betrifft.
Danke für Eure Bemühungen!
Lg
struppi schrieb:> Wilhelm M. schrieb:>> Nein, das Problem an dem Code des TO ist, dass keine Flankenerkennung>> durchgeführt wird. Es wird der statische Zustand ermittelt. Denn es>> handelt sich um anonyme Blöcke, die statics werden also bei jedem Aufruf>> mit 0 initialisiert.>> Warum funktioniert es dann?
Ich habe das Macro bewusst falsch verwendet: ein Macro ist eben keine
Funktion und sollte auch so nicht verwendet werden.
> Warum Funktioniert den dann dieser CODE??> https://www.mikrocontroller.net/attachment/67964/debounce.c
Steht ja eigentlich in dem Kommentar des Codes drin ...
Jede neue(!) Verwendung des Macros führt zu einem neuen(!) unbenannten
Block/Scope (hier als GCC-Erweiterung als
Compound-Statement-Expression). Und der hat seine eigenen lokalen
Objekte.
(Das kann Vorteile haben ... s.a. Dein zitiertes Beispiel, das für jeden
Pin
sinnvollerweise eine eigene static-Variable angelegt wird. In meinem
extra konstruierten Fall wird es aber falsch).
Wird das Ganze also in einer Schleife verwendet und pro Pin nur
einmal(!) das Macro benutzt, ist es richtig.
> Ich bin ein Anfänger bitte um nähere Erklärungen was das Static und> staic in Inline Funktionen betrifft.
Da empfehle ich ein C-Buch oder
http://en.cppreference.com/w/c/language
Wilhelm M. schrieb:> Wird das Ganze also in einer Schleife verwendet und pro Pin nur> einmal(!) das Macro benutzt, ist es richtig.
Also bei richtiger verwendung ginge es?
Wie würde sich das verhalten wenn ich anstelle von diesem Macro den Code
in eine Inline Funktion packen würde?
Würde dann bei jeden Aufruf die staic deklarierte Variable "mem" neu
verwendet?
Danke Lg
struppi schrieb:> Wilhelm M. schrieb:>> Wird das Ganze also in einer Schleife verwendet und pro Pin nur>> einmal(!) das Macro benutzt, ist es richtig.>> Also bei richtiger verwendung ginge es?
Ja ;-)
> Wie würde sich das verhalten wenn ich anstelle von diesem Macro den Code> in eine Inline Funktion packen würde?
Wie wäre es mit ausprobieren?
Wobei es in der o.g. Form nur als Funktion geschrieben wieder eine
andere Fehlerquelle beinhaltet (s.a. Post von sheevaplug).
> Würde dann bei jeden Aufruf die staic deklarierte Variable "mem" neu> verwendet?
Schlage mal die Bedeutung von static nach.
Wilhelm M. schrieb:> Schlage mal die Bedeutung von static nach.
Ich weiß das static deklarierte Variablen nach der ausfürung der
Funktion erhalten bleiben.
Was ich aber in meinem C Buch nicht finde wie das verhalten in einer
Inline Funktion ist!
Lg
Danke Für die Geduld.
Wilhelm M. schrieb:> Wie wäre es mit ausprobieren?
Funktioniert aber ich weiß nicht wie es sich verhaltet wenn sonderfälle
auftreten ;)
Wilhelm M. schrieb:> Einvolatile uint8_t p = 1;>> int main() {> if (RISING_AGE(p, 0)) {> printf("A\n");> }> if (RISING_AGE(p, 0)) {> printf("B\n");> }> }
So wie du mir oben gezeigt hats
Danke
Lg
Sheeva P. schrieb:> Schick! Trotzdem halte ich es für eine gute Idee, den Status fest an die> Port- und Registerkombination zu binden, um damit Fehler wie diesen hier> ed.update( (PORTA & (1<<PA0)) );> ed.update( (PORTB & (1<<PB1)) );>> zu vermeiden.
Im Prinzip hast du recht. Aber eine andere Sache ist gefährlicher:
Häufig soll der Zustand eines anderen Signals bei einer Flanke
verarbeitet werden. Wird das Auslesen eines Portpins in die Klasse
gezogen wird das ganze asynchron. Jede Interruptunterbrechung macht das
ganze inkonsistent.
Daher sollte die Klasse auch mit Daten aus einem gespiegelten Speicher
klarkommen und nicht nur mit direkten IO Ports.
struppi schrieb:> Wilhelm M. schrieb:>> Schlage mal die Bedeutung von static nach.>> Ich weiß das static deklarierte Variablen nach der ausfürung der> Funktion erhalten bleiben.> Was ich aber in meinem C Buch nicht finde wie das verhalten in einer> Inline Funktion ist!
Wie gesagt: hier entsteht das nä. Problem.
Du hast ja eingangs gefragt, obs mit OOP (C++) ggf. besser ginge. Also:
wir bewegen uns durch Selbsterkenntnis dahin ;-))
Zeige mal Deinen Code soweit ... dann schauen wir.
struppi schrieb:> Wilhelm M. schrieb:>> Zeige mal Deinen Code soweit ... dann schauen wir.>> Welchen? den meiner Inline Funktion?
Ja, alles was dazu relevant ist.
struppi schrieb:> Wilhelm M. schrieb:>> Schlage mal die Bedeutung von static nach.>> Ich weiß das static deklarierte Variablen nach der ausfürung der> Funktion erhalten bleiben.> Was ich aber in meinem C Buch nicht finde wie das verhalten in einer> Inline Funktion ist!
Inline bei Funktion bedeutet nur, daß der Compiler sich den Call sparen
soll und den Code bei jedem Aufruf direkt einfügt. Er kann das auch ohne
inline machen, wenn er meint der Call wäre zu aufwendig. Dadurch ändern
sich aber die anderen Eigenschaften der Funktion nicht. Die Variablen im
"Funktions-Scope" bleiben weiterhin außerhalb unsichtbar und static legt
genau eine Instanz einer Variablen an, die genau einmal, beim
Programmstart initialisiert wird.
Nicht durch die (inzwischen vielen) anderen Verwendungen von inline
verwirren lassen.
>>> so in etwa;)
Hieran sieht man, dass es (meistens) ungut ist, Funktionen mit internen
Zuständen (also keine Funktionen mehr im engeren Sinn) zu schreiben.
Denn wendest Du Deine Funktionen erneut an auf einen anderen Pin /
Objekt
1
if(steigende(pdo_PowerCtrlA[2],7)){
2
...
3
}
wirds wieder falsch. Denn Du / der Programmierer muss sich nun merken,
dass der interne Zustand der Funktion für Bit 0 in pcd_PowerCtrl[2] gilt
und sonst für nichts anderes.
Folglich wäre es besser:
was nichts anderes bedeutet, als dass wir den internen Zustand
externalisiert haben. Damit haben wir eine Funktion, die auf ein Objekt
vom Type flag_t angewendet wird. Das kann man objektbasierte
Programmierung nennen.
Nun ist muss der Programmierer explizit sagen, welche Flags benutzt
werden sollen, und macht hoffentlich keinen Fehler (aber optimal ist das
ja noch nicht!).
Jetzt kannst Du es leicht nach OOP (C++) umbauen.
Die nächste Stufe wäre nun, dass man die Flags ausschließlich einem
bestimmten Pin zur Flankenüberwachung zuordnen kann (das kann man jetzt
noch falsch machen). Das könnte man mit dem Ansatz ganz oben von mir
sicherstellen ...
Carl D. schrieb:> struppi schrieb:>> Wilhelm M. schrieb:>>> Schlage mal die Bedeutung von static nach.>>>> Ich weiß das static deklarierte Variablen nach der ausfürung der>> Funktion erhalten bleiben.>> Was ich aber in meinem C Buch nicht finde wie das verhalten in einer>> Inline Funktion ist!>> Inline bei Funktion bedeutet nur, daß der Compiler sich den Call sparen> soll und den Code bei jedem Aufruf direkt einfügt.
Jaein, "soll" ist hier aber kein "muss". Das darf er aber auch sonst
nach der as-if-rule.
inline ist hauptsächlich dazu da, die ODR nicht zu verletzten.
Wilhelm M. schrieb:> was nichts anderes bedeutet, als dass wir den internen Zustand> externalisiert haben. Damit haben wir eine Funktion, die auf ein Objekt> vom Type flag_t angewendet wird. Das kann man objektbasierte> Programmierung nennen.>> Nun ist muss der Programmierer explizit sagen, welche Flags benutzt> werden sollen, und macht hoffentlich keinen Fehler (aber optimal ist das> ja noch nicht!).>> Jetzt kannst Du es leicht nach OOP (C++) umbauen.>> Die nächste Stufe wäre nun, dass man die Flags ausschließlich einem> bestimmten Pin zur Flankenüberwachung zuordnen kann (das kann man jetzt> noch falsch machen). Das könnte man mit dem Ansatz ganz oben von mir> sicherstellen ...
Ok ich verstehe! Also gibt es keine Möglichkeit in c eine Instanz einer
Funktion zu deklarieren?!
Macht C++ also OOP auf einem 8 Bit Maschine Sinn? Ich habe hier einen
At90Can128 im Einsatz.
Im Prinzip ist die Macro Methode wenn nur einmal mit selber Variable
verwendet am leichtesten anzuwenden, Oder?
Lg
Ps Danke für die Fachliche Kompetenz!
struppi schrieb:> Wilhelm M. schrieb:>> was nichts anderes bedeutet, als dass wir den internen Zustand>> externalisiert haben. Damit haben wir eine Funktion, die auf ein Objekt>> vom Type flag_t angewendet wird. Das kann man objektbasierte>> Programmierung nennen.>>>> Nun ist muss der Programmierer explizit sagen, welche Flags benutzt>> werden sollen, und macht hoffentlich keinen Fehler (aber optimal ist das>> ja noch nicht!).>>>> Jetzt kannst Du es leicht nach OOP (C++) umbauen.>>>> Die nächste Stufe wäre nun, dass man die Flags ausschließlich einem>> bestimmten Pin zur Flankenüberwachung zuordnen kann (das kann man jetzt>> noch falsch machen). Das könnte man mit dem Ansatz ganz oben von mir>> sicherstellen ...>> Ok ich verstehe! Also gibt es keine Möglichkeit in c eine Instanz einer> Funktion zu deklarieren?!
Was meinst Du damit? Ein Funktionsobjekt? Ein Closure?
> Macht C++ also OOP auf einem 8 Bit Maschine Sinn? Ich habe hier einen> At90Can128 im Einsatz.
Nach meiner Meinung: absolut! Allerdings muss sollte man sich im
wesentlichen auf statische Polymorphie beschränken, TMP benutzen und
auch Exceptions aussen vor lassen (gehen eh nicht beim avr-g++).
Generell kann man das gleiche machen wie in C und noch sehr, sehr viel
mehr. Was m.E. zu besserem (in vielerlei Hinsicht) Code führt (teilweise
sogar schneller).
Aber es werden jetzt gleich ganz viele Stimmen kommen, dass C++ Quatsch
ist ;-))
> Im Prinzip ist die Macro Methode wenn nur einmal mit selber Variable> verwendet am leichtesten anzuwenden, Oder?
Wie gesagt: wenn man keinen Fehler macht, ist so ziemlich alles ok.
M.E: geht es aber immer um ein ganz wesentliches Grundprinzip: Eine
Schnittstelle sollte einfach richtig und schwer falsch zu benutzen sein.
Und das erfüllt die Macro Methode (auch noch aus anderen Gründen) nicht.
Wilhelm M. schrieb:> Denn wendest Du Deine Funktionen erneut an auf einen anderen Pin /> Objekt> ...> wirds wieder falsch.
Man kann es auch anders herum sehen:
Wenn man mit dem Makro für jeden abzufragenden Pin jeweils genau eine
Stelle im Source wählt, dann funktioniert alles wie gewünscht, denn dann
gibt es für jeden abzufragenden Pin gibt es genau einen "Speicher" mem.
Die Externalisierung ist dann nicht notwendig. Und so wird das Makro
zumindest einfacher anzuwenden als jede (inline-)Funktion.
struppi schrieb:> Im Prinzip ist die Macro Methode wenn nur einmal mit selber Variable> verwendet am leichtesten anzuwenden,
Ja.
Ja, ich weiß: Makro-Hassern dreht sich da der Magen um. Aber jeder
Versuch, das Makro durch Alternativlösungen zu schlagen, wird auf jeden
Fall komplizierter - auch wenn der Compiler es schaffen sollte, trotz
"Externalisierung" denselben Code wie beim Makro zu erzeugen. Der
Binärcode wird auf jeden Fall nicht besser oder performanter.
Wilhelm M. schrieb:> Was meinst Du damit? Ein Funktionsobjekt? Ein Closure?
Ich meine einfach eine Art um die Funktion zwei drei ....X mal zu
verwenden und das die variablen die stisch sein müssen einfach neu
angelegt werden im Hintergrund ohne das ich einen Fehler machen kann.
Wilhelm M. schrieb:> TMP benutzen
Was ist das?
Gibt es ein Beispiel wie das Atmel Studio7 eingestellt werden muß damit
ich c++ verwenden kann?
Danke für deine guten Erklärungen!!!!
Frank M. schrieb:> Wenn man mit dem Makro für jeden abzufragenden Pin jeweils genau eine> Stelle im Source wählt, dann funktioniert alles wie gewünscht, denn dann> gibt es für jeden abzufragenden Pin gibt es genau einen "Speicher" mem.
Wie gesagt: wenn man es richtig macht, ist ja alles ok. (Oben hatte ich
zur Erläuterung ein Beispiel gebracht, wie es leicht falsch wird).
> Die Externalisierung ist dann nicht notwendig. Und so wird das Makro> zumindest einfacher anzuwenden als jede (inline-)Funktion.
Mehr ist bei reinem C m.E. nicht möglich.
> struppi schrieb:>> Im Prinzip ist die Macro Methode wenn nur einmal mit selber Variable>> verwendet am leichtesten anzuwenden,>> Ja.>> Ja, ich weiß: Makro-Hassern dreht sich da der Magen um. Aber jeder> Versuch, das Makro durch Alternativlösungen zu schlagen, wird auf jeden> Fall komplizierter - auch wenn der Compiler es schaffen sollte, trotz> "Externalisierung" denselben Code wie beim Makro zu erzeugen.
Das sollte er schaffen ;-)
> Der> Binärcode wird auf jeden Fall nicht besser oder performanter.
Ich denke, er ist identisch.
Allerdings: wenn es die Anwendung zulässt, dass man die Flags als lokale
Variable (etwa in main) repräsentiert, wird der Ansatz mit
Externalisierung effizienter.
Ich mache die Flankenerkennung immer zusammen mit der Entprellung und
parallel für einen kompletten IO-Port. Das bischen Code wird in einen
Timerinterrupt mit eingefügt. Das Main muß dann nur noch das Ereignis
abholen.
struppi schrieb:> Wilhelm M. schrieb:>> Was meinst Du damit? Ein Funktionsobjekt? Ein Closure?>> Ich meine einfach eine Art um die Funktion zwei drei ....X mal zu> verwenden und das die variablen die stisch sein müssen einfach neu> angelegt werden im Hintergrund ohne das ich einen Fehler machen kann.
Dann wären wir in C++ bei einem Funktor angelangt.
> Wilhelm M. schrieb:>> TMP benutzen>> Was ist das?
Template-Meta-Programmierung.
> Gibt es ein Beispiel wie das Atmel Studio7 eingestellt werden muß damit> ich c++ verwenden kann?
Da kann ich leider nicht weiterhelfen, das benutze ich nicht.
Peter D. schrieb:> Ich mache die Flankenerkennung immer zusammen mit der Entprellung und> parallel für einen kompletten IO-Port. Das bischen Code wird in einen> Timerinterrupt mit eingefügt. Das Main muß dann nur noch das Ereignis> abholen.
Ich kenne deine ausgezeichnete Entprellung.
Ich benötige aber oft eine Flankenerkennung bei Variablen die über zB
Can rein kommen.
Deine Variante erkennt die fallende Flanke ich benötige aber beide.
Außerdem müsste ich deine Entprellung ausbohren und und mittels
Hilfsvariable zB 16 bit zusammen bauen.
Deshalb finde ich so eine Funktion oder Makro besser und wartbarer.
Wilhelm M. schrieb:> Da kann ich leider nicht weiterhelfen, das benutze ich nicht.
Kein Problem hast mir ja sonst viel bei gebracht;)
Lg
struppi schrieb:> Peter D. schrieb:>> Ich mache die Flankenerkennung immer zusammen mit der Entprellung und>> parallel für einen kompletten IO-Port. Das bischen Code wird in einen>> Timerinterrupt mit eingefügt. Das Main muß dann nur noch das Ereignis>> abholen.>> Ich kenne deine ausgezeichnete Entprellung.> Ich benötige aber oft eine Flankenerkennung bei Variablen die über zB> Can rein kommen.> Deine Variante erkennt die fallende Flanke ich benötige aber beide.> Außerdem müsste ich deine Entprellung ausbohren und und mittels> Hilfsvariable zB 16 bit zusammen bauen.
Peters Flankenerkennung merkt sich diese auch bis sie jemand abfragt,
wärend oben eine C++-Version zu sehen war, die die erkannten Flanken
schon beim nächsten Takt wieder vergessen hat. Nur "alt XOR neu" ist
eher ein Hochpassfilter.
Marc schrieb:> Sheeva P. schrieb:>> Schick! Trotzdem halte ich es für eine gute Idee, den Status fest an die>> Port- und Registerkombination zu binden, um damit Fehler wie diesen hier>> ed.update( (PORTA & (1<<PA0)) );>> ed.update( (PORTB & (1<<PB1)) );>> zu vermeiden.>> Im Prinzip hast du recht. Aber eine andere Sache ist gefährlicher:> Häufig soll der Zustand eines anderen Signals bei einer Flanke> verarbeitet werden. Wird das Auslesen eines Portpins in die Klasse> gezogen wird das ganze asynchron. Jede Interruptunterbrechung macht das> ganze inkonsistent.>> Daher sollte die Klasse auch mit Daten aus einem gespiegelten Speicher> klarkommen und nicht nur mit direkten IO Ports.
Sheeva P. schrieb:> Wer kein std::functional hat, kann natürlich auch Funktionszeiger> benutzen.
Lieber nicht. Auf einem Microcontroller hat man ja nicht unvorhersehbar
viele Edge-Inputs, sondern nur genau so viele wie die angeschlossene
Hardware hergibt. Deshalb ist es viel besser, die Klasse gleich als
Template zu gestalten, wobei der updater ein Template-Parameter ist. Ich
wette, dass in typischen Anwendungsfällen der Code am Ende kleiner ist,
selbst wenn man, sagen wir, 5 EdgeDetectoren instanziieren muss.
BTW, ich würde außerdem versuchen, die 3 bool Variablen in ein Byte zu
packen:
1
boololdstate:1;
2
boolrise:1;
3
boolfall:1;
Wobei ich zugebe, dass ich in meinem Leben noch nie bools in ein
Bitfield gequetscht habe.
Sheeva P. schrieb:> Wer kein std::functional hat, kann natürlich auch Funktionszeiger> benutzen.
Evtl. ist die Einschränkung auf Callables (void) -> bool etwas zu
einschränkend. In solchen fällen kann man sehr gut class template
argument deduction (ggf. mit einem deduction guide) verwenden:
1
template<typenameUpdater>
2
classEdgeDetector{
3
public:
4
EdgeDetector(Updaterf):mF{f}{}
5
template<typenameA=void>
6
voidupdate(constA&arg){
7
mF(arg);
8
}
9
voidupdate(){
10
mF();
11
}
12
private:
13
UpdatermF;
14
};
Man sollte in meinem Code oben noch die Überladung durch SFINAE
ersetzen...
tictactoe schrieb:> BTW, ich würde außerdem versuchen, die 3 bool Variablen in ein Byte zu> packen:>
1
>boololdstate:1;
2
>boolrise:1;
3
>boolfall:1;
4
>
> Wobei ich zugebe, dass ich in meinem Leben noch nie bools in ein> Bitfield gequetscht habe.
Kann man aber gut machen. Gibt halt mehr Flash (Assembler Code), weniger
RAM.
Mittlerweile habe ich mir angewöhnt, so etwas auch generisch zu lösen.
Dann kann ich wählen per TemplateParameter, ob einzelne bools, ein
BitField, oder ggf. auch ein GPIOR (bspw. bei AVR sehr effizient wenn
sbi/cbi fähig) benutzt wird.
tictactoe schrieb:> Auf einem Microcontroller hat man ja nicht unvorhersehbar> viele Edge-Inputs, sondern nur genau so viele wie die angeschlossene> Hardware hergibt. Deshalb ist es viel besser, die Klasse gleich als> Template zu gestalten, wobei der updater ein Template-Parameter ist. Ich> wette, dass in typischen Anwendungsfällen der Code am Ende kleiner ist,> selbst wenn man, sagen wir, 5 EdgeDetectoren instanziieren muss.
Das käme auf einen Versuch an.
> BTW, ich würde außerdem versuchen, die 3 bool Variablen in ein Byte zu> packen:
Da hatte ich auch schon 'dran gedacht und wollte es in meinem Posting
eigentlich auch erwähnt haben, aber...
Wilhelm M. schrieb:> Evtl. ist die Einschränkung auf Callables (void) -> bool etwas zu> einschränkend.
Ich hatte darüber nachgedacht, es dann aber ganz absichtlich so gemacht.
Der Status kann in diesem Fall ja eh nur ein Bool sein, und deswegen ist
es IMHO korrekt, den Rückgabewert fest darauf zu beschränken. Und weil
ich das Ganze festnageln und gerade nicht von externen Parametern
abhängig machen, sondern im Gegenteil unbeabsichtigte Fehler verhindern
will, soll diese Funktion gar keine Parameter annehmen können. Wer das
trotzdem unbedingt haben will, kann das ja wahlweise als Template oder
über Vererbung realisieren.