Endlich habe ich auch mal wieder ein Problemchen, wo mir keine Lösung zu
einfällt.
Beim anpassen einer meiner Arduino Libraries (eine geschrumpfte
ProtoThreads Implementierung) haut es mir folgende Warnung um die Ohren:
> warning: storing the address of local variable> 'taskLable39' in 'lable' [-Wdangling-pointer=]
Die Meldung wird 3 mal geworfen, für alle 3 Lables im folgenden
Beispiel, dazu noch eine kleine Horde an verschiedenen note: folge
Meldungen.
µC:
STM32F411CE
Compiler:
xpack-arm-none-eabi-gcc\\12.2.1-1.2/bin/arm-none-eabi-g++
Verwunderlich ist daran, dass es keine Variable namens "taskLable39" im
Code gibt.
Hier der Code:
-----------
Der Code funktioniert, nur die Meldungen nerven.
Gibt es eine Abhilfe, ohne die dangling-pointer Warnung ganz
abzuschalten?
Warum ist taskLableStart keine lokale Variable (wirft keine Warnung)
aber die anderen schon?
Ich sehe da keinen Unterschied.
Arduino F. schrieb:> Verwunderlich ist daran, dass es keine Variable namens "taskLable39" im> Code gibt.
Jein:
Arduino F. schrieb:> #define PREFIX taskLableArduino F. schrieb:> lable = &&CONCAT(PREFIX,__LINE__); \
Die Zahlen dahinter sind die jeweiligen Zeilenzahlen.
Hmmm schrieb:> Die Zahlen dahinter sind die jeweiligen Zeilenzahlen.
Das ist mir klar, ist auch so beabsichtigt!
Aber für mich ist ein Label ein Label und keine lokale Variable.
Egal ob da eine Zahl dranhängt.
Die Zahl ist nicht verboten.
lable = &&taskLableStart
Wirft keine Warnung.
Arduino F. schrieb:> Das ist mir klar, ist auch so beabsichtigt!
Achso, ich dachte, deine Verwirrung bezog sich hauptsächlich auf das
Anhängsel.
Ich vermute, dass gewarnt wird, weil Deine Label-Pointer ausserhalb der
Funktion unbrauchbar sind, also dangling.
Hmmm schrieb:> Ich vermute, dass gewarnt wird, weil Deine Label-Pointer ausserhalb der> Funktion unbrauchbar sind, also dangling.
Nöö...
Bleibt ja alles in der Funktion.
Arduino F. schrieb:> lable = &&taskLableStart> Wirft keine Warnung.
taskLableStart ist auch nicht von außen erreichbar.
Selbst wenn ich beide Vorkommen in taskLableStart4711 ändere, keine
Meldung und funktioniert wie gewollt.
Die Zahlen sind es nicht, die mir Sorgen machen.
Zuerst mal, der Mechanismus der da benutzt wird ist eine GCC-Erweiterung
"Labels as Values"
https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html
Damit sollte die Frage
> Aber für mich ist ein Label ein Label und keine lokale Variable.
beantwortet sein. Mit der GCC-Erweiterung ist ein Label ein Wert, der
einer Variablen vom Typ "void *" zugewiesen werden kann.
Genau das wird im Code mehrfach gemacht. Ein Label wird mit dem
&&-Operator geholt und der Variable "label" zugewiesen.
Aber, und da hat der Compiler recht, die Scopes der Label (also Werte)
taskLabel39, taskLabel44 und taskLabel47 sind im (unterhalb des) Scope
der Variable "label" verschachtelt. Und zwar 2 (39) bzw. 3 Ebenen (44,
47) tiefer (wenn ich mich nicht verzählt habe).
Die "Labels as Values" werden nach oben in einem Scope gespeichert wo
sie nach Meinung des Compilers (und ich glaube zurecht) nicht immer
gültig sind.
Der Task-Switching Code springt über die Dangling Pointer in tiefer
liegende Scopes ein, und niemand weiß wie der Stack zu dem Zeitpunkt für
die Scopes aussieht. Dass das funktioniert ist Glück.
In der verlinkten Beschreibung steht auch drin (erster Satz):
>> You can get the address of a label defined in the current function>> (or a containing function)
D.h. in andere Richtung geht es. In einem nested Scope den Wert eines
Labes in einem umgebenden, höheren Scope darf man holen und benutzen -
um damit aus einem umgebenden Scope wieder nach oben zu springen. Bei
deinem Code wird aber nicht nach oben gesprungen, sondern nach unten
oder seitwärts.
(unten, oben, seitwärts im Sinne der Verschachtelung der Scopes, nicht
der Programmzeilen).
> taskLableStart ist auch nicht von außen erreichbar.
Nein, aber wird im gleichen Scope zugewiesen und benutzt, in dem "label"
deklariert ist. Damit ist der Pointer in dem Scope gültig und wird nicht
angemeckert.
Noch ein Zitat aus der Beschreibung
>> You may not use this mechanism to jump to code in a different function.>> If you do that, totally unpredictable things happen. The best way to>> avoid this is to store the label address only in automatic variables>> and never pass it as an argument.
Mein Fazit, der Code ist Scheiße und reine Glückssache dass das
funktioniert.
Wie kommt man eigentlich auf solch einen Mist? Du arbeitet hier auf
einem potenten CM4 und murkst mit so einem Protothreads ähnlichen Zeug
rum? Wenn schon kooperatives Multitasking dann wenigstens über
vernünftige Statemachines.
Andreas M. schrieb:> Wie kommt man eigentlich auf solch einen Mist?
Hat er doch geschrieben
> Arduino F. schrieb:>> Beim anpassen einer meiner Arduino Libraries
Die Protothreads sind doch keine Arduino Erfindung, die gibt es schon
lange und andere benutzen die auch.
Ist das eine selbst modifizierte Variante? Ich sehe auch nicht das die
lableTaskXX verschachtelt sind, die sind im Scope von lable. Aber warum
ist lable static? Kann das das Problem sein? Der static Pointer wäre
doch ungültig wenn er auf Stackvariablen zeigt.
Und wer hat das 'lable' genannt?
J. S. schrieb:> Und wer hat das 'lable' genannt?
Vielleicht jemand, der meint, daß es britischer klingt, wenn man die
E-Konsonant-Kombination am Wortende durch Konsonant-E ersetzt, wie es in
"Centre" und "Theatre" geschieht, (statt des amerikanischen "Center" und
"Theater").
Die Zehennägel stellt es trotzdem auf.
Jens K. schrieb:> "Wenn Sie dieses Gerät deaktivieren, funktioniert es nicht mehr!"
Meine Lieblings-Meldung war
> "Keybbard error, press F1 to resume"
Ich erinnere mich an Server, an denen deswegen immer eine Tastatur
hängen musste. Sie konnten sonst nicht booten.
J. S. schrieb:> Aber warum ist lable static?
Wäre es global, könnte es nur eine Task geben. Oder eben für jede Task
eine globale mit eigenem Bezeichner
Die Funktion test() wird auf dem STM32 ein paar Millionen mal pro
Sekunde aufgerufen.
Über all die Aufrufe muss das Sprungziel gemerkt werden.
Also darum: static.
In meinem original Code ist es eine Instanzeigenschaft (nicht static),
dort bin ich auf die Meldungen gestoßen.
Das oben gezeigte Beispiel ist eine Minimal Version um die Meldung zu
reproduzieren.
---------------
Dieses Beispiel hier wirft die Meldung berechtigt:
1
int*merker;
2
3
voidsetup()
4
{
5
inttest=42;
6
7
Serial.begin(9600);
8
merker=&test;
9
}
10
11
voidloop()
12
{
13
Serial.println(*merker);
14
}
Es wirft:
> warning: storing the address of local variable 'test' in 'merker'
[-Wdangling-pointer=]
Logisch, die lokale Variable verfällt nach dem verlassen der Funktion
setup(), und der pointer ist "dangling".
Da ist die Meldung korrekt!
Das Beispiel im Eingangsposting hat keine verfallenden Variablen.
Nirgendwo.
-------------
Hannes J. schrieb:> "Labels as Values"> https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html>> Damit sollte die Frage>> Aber für mich ist ein Label ein Label und keine lokale Variable.> beantwortet sein. Mit der GCC-Erweiterung ist ein Label ein Wert, der> einer Variablen vom Typ "void *" zugewiesen werden kann.
Deiner Logik kann ich nicht folgen. Der void Zeiger wird nicht
angemeckert.
Sondern die angebliche Variable namens "taskLable39"
Der verlinkte Text ist mir natürlich bekannt.
Mit keiner einzigen Zeile verstoße ich gegen die dort genannten
Bedingungen.
Oder habe ich da was übersehen?
------------
J. S. schrieb:> Ist das eine selbst modifizierte Variante?
Ja!
Das Original von Adam Dunkels verwendet switch/case als Mechanismus. Was
es schwieriger macht switch/case dann noch selber in der Task nutzen zu
wollen.
Zudem mutiert switch bei zunehmender Anzahl case von einer Sprungleiste
zu einer Kette von Vergleichen, welche es sehr langsam macht, ins
Besondere für die weit hinten liegenden Fälle.
Die goto Variante ist immer gleich schnell.
----------
Andreas M. schrieb:> Wie kommt man eigentlich auf solch einen Mist? Du arbeitet hier auf> einem potenten CM4 und murkst mit so einem Protothreads ähnlichen Zeug> rum? Wenn schon kooperatives Multitasking dann wenigstens über> vernünftige Statemachines.
Es ist eine Statemachine.
Das Verfahren funktioniert.
Mein Ziel ist es, es auf allen Arduino üblichen Plattformen (ohne
Meldung) zum laufen zu bekommen.
Bisher läuft es vom Tiny85(alle 8Bit AVR) über alle ESP bis gar zum K210
mit seinen 64Bit.
Hannes J. schrieb:> Noch ein Zitat aus der Beschreibung>>>> You may not use this mechanism to jump to code in a different function.>>> If you do that, totally unpredictable things happen. The best way to>>> avoid this is to store the label address only in automatic variables>>> and never pass it as an argument.
Das trifft auf meinen Code nicht zu, bleibt alles in der gleichen
Funktion.
Hannes J. schrieb:> Mein Fazit, der Code ist Scheiße und reine Glückssache dass das> funktioniert.
Wieso Glückssache?
Wogegen verstoße ich?
Vermutung: Der Compiler behandelt die labels wie lokale Variablen (was
deren Lebensdauer betrifft). Das erklärt dann die Warnungen, und auch,
warum es bei taskLableStart nicht auftritt.
Klaus schrieb:> Der Compiler behandelt die labels wie lokale Variablen
Ja!
Die Frage ist, warum tut er das?
Denn es ist ja ein Label und keine Variable.
Oder viel wichtiger, wie kann ich im das abgewöhnen, das Label als
Variable zu betrachten?
Hannes J. schrieb:> Die "Labels as Values" werden nach oben in einem Scope gespeichert wo> sie nach Meinung des Compilers (und ich glaube zurecht) nicht immer> gültig sind.>> Der Task-Switching Code springt über die Dangling Pointer in tiefer> liegende Scopes ein, und niemand weiß wie der Stack zu dem Zeitpunkt für> die Scopes aussieht. Dass das funktioniert ist Glück.
Das ist natürlich richtig.
Zumindest das mit den Anweisungsblöcken, da springe "ich" in tiefere
Ebenen.
Aber das mit dem Stack, das ist irrelevant.
Da ist nix mit Stacknutzung!
Damit ist:
> nicht immer gültig sind.
falsch
Und auch:
> und niemand weiß wie der Stack zu dem Zeitpunkt für> die Scopes aussieht. Dass das funktioniert ist Glück.
Irrelevant, da keine Stacknutzung.
Arduino F. schrieb:> Irrelevant, da keine Stacknutzung.
Das ist für den Compiler aber nur mit Zusatzaufwand zu erkennen.
Würde ich den Compiler bauen müssen, hätte ich mich auch so entschieden.
Arduino F. schrieb:> Wilhelm M. schrieb:>> Lable: ein kleines label?>> Meinen Dank, für diesen nützliche Ansatz.
Sorry, aber Du lieferst immer wieder Steilvorlagen ;-)
Klaus schrieb:> der Compiler warnt dich davor,
Der Compiler warnt vor irgendwas, was ich da gar nicht tue.
Es gibt keinen Pointer, welcher auf ein Element auf dem Stack zeigt.
Klaus schrieb:> Du machst potentiell gefährliche Dinge,
Welche?
https://en.cppreference.com/w/cpp/language/gotoKlaus schrieb:> Die Warnungen abzuschalten dürfte ja kein allzu großes Problem sein,> oder?
Doch!
> disable GCC warnings for a few lines of code
Ist in diesem Fall keine Lösung.
Es hat in einer Zeile zu geschehen.
Eine eher globale Abschaltung ist natürlich möglich, aber auch nicht so
sinnvoll, da in der Luft hängende Pointer durchaus gefährlich werden
können/sind.
Arduino F. schrieb:> Das Verfahren funktioniert.> Mein Ziel ist es, es auf allen Arduino üblichen Plattformen (ohne> Meldung) zum laufen zu bekommen.> Bisher läuft es vom Tiny85(alle 8Bit AVR) über alle ESP bis gar zum K210> mit seinen 64Bit.
Dein Verfahren funktioniert nicht oder zumindest nur für
Spielzeuganwendungen. Schreibst du sogar selbst:
Arduino F. schrieb:> Irrelevant, da keine Stacknutzung.Arduino F. schrieb:> Zudem mutiert switch bei zunehmender Anzahl case von einer Sprungleiste> zu einer Kette von Vergleichen, welche es sehr langsam macht, ins> Besondere für die weit hinten liegenden Fälle.
Das ist im allgemeinen so Unsinn. Es kommt auf den Compiler an.
Wieso verwendest Du Compilerspezifika, wenn du willst, das es auf
möglichst vielen Platformen läuft? Das ist genau das Gegenteil von dem
was man da macht.
Bei dieser nicht-standardisierten Erweiterung des computed-goto im GCC
https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html
werden Labels wohl intern etwas anders behandelt. Anscheinend hat sich
das mit Version 13 auch wieder geändert, zumindest ist die Warnung weg.
Im Kontext von C++ solltest Du Dir auch über andere Folgen im Klaren
sein.
Alles in allem: Mist!
Arduino F. schrieb:> Zudem mutiert switch bei zunehmender Anzahl case von einer Sprungleiste> zu einer Kette von Vergleichen
Die Entscheidungsfindung der Implementierung eines Switch-Statesments
ist weit komplexer. Eine Variante der Kette aus Vergleichen ist ein
Binärbaum an Stelle linearer Vergleiche. Bei vielen und weit auseinander
liegenden Fällen ist so ein Entscheidungsbaum im Platzverbrauch
effizienter als eine Tabelle. Weshalb das Optimierungsziel Tempo/Platz
eine Rolle spielen kann.
Wilhelm M. schrieb:> Nimm Version 13, da ist die Warnung weg ;-)
Das wollte ich auch gerade schreiben.
Wenn ein Update aus irgendeinem Grund nicht möglich sein sollte:
Compiler-Warnungen sind Hinweise auf mögliche Fehler. Wenn du sicher
bist, alles richtig gemacht zu haben, und dich die Warnung stört, kannst
du sie ja auch einfach abschalten.
Andreas M. schrieb:> Wieso verwendest Du Compilerspezifika, wenn du willst, das es auf> möglichst vielen Platformen läuft? Das ist genau das Gegenteil von dem> was man da macht.
In der Arduinowelt kommt bisher ausschließlich der Gcc zu Einsatz.
Andreas M. schrieb:> Dein Verfahren funktioniert nicht oder zumindest nur für> Spielzeuganwendungen. Schreibst du sogar selbst:>> Arduino F. schrieb:>> Irrelevant, da keine Stacknutzung.
In dem Beispiel findet keine Stacknutzung statt, die mit dem
Pointer/Warnung in Verbindung steht.
Natürlich nutzt das dort verwendete Serial.println() den Stack. Was aber
kein Problem darstellt. Der Stack ist ja da und nutzbar.
Meinen herzlichen Dank für die freundliche Unterstützung.
Mit eurer Hilfe und noch ein bisschen Doku stöbern konnte ich mein
Problemchen lösen.
Es funktioniert wie geplant, und die Meldungen unterbleiben.
Arduino F. schrieb:> Warum ist taskLableStart keine lokale Variable (wirft keine Warnung)> aber die anderen schon?
Dieses konnte hier leider nicht zu meiner Zufriedenheit beantwortet
werden.
Arduino F. schrieb:> Der Code funktioniert, nur die Meldungen nerven.> Gibt es eine Abhilfe, ohne die dangling-pointer Warnung ganz> abzuschalten?
Ja!
Siehe:
Arduino F. schrieb:> Ist in diesem Fall keine Lösung.> Es hat in einer Zeile zu geschehen.
Schön dass du doch noch selbst gemerkt hast, dass es in der Art eben
doch geht.
Arduino F. schrieb:> Es funktioniert wie geplant, und die Meldungen unterbleiben.
Für die typische Arduino-Definition von "funktioniert". Du springst
immer noch in Bereiche ein ohne den Stack richtig zu handhaben. Und
Register auch nicht.
Du hast ein triviales Beispiel dass zufällig funktioniert. Das ist
alles.
>> Der Code funktioniert, nur die Meldungen nerven.
Du kannst deine Behauptung noch so oft wiederholen, der Code
funktioniert nicht. Hat vorher nicht funktioniert und wird durch
Abschalten der Warnung auch nicht funktionieren.
Hannes J. schrieb:> Du springst> immer noch in Bereiche ein ohne den Stack richtig zu handhaben.
Es ist dem Protothread Konzept innewohnend, dass man lokale Variablen
nur aufmerksam nutzen darf, da sie bei jedem return verfallen.
Hannes J. schrieb:> Und Register auch nicht.
Für Registerinhalte gilt das gleiche!
Auch diese verfallen nach dem return.
Alles keine Überraschung!
Hannes J. schrieb:> Du hast ein triviales Beispiel dass zufällig funktioniert.
Welches du natürlich belegen kannst.
Oder endet es bei der Behauptung?
Hier mal Lesestoff:
https://de.wikipedia.org/wiki/Protothread