[C auf µCs] Mehrere Callbacks ohne dyn. memory

OP (Firma: Torrox.de) #4333401
Lesenswert?
• ▲
▼
Hallo,
callbacks werden ja gerne verwendet, um Abhängigkeiten von SW-Teilen zu 
reduzieren. Jetzt ärgere ich mich gerade mit einer C-Library herum, die 
es zwar erlaubt, einen callback zu installieren, aber auch genau nur 
einen.

Jetzt frage ich mich gerade ob es ggf. ein übliches Pattern sein könnte, 
beim Installieren nicht nur den Funktionszeiger mit zugeben, sondern 
auch den Speicher, der benötigt wird, um eine verkettete Liste mit 
callbacks auf zu bauen?

Beispiel:
1
// library.h
2
typedef void (*callback_t)( int update );
3

4
struct callback_node
5
{
6
    callback_t              callback;
7
    struct callback_node*   next;
8
};
9

10
void install_callback( struct callback_node* );
11

12
void update( int value );
13

14
// library.h
15
static struct callback_node* nodes = 0;
16

17
void install_callback( struct callback_node* node )
18
{
19
    node->next = nodes;
20
    nodes = node;
21
}
22

23
void update( int value )
24
{
25
    for ( struct callback_node* n = nodes; n; n = n->next )
26
        n->callback( value );
27
}
28

29
// main.c
30
void showupdates( int update )
31
{
32

33
}
34

35
struct callback_node showupdates_node = { showupdates };
36

37
int main()
38
{
39
    install_callback( &showupdates_node );
40
}

das würde erlauben, mehrere Callbacks zu installieren, ohne dynamischen 
Speicher zu verwenden oder mit fest dimensionierten Array zu arbeiten. 
Der overhead ist mit einem pointer auch recht überschaubar. Zusätzlich 
könnte man in callback_node auch noch einen void-pointer als User 
argument unterbringen.

Ist das ein übliches Pattern in C, das evtl. sogar einen Namen hat?

mfg Torsten
Gast #4335696
Lesenswert?
• ▲
▼
Torsten R. schrieb:
> sondern
> auch den Speicher, der benötigt wird, um eine verkettete Liste mit
> callbacks auf zu bauen?

Man verschleppt damit die ganzen schmutzigen Implementierungsdetails der 
verlinkten Liste in main. Außerhalb von library.c muss eigentlich keiner 
sehen können, ob die callbacks in einem festen Array oder einer linked 
list oder was auch immer wohnen.
Mir wäre das zu hässlich und ich würde die library.h auf
1
void install_callback( callback_t fun);
2
void update(int value); // die haette man auch run_all_callbacks() o.ae. nennen koennen.
eindampfen. Echte Embedded-Helden mit Abstraktionsallergie werden sich 
aber wahrscheinlich unwohl fühlen, wenn sie nicht genau wissen, was 
install_callback() genau tut.


Torsten R. schrieb:
> Was, nicht mal Kommentare zur Namensgebung? ;-)
Du hättest noch ein paar verschiedene Dinge mehr "update" nennen können 
;)
OP (Firma: Torrox.de) #4335818
Lesenswert?
• ▲
▼
Hallo Peter,

Peter D. schrieb:
> Was soll denn der Vorteil von einzelnen über das ganze Programm
> verstreuten Callback Variablen sein, gegenüber einem Array?

der Vorteil wäre Lokalität. Ein Array bräuchte eine Dimension, die 
entweder global festgelegt werden müsste, oder dynamisch zur Laufzeit 
wachsen müsste.

Das Muster hätte den Vorteil, dass kein dynamischer Speicher verwendet 
werden muss und auch keine feste Array-Größe vorgegeben werden muss (und 
damit keine Laufzeitfehler erzeugen kann).

Die Nachteile wären: leicht höhere Komplexität bei der Nutzung, die 
zusätzliche Fehlermöglichkeit, den Node auf dem Stack zu alloziieren und 
der Overhead einer verketten Liste (gegenüber einem Array).

mfg Torsten
Gast #4335863
Lesenswert?
• ▲
▼
bin auch freund der linked list ...
ich verwende das ganz schmerzfrei auf einem Cortex M

ich hatte nurprobleme bie verwendung von calloc statt malloc
irgendiwe stieg der µC manchmal genau beim calloc aus.

mit malloc + memset klappt das aber ohne probleme
OP (Firma: Torrox.de) #4335891
Lesenswert?
• ▲
▼
Hallo Peter,

Peter D. schrieb:

> Der Vorteil ist, daß das Array nur so groß sein muß, wie die maximale
> Anzahl gleichzeitig aktiver Callbacks.

Der Nachteil ist aber dann, die Ermittlung der maximalen Anzahl 
gleichzeitiger Callbacks. In Deinem Beispiel könnte man nach "timeradd" 
grepen und die Anzahl der Fundstellen als Anhaltspunkt nehmen um 
MAXTIMERS zu dimensionieren. Oder man könnte warten, bis Fehler zur 
Laufzeit auftreten und dann MAXTIMERS erhöhen, bis keine Fehler mehr 
auftreten. Gerade bei Timer kann ich mir vorstellen, dass es nicht ganz 
einfach sein wird, die maximale Anzahl gleichzeitig aufgesetzter Timer 
zu ermitteln (und mit dem Entwicklungsfortschritt laufend anzupassen).

Wenn Du die Library in mehreren Projekten einsetzt, musst Du sie dann 
zumindest für jedes Projekt neu Dimensionieren und auch neu übersetzen.

Aber gerade in Deinem Fall, wäre es eigentlich besonders hilfreich, wenn 
Du das Alloziieren der Knoten, dem Aufrufer überlassen würdest, da Du 
für die Reihenfolge der Timer eh eine verkette Liste pflegst.

Um so eine Implementierung, könnte man dann dünne Wrapper schreiben, die 
den Speicher dann z.B. aus einem heap oder einem array alloziieren.

mfg Torsten

Btw: wenn Du neben der Liste t_first noch eine zweite Liste t_empty 
führen würdest, könntest Du Knoten in O(1) alloziieren und bräuchtest 
T_FREE nicht. timerinit() würde dann alle Elemente in t_empty eintragen.

Antwort schreiben

Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt. Bitte hier nur auf die ursprüngliche Frage antworten, für neue Fragen einen neuen Beitrag erstellen.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.