lehrkraefte:blc:informatik:ffprg2-2021:l8

Klassen und Instanzen

// Definition typischer in .h Datei
class Beispiel {
  private:
  int instanz_variable = 42;
 
  public:
 
  static int klassen_variable;   // ACHTUNG: diese Variable **muss** in einer .cpp-Datei noch deklariert werden!  
  void hallo_instanz();
  static void hallo_klasse();
 
};   // Strichpunkt nach Klassendefinition!
 
// Implementation typischweise in .cpp-Datei
 
void Beispiel::hallo_instanz() { // Zugriff auf Instanz-Variablen und -Methoden. Die Variable 'this' enthält einen Pointer auf die aktuelle Instanz.
  Serial.printf("hallo_instanz(): instanz=%d, klasse=%d\n", instanz_variable, klassen_variable);
  instanz_variable++;
  klassen_variable++;
}
 
void Beispiel::hallo_klasse() {  // Kein Zugriff auf Instanz-Variablen (und -Methoden), wie eine ganz normale Funktion.
  Serial.printf("hallo_klasse(): klasse=%d\n", klassen_variable);
  klassen_variable++;
}
 
// Diese Zeile **muss** in einer .cpp-Datei stehen.
int Beispiel::klassen_variable  = 23;
 
// Start vom Programm (normalerweise main() in C++, ausser mit Arduino-Framework)
void setup() {
  Beispiel bsp1; // Neues Beispiel
  bsp1.hallo_instanz();      // hallo_instanz(): instanz=42, klasse=23
  bsp1.hallo_instanz();      // hallo_instanz(): instanz=43, klasse=24
  Beispiel::hallo_klasse();  // hallo_klasse(): klasse=25
 
  Beispiel* bsp_pointer = new Beispiel();
  bsp_pointer->hallo_instanz();  // hallo_instanz(): instanz=42, klasse=26
  bsp1.hallo_instanz();          // hallo_instanz(): instanz=43, klasse=27
  bsp_pointer->hallo_instanz();  // hallo_instanz(): instanz=43, klasse=28
 
  delete bsp_pointer;     // Für jedes new genau ein delete!
  bsp_pointer = nullptr;  // good practice
}

Callbacks

Callbacks sind Funktionen, die registriert werden und dann aufgerufen, wenn etwas passiert (z.B. ein Klick). Das ist oft besser als polling (permanent abfragen, ob was geklickt wurde).

Das Problem ist, dass damit keine (bzw. nicht ohne weiteres) Instanz-Methoden registriert werden können, weil dazu eine Referenz (oder Pointer) auf die Instanz gebraucht wird. Dazu gibt es verschiedene Lösungsansätze:

  • Klassenvariable, die einen Pointer auf eine Instanz enthält (geht natürlich nur, wenn es nur eine Instanz geben kann). Ich finde diese Version «unschön»
  • Wenn der Callback die Möglichkeit bietet, zusätzliche Daten zu übermitteln, kann ein Pointer (this) auf die Instanz übergeben werden. Ich ziehe diese Version vor.
  • Je nachdem ist es möglich eine Closure zu machen und so einen Pointer auf die Instanz zu übergeben (nicht möglich mit unserer lvgl-Version, wäre aber das Eleganteste).

In jedem lvgl-Objekt kann ein Pointer als user-data gespeichert werden. Dieser Pointer hat den Typ (void*), d.h. einfach eine Speicheradresse ohne Information, was dort zu finden ist.

Dieser Pointer wird dann mittels Typenumwandlung (cast) zum gewünschten Pointer gemacht:

// In einer Instanz-Methode im GUI-Objekt ein Pointer auf die aktuelle Instanz speichern:
lv_obj_set_user_data(obj, this);
 
// Im der Callback-Funktion die Instanz auslesen und darauf die Instanz-Methode aufrufen:
Beispiel* bsp = (Beispiel*) obj->user_data;
bsp->instanz_methode();

Es gibt zwei Möglichkeiten:

  • Klassenmethode (static)
  • Anonyme Funktion (mit user_data im gui-Objekt)
lv_obj_set_event_cb(button, [](lv_obj_t *button, lv_event_t event) {
        if (event != LV_EVENT_SHORT_CLICKED) return;
        ((Beispiel*)(button->user_data))->hallo_instanz();
    }
);

Je nach Definition des Callbacks könnten in die eckigen Klammern noch Variablen eingetragen werden, auf die die Funktion Zugriff hat (closure). Das geht aber nicht mit lvgl-Callacks.

  • lehrkraefte/blc/informatik/ffprg2-2021/l8.txt
  • Last modified: 2021/12/07 08:30
  • by Ivo Blöchliger