Hallo Forum, ich programmiere seit kurzen für den Arduino. Man kommt ja effektiv nicht um C++ und eine ordentliche Kapselung herum, wenn man sein Programm flexibel halten möchte. Nun stehe ich vor dem Problem, dass ich eine Ereignisbehandlung benötige und das gerne so ähnlich wie bei .NET haben möchte. D.h. konkret, dass meine Screen Klasse andere Klassen wie bspw. meine eigene Button Klasse instanziiert. Und in der Screen Klasse möchte ich da mehrere Member Functions als Klick Handler für unterschiedliche Button Instanzen haben. Ich bin noch nicht so der C++ Experte, deswegen hänge ich gerade etwas an der Problemaatik. Also Observer Pattern fällt für mich aus, weil ich dann ständig alle Instanzen in der Methode checken müsste, um bedingt reagieren zu können. std::function gibt es wohl für Arduino nicht. Function Pointer scheinen so erst mal auch nicht zu funktionieren, wenn man Member Functions zuweisen will. Ich habe mal ein Beispiel gesehen, wo man eine eigene Event Klasse hatte, die über Templates konkrete T Function Pointer entgegen nimmt, aber habs nicht mehr gefunden. Hat vielleicht jemand eine Idee, wie ich am besten mein Event System umsetze? Danke im Voraus! Gruß, 640KB
Bert S. schrieb: > Man kommt ja effektiv > nicht um C++ und eine ordentliche Kapselung herum, wenn man sein > Programm flexibel halten möchte. Schwurbel.
Arduino ist C++. Was hindert dich also daran? Lern dann erst einmal die Sprache. Allerdings ist zu bedenken, dass je nach Architektur wenig Speicher zur Verfügung steht. Und dass alles was mit dynamischen Speicher arbeitet, irgendwann das System schrottet. (Stichwort Trashing) Vielleicht solltest du solche Konzepte lieber auf größeren Plattformen umsetzen. Aber nicht auch einem AtMega mit 8KByte SRAM.
Bert S. schrieb: > Also Observer Pattern fällt für mich aus, weil ich dann ständig alle > Instanzen in der Methode checken müsste, um bedingt reagieren zu können. Du könntest das mit einem User-Data-Argument o.ä. machen. Oder mehrere Ereignishandler-Klassen, eine pro Button. Oder eine Konstruktion mit CRTP-Observer-Klasse und Mehrfach-Ableitung um die Callback-Funktion praktisch mehrfach zu überschreiben. Das kann ich nachher mal ausführen. Bert S. schrieb: > Function Pointer scheinen so erst mal auch nicht zu funktionieren, Doch, allerdings muss es für nicht-statische Funktionen ein Member-Function-Pointer sein. Das zu nutzen braucht etwas template-Bastelei und type erasure. Dass jetzt hier sofort die C++-Hasser mit ihren Mistgabeln und Fackeln kommen war klar, lass dich davon nicht entmutigen. Das geht alles wunderbar, auch auf einem AVR. Nur mit dynamischem Speicher muss man aufpassen. Strategy Pattern und Member-Function-Pointer benutze ich ständig auf Mikrocontrollern, das ist überhaupt kein Problem.
Hallo Programmierer, danke für die Antwort. Das mit den Templates werde ich mir noch mal anschauen. Ich hatte das auch irgendwo schon mal gesehen. Ja, dynamisch Speicher allokieren im Sinne von zeitlich während der Verarbeitung/Loop sollte man vermutlich vermeiden, wegen der Fragmentierung. Aber wenn man sozusagen alles notwendige am Anfang allokiert und dann alles über ctor injeziert, sollte es ja gehen. Gruß, 640KB
Bert S. schrieb: > Ich habe mal ein Beispiel gesehen, > wo man eine eigene Event Klasse hatte, die über Templates konkrete T > Function Pointer entgegen nimmt, aber habs nicht mehr gefunden. im Mbed-os gibt es sowas, ist aber schon recht komplex und das ist nur für Cortex-M. Es setzt zwar nicht auf std::function, wird für einen AVR aber sicher schnell zu viel und es werden auch andere Klassen der std Lib benutzt. Der Vorteil ist eben das es typsicher ist, im Gegensatz zu generischen Funktionszeigern in C. https://os.mbed.com/docs/mbed-os/v6.15/apis/event-handling-apis.html
Bert S. schrieb: > D.h. konkret, dass meine Screen Klasse andere Klassen wie > bspw. meine eigene Button Klasse instanziiert. Und in der Screen Klasse > möchte ich da mehrere Member Functions als Klick Handler für > unterschiedliche Button Instanzen haben. Statt gleich mit einem riesen Geschwurbel auf die armen Tasten einzuprügeln, würde ich schrittweise vorgehen und mich erstmal mit der Problematik Entprellen und Flankenerkennung beschäftigen. Wenn man dann erstmal eine Instanz hat, die fertige Drückereignisse liefert, gestalten sich alle nachfolgenden Schritte viel einfacher.
Geschwurbel scheint das neue Lieblingswort hier zu sein. Wie entprellst du denn Buttons auf einem Screen?
Johannes S. schrieb: > Wie entprellst du denn Buttons auf einem Screen? Arduino verbinde ich gedanklich nicht mit einem Touch-GLCD. Das hätte man dazu schreiben müssen.
Hallo Peter, der Code existiert doch schon alles. Ich wollte aber ein paar komplexere Sachen darstellen, deshalb habe ich alles refactored und erst mal ein gewisses Grundgerüst entwickelt. Ich habe sowohl eine Button Klasse (für normale Taster) als auch eine ScreenButton Klasse. Die ScreenButton Klasse erbt dann von meiner VisualTouchElement (macht HitTest) Klasse und die wiederum von der VisualElement Klasse (sowas wie ein Canvas). Konkret habe ich ein 3,5" Touchscreen (480*320) und möchte da ein Diorama ansteuern mit ein paar Funktionen. Auf dem Touchscreen mache ich ein Startbildschirm mit ein paar Pixel Animationen, Uhr, Wecker, Temperatur und Bildschirm Buttons, um bspw. Weckzeit einzustellen oder Bitmaps und Sound abzuspielen. Gruß, 640KB
:
Bearbeitet durch User
Bert S. schrieb: > Auf dem Touchscreen mache ich > ein Startbildschirm mit ein paar Pixel Animationen, Uhr, Wecker, > Temperatur und Bildschirm Buttons, um bspw. Weckzeit einzustellen oder > Bitmaps anzuzeigen. Ja schon klar, und das alles funktioniert ja leicht auf einem Arduino Uno, Hardware ist geduldig.
das geht auch gut mit lvgl.io, ist allerdings für AVR zu groß und braucht Cortex-M. Das ist zwar alles in C, lässt sicher gut mit C++ kombinieren weil die Basisstrukturen alle einen 'Userdata' Pointer haben. Ein Eventsystem gibt es da natürlich auch, eben klassisch mit Callback und fixen Argumenten. Funktioniert aber auch alles.
Peter D. schrieb: > Wenn man dann erstmal eine Instanz hat, die fertige Drückereignisse > liefert, gestalten sich alle nachfolgenden Schritte viel einfacher. Das hat absolut nichts mit der Frage zu tun. Bei einem völlig anderen Event, wie z.B. Timer, stellt sich die Frage genau so, aber das hat nichts mit Entprellen zu tun. Das sind zwei völlig unterschiedliche Baustellen. Hier geht es um Software-Engineering und Patterns. Das ist auch kein "Geschwurbel", das ist absoluter Standard wenn es um komplexere Software geht. Bert kennt das ja offensichtlich von .net, und viele andere Frameworks und Sprachen machen das genau so. Im Anhang habe ich drei Varianten implementiert. 1. Variante: "FunPointers" Dies ist die einfachste und klassischste Möglichkeit. Der Button enthält ein Array aus gewöhnlichen Funktionspointern und eine Array aus "void* user_data"-Pointern pro Callback-Typ, also genau so wie man es in C seit Ewigkeiten macht. Dazu gibt es einen kleinen Wrapper "wrapMemFun", welchem man einen Member-Funktions-Pointer übergibt und der daraus einen normalen Funktions-Pointer mit zusätzlichem user_data-Argument macht. Damit kann man dann sehr einfach Member-Funktions-Pointer an die Callback-Registrations-Funktionen übergeben:
1 | btn0.addClickCallback (wrapMemFun<&Screen::onButton0Click>, this); |
Vorteile: Sehr einfach und C-kompatibel Nachteile: Man kann nicht direkt eine verkettete Liste der Observer aufbauen, wenn man die Array-Größe nicht "zufällig" genau richtig gewählt hat hat man entweder Speicher verschwendet oder zu wenig Speicher, die Fummelei mit userData-Zeigern ist etwas unschön. 2. Variante: "MultiInstanceTag" Hier wird das klassische Observer-Pattern implementiert. Über den "next"-Pointer in der "ButtonObserver"-Klasse wird eine verkettete Liste aufgebaut. Die Button-Klasse enthält einen Zeiger auf den ersten Observer. Dazu kommt eine "ButtonObserverRelay"-template Klasse, von der man dann in der "Screen"-Klasse mehrfach ableiten kann. Die "ButtonObserverRelay"-Klasse implementiert die virtuellen Funktionen, und leitet diese an die abgeleitete Klasse weiter. Um dort dann zwischen den unterschiedlichen Buttons unterscheiden zu können, übergibt man der ButtonObserverRelay-Klasse einen "Dummy"-Parameter "Instance", der für jeden Button unterschiedlich (aber beliebig) sein muss. Über einen zusätzlichen Parameter der Funktionen der Screen-Klasse wird dann zwischen den Buttons unterschieden:
1 | bool onButtonClick (int x, ButtonObserverRelay<0, Screen>*); |
2 | bool onButtonClick (int x, ButtonObserverRelay<1, Screen>*); |
Vorteile: Beliebig viele Observer ohne dynamische Speicherverwaltung oder Speicherverschwendung, relativ einfach, bei vielen Callbacks pro Klasse (wie Button) steigt der RAM-Verbrauch nicht an (im Gegensatz zu Variante 1) Nachteile: Funktionsnamen in der Screen-Klasse müssen fix sein (heißen bei allen Buttons onButtonClick), daher weniger übersichtlich, zusätzlicher Tag-Parameter ist nicht so schön 3. Variante: "MultiInstanceDeclare" Diese Variante ist der 2. Variante sehr ähnlich und wird nahezu identischen Maschinencode produzieren. Das grundlegende Observer-Pattern ist identisch implementiert. In der Screen-Klasse werden die gewünschten Callbacks über einen Typ deklariert:
1 | using DeclareCallbacks = Callbacks< |
2 | Callback<Button::OnClick, 0, &Screen::onButton0Click>, |
3 | Callback<Button::OnDoubleClick, 0, &Screen::onButton0DoubleClick>, |
4 | Callback<Button::OnClick, 1, &Screen::onButton1Click>, |
5 | Callback<Button::OnDoubleClick, 1, &Screen::onButton1DoubleClick> |
6 | >; |
Die ButtonObserverRelay-Klasse extrahiert dort heraus die gewünschte Callback-Funktion. Die Typen Button::OnClick / Button::OnDoubleClick werden nie instanziert, sie dienen nur als Marker zur Unterscheidung der Callbacks. Man könnte sie aber auch als Event-Typ einsetzen. Vorteile: Gleiche wie Variante 2, Namen der Callbacks beliebig wählbar, kein Tag-Parameter mehr nötig Nachteile: Braucht etwas kompliziertere template-Schnipselei in der Callbacks-Klasse, die man aber in einer Library verstecken kann Im Code aller Varianten sieht es so aus, als würden die Callbacks durch mehrere Funktionsebenen gehen, was die Performance verringern könnte. Der Compiler optimiert das aber weg, insbesondere wenn man LTO nutzt, sodass die einzelnen Ebenen ineinander inlined werden. Dadurch ist es genau so effizient wie ein "nackter" Funktionszeiger. Tatsächlich sind Varianten 2 und 3 je nach Situation auch Speicher-effizienter als die klassische C-Version mit Funktionszeigern. Alle drei Varianten brauchen keine Standard-Library und sollten daher auch auf dem AVR funktionieren. Variante 3 braucht aber C++17. C++11/14 wären auch möglich, wären dann aber in der Verwendung etwas lästiger mangels "auto"-NTTP.
Schwurbelmeister, schau dir doch einfach mal ein paar Beispiele an wie bspw. MCUFriend_kbv. Sowas reicht für mir von der Geschwindigkeit erst mal aus. Eine Bitmap Klasse werde ich auch noch mal selbst neu programmieren. Aus dem MCUFriend_kbv Beispiel dauert es ca. 1-2 Sekunden, um ein 480*320 24Bit Bitmap von SDCard zu zeichnen. Das ist schon ganz OK. Aber man kann ja eine Animation machen wie bspw. von innen nach außen zeichen. Die Informationen kann man ja aus dem Bitmap Header lesen (Auflösung, Offset, Größe). Also Bitmaps anzeigen und Sound abspielen, habe ich schon prinzipiell umgesetzt. Uhr und Temperatur von einem RTC DS3231 habe ich auch schon gemacht. Allerdings werde ich die Temperatur über einen eigenen Thermistor bestimmen, das ist vom DS3231 unbrauchbar, wegen der Chip Temperatur drumherum. Aber es ging hier ja auch um C++ Ereignisse ;) Gruß, 640KB
:
Bearbeitet durch User
Bert S. schrieb: > Eine Bitmap Klasse werde ich auch noch mal selbst neu programmieren. Aus > dem MCUFriend_kbv Beispiel dauert es ca. 1-2 Sekunden, um ein 480*320 > 24Bit Bitmap von SDCard zu zeichnen. Es gehört hier eigentlich nicht wirklich hin, aber wenn du den "Grafik-Turbo" erleben möchtest, kannst du einen Cortex-M einsetzen der ein paralleles Display-Interface (auch RGB-Interface genannt) und eine DMA-Einheit besitzt und an ein externes SDRAM angebunden ist. Damit kannst du Animationen mit vielen Hz abspielen. Wenn du außerdem ein SDIO-Interface (auch SDMMC genannt) für die SD-Karte hast, kannst du Daten mit vielem MByte/Sec einlesen, also praktisch auch Videos abspielen. Boards die so etwas bieten sind z.B. das STM32F746 Discovery oder das STM32F429-Discovery. Das ist aber eine ganz andere Hausnummer als der Arduino-Mega. Damit macht Grafik/GUI-Programmierung dann richtig Spaß - hohe Auflösung und Farbe, komplexe GUIs, Animationen, blitzschnelle flüssige Reaktionen, Null Ladezeiten...
Programmierer, besten Dank für die ganzen Hinweise! Ich versuche mich erst mal am Arduino Entwickler Board und später mit dem ESP32 (weil ich da einige rumliegen habe). Der ATMega 2560 ist zwar ein 8Biter, aber für mich auch erst mal relativ einfach zu verstehen (Assembler will ich auch meine Kenntnisse verbessern). Gruß, 640KB
:
Bearbeitet durch User
Schwurbelmeister schrieb: > Bert S. schrieb: >> Auf dem Touchscreen mache ich >> ein Startbildschirm mit ein paar Pixel Animationen, Uhr, Wecker, >> Temperatur und Bildschirm Buttons, um bspw. Weckzeit einzustellen oder >> Bitmaps anzuzeigen. > > Ja schon klar, und das alles funktioniert ja leicht auf einem > Arduino Uno, Hardware ist geduldig. > War auch mein Gedanke dazu. Der TE hat wohl nie die 8Bit vom 1990 Zeit erlebt, oder irgendwas mal mit einem Atmel gemacht. Sonst wüßte er die Grenzen von 64Kbyte Adressraum. Wir haben letztens mal ein SPI-Display von einem Arm M7 angesteuert. Und selbst da machten die Animationen keinen Spass. Hallo TE, wenn du es doch auf einem ATMega hinbekommst, dann würde mich der Sourcecode interessieren.
PittyJ schrieb: > War auch mein Gedanke dazu. Der TE hat wohl nie die 8Bit vom 1990 Zeit > erlebt, oder irgendwas mal mit einem Atmel gemacht. Sonst wüßte er die > Grenzen von 64Kbyte Adressraum. Da kann man einiges drin tun. Man schaue sich doch bloß Spiele oder Demos auf den Homecomputern der 8-Bit-Ära an. Und die hatten viel weniger Takt und eine deutlich ineffizienteren Befehlssatz als ein AVR8 (Z80- oder 6502-Derivate mit 1..2MHz). Nö, warum das heute so "unmöglich" scheint, hat nur einen einzigen Grund: hochgeschwurbelte, ineffiziente Hochsprachen. Insbesondere in Kombination mit dramatisch unfähigen Programmierern, die zwar möglicherweise die Sprache noch einigermaßen beherrschen, aber von der Maschine so viel Ahnung haben wie ich vom Plätzchenbacken oder vom Häkeln. > Wir haben letztens mal ein SPI-Display von einem Arm M7 angesteuert. Und > selbst da machten die Animationen keinen Spass. Das ist mit an Sicherheit grenzender Wahrscheinlichkeit aber nicht der M7 dran Schuld, sondern das über einen "engen" SPI-Pfad angebundene Display. Mit einem parallel angesteuerten Display und dem Framebuffer im RAM des M7 sieht das schon ganz anders aus... Das geht dann sicher auch in C oder C++ noch akzeptabel. Aber für die kleinen muss man halt das nehmen, was die am besten verstehen: Asm.
Bert S. schrieb: > Also Observer Pattern fällt für mich aus, weil ich > dann ständig alle Instanzen in der Methode checken müsste, um bedingt > reagieren zu können. Schau dir doch einfach mal MVC Muster an. Lässt sich gut in C++ implementieren. Wenn's zu kompliziert ist, reicht vielleicht auch einfach ein Durchreichen eines EventHandlers per Konstruktor.
1 | typedef void fnCallback(void); |
2 | |
3 | class Button |
4 | { |
5 | fnBtnCallback m_onClicked; |
6 | |
7 | public: |
8 | Button( fnBtnCallback obs ) : m_onClicked( obs ) {} |
9 | ~Button() {} |
10 | |
11 | void handleClick() |
12 | { |
13 | if( m_onClicked != nullptr ) |
14 | { |
15 | m_onClicked( this ); |
16 | } |
17 | } |
18 | }; |
Memberfunktions-Callbacks gehen auch, musste halt immer gegen das Objekt aufrufen, also quasi sowohl den Objektpointer als auch den Member-Pointer mitgeben. Halt so in der Art wie oben schon beschrieben: typedef void (Button::*fnBtnCallback)( void ); und dann etwa so: Button btn; btn->fnBtnCallback();
c-hater schrieb: > hochgeschwurbelte, ineffiziente Hochsprachen LOL. Mich stört an C++ die anzahl wrappers um den eigentlichen code. C++ macht sachen unübersichtlich und kompliziert. Es ist mir nicht klar ob diese wrapper auch code erzeugen oder ob die wegoptimiert werden. Die ausrede mit dem "wiederverwendbar" ist wie mit dem "marxismus", funktioniert beides nicht :)
Pepe T. schrieb: > c-hater schrieb: >> hochgeschwurbelte, ineffiziente Hochsprachen > > LOL. > Mich stört an C++ die anzahl wrappers um den eigentlichen code. C++ > macht sachen unübersichtlich und kompliziert. > Es ist mir nicht klar ob diese wrapper auch code erzeugen oder ob die > wegoptimiert werden. C++ ist da doch harmlos, da kann man immer noch normalen C-Code mit schreiben. Guck mal C# an - oder Java, wo jeder int geboxt werden muss um in ne Funktion übergeben zu werden. Und "Shy Code" hat man bei C# auch nicht gerade erfunden: objekt1.bla.blub.parameters.foo = objekt2.blub.bli.MeinTollerWert; > Die ausrede mit dem "wiederverwendbar" ist wie mit dem "marxismus", > funktioniert beides nicht :) Wiederverwendbar ist ne Qualität, die man sich richtig erarbeiten muss. Viel C/C++-Code leistet in einer Funktion schlicht zu viel auf einmal - man braucht nur zu zählen, wieviele Testfälle man für einen Unittest schreiben müsste, um jede Datenänderung einer Funktion abzutesten. In der Praxis oft schlicht nicht möglich.
Könnt ihr euer übliches C++ Haten woanders abladen und hier die ernsthaften Entwickler ein produktives Gespräch führen lassen? Danke.
c-hater schrieb: > PittyJ schrieb: > > >> Wir haben letztens mal ein SPI-Display von einem Arm M7 angesteuert. Und >> selbst da machten die Animationen keinen Spass. > > Das ist mit an Sicherheit grenzender Wahrscheinlichkeit aber nicht der > M7 dran Schuld, sondern das über einen "engen" SPI-Pfad angebundene > Display. Mit einem parallel angesteuerten Display und dem Framebuffer im > RAM des M7 sieht das schon ganz anders aus... > Ja, der Flaschenhals war SPI. Doch der TE will statt eines M7 einen ATMega verwenden. Leistungsmäßig noch mal 2 Klassen darunter. Da kann man doch im Ram nicht mal ansatzweise einen Framebuffer bauen. Dehsalb meine Vermutung, dass der TE nicht genau die Grenzen der 8-Bitter kennt, und aus einer vollkommen anderen Welt kommt.
Ich kann die Intention des TO durchaus verstehen. Aber erstens geht das alles eher in Richtung Architektur, und dazu ist hier zu 98% das falsche Publikum unterwegs. Und zum anderen ist er dafür tatsächlich auf dem falschen Rechner. Auf einem AVR kann man sich schon was basteln mit einem Feld, in dem man nach Bedarf Funktionszeiger ablegt, über die dann andere Programmteile über events informiert können, oder ähnliches. Aber mit groß OO und Instanziieren würde ich auch eher mit Cortex-irgendwas im oberen Bereich anfangen, was Flash und RAM angeht, und wo C++ auch schon etwas weiter gediehen ist als bei avr-gcc. Da kann man dann schöne Dinge treiben. Die Design patterns der Gang of Four kann man bei AVR nur als Lötunterlage benutzen.
:
Bearbeitet durch User
Klaus W. schrieb: > Aber mit groß OO und Instanziieren würde ich auch eher mit > Cortex-irgendwas eher im oberen Bereich anfangen, was Flash und RAM > angeht Warum? Ausgerechnet das Arduino-Framework benutzt OOP mit großem Erfolg, auch auf AVR. Meine Strategy-Pattern-Beispiele sind sogar RAM-sparender als ein Feld aus Funktionszeigern, sofern man mehrere Callbacks im Observer hat. Dieses verallgemeinernde "OOP ist ineffizient" ist einfach Quatsch. Wenn du das nicht glaubst, schau dir den generierten Assemblercode an. Klaus W. schrieb: > Die Design patterns der Gang of Four kann man bei AVR nur als > Lötunterlage benutzen. Das ist eine haltlose Behauptung. Nicht alle dieser Patterns sind sinnvoll für kleine Systeme (z.B. die Factory Patterns), aber andere sehr wohl (z.B. State, Strategy, Observer). Wenn die Architektur Funktionszeiger unterstützt, funktionieren auch diese Patterns.
Klar kann man einiges in ein paar kB reinquetschen. Aber um eine LED blinken zu lassen, hilft einem die tolle Architektur nicht weiter. Und für ein halbwegs sinnvolles komplexeres Unterfangen wird es schnell zu eng für Funktion und schön gleichzeitig. Wenn man schön als Selbstzweck betrachtet und nicht viel Funktion braucht, habe ich nichts dagegen. Auf Dauer glücklich wird man damit vermutlich nicht. Aber jeder wie er will...
Programmierer schrieb: > Dieses verallgemeinernde "OOP ist ineffizient" ist einfach > Quatsch. Wenn du das nicht glaubst, schau dir den generierten > Assemblercode an. Das habe ich nie behauptet, im Gegenteil. Wenn du meine anderen Ergüsse der letzten Jahre hier kennen würdest, wüsstest du daß, ich im Gegenteil vieles nützlich finde was z.B. C++ mehr bietet als C. Aber ich weiß eben auch, daß man auf sehr kleinen Systemen wie AVR schon genau hinschauen muß, was man anrichtet. Und dann wird ein "ich krieg das da alles rein, anstatt einen anderen Rechner zu nehmen" schnell zum Selbstzweck. Nur weil man es vielleicht kann, heißt es nicht daß es auch sinnvoll ist.
Klaus W. schrieb: > Aber um eine LED blinken zu lassen, hilft einem die tolle Architektur > nicht weiter. Offensichtlich geht es hier nicht um einen LED-Blinker. Und bei einem komplexen LED-Blinker, z.B. mit USB- oder Internet-Steuerung und riesiger LED-Matrix hilft OOP sicherlich auch. Klaus W. schrieb: > Und für ein halbwegs sinnvolles komplexeres Unterfangen wird es schnell > zu eng für Funktion und schön gleichzeitig. Das stimmt halt einfach nicht. Je nachdem wie man es macht ist der Overhead durch OOP Null oder winzig. Beim Arduino wird selbst das Serial.print und seine Varianten über OOP gemacht, und es funktioniert super. Bevor man über OOP lästert sollte man sich mal anschauen, was da tatsächlich im Hintergrund passiert.
Hallo Forum, ich hatte leider keine Zeit mehr bisher mich um das Thema zu kümmern. Ich habe erst mal eine einfache simple Lösung implementiert und wollte den Code mal schnell sharen: class IEventCallback { public: virtual void Execute() = 0; }; template<typename T> class EventCallback : public IEventCallback { public: EventCallback(T* instance, void (T::*function)()) : instance(instance), function(function) {} virtual void Execute() override { (instance->*function)(); } private: void (T::*function)(); T* instance; }; und verwendet wird es so: EventCallback<MainScreen> *obj = new EventCallback<MainScreen>(this, &MainScreen::ButtonClick); this->button->AddHandler(obj); void MainScreen::ButtonClick() { Serial.print("button click handler"); if(this->button->backColor != TFT_BLUE) this->button->backColor = TFT_BLUE; else if(this->button->backColor != TFT_BLACK) this->button->backColor = TFT_BLACK; this->button->Invalidate(); }; Der Click Event wird dann im Button ausgelöst, wenn der HitTest (Touch) erfolgreich war. Für Verbesserungsvorschläge und Anmerkungen freue ich mich natürlich immer. Gruß, 640KB
Das ist jetzt aber eine doppelte Indirektion, also virtuelle Funktion Plus Funktionszeiger. Das verschwendet ein paar Takte und Bytes.
Ich wusste mir nicht anders zu helfen :-) Über das Interface mit der virtuellen Funktion möchte ich den typisierten Member Function Pointer abstrahieren. Im Button wird dann ein Objekt von IEventCallback gespeichert und dann getriggert durch Execute().
Bert S. schrieb: > Über das Interface mit der virtuellen Funktion möchte ich den > typisierten Member Function Pointer abstrahieren. Du könntest den Member-Funktions-Zeiger als template-Argument übergeben. Dann verschwendest du nur noch den Speicher für den instance-Zeiger. Bert S. schrieb: > Ich wusste mir nicht anders zu helfen :-) Es wurden doch genug Lösungen präsentiert...
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.