Jörg W. schrieb:>> Sind sie nicht, vor allem in C++ nicht (getrennte Operatorüberladung).>> Da ist allerdings was dran (hatte ich ja auch viel weiter oben schon> erwähnt).
Wenn ich drüber nachdenke: für die Basistypen (für die volatile im
Zusammenhang mit Hardware wichtig ist) kann man die Operatoren sowieso
nicht überladen. Man könnte wahrscheinlich gut damit leben, volatile nur
für solche Datentypen überhaupt noch zuzulassen (also Ganz- und
Gleitkommazahlen, boolean, sowie Zeiger) und für alle anderen Typen das
Verhalten als undefined zu erklären. Damit hätte sich das Problem
überladener Operatoren in dieser Hinsicht erledigt.
Grenzfall wären enums: sie sind natürlich nützlich für
Hardware-Abbildungen, aber für sie ist operator overloading zugelassen.
Jörg W. schrieb:> Veit D. schrieb:>>> Wie jetzt? Hat Johann hingeschmissen?>> Kennst du diese Diskussion denn nicht?> Beitrag "gcc11 könnte das avr Backend verlieren"
Ja das kenne ich. Das klang jetzt leider für mich so das Johann gar
nichts mehr für gcc macht, stimmt ja so auch nicht. 64bit Double hat er
vor kurzem erst eingebaut bzw. gcc zur Verfügung gestellt, wie auch
immer, ist von ihm. Das er dagegen keine Energie ins Backend steckt kann
ich nachvollziehen.
Jörg W. schrieb:> und für alle anderen Typen das> Verhalten als undefined zu erklären.
Was ist z.B. mit einem Ringbuffer zwischen Applikation und Interrupt?
Jörg W. schrieb:> Da musst du doch nur die head- und tail-Zeiger volatile machen.
Auch wieder wahr, der Buffer selber muß ja nur den Bereich
bereitstellen. Muß man halt im Hinterkopf behalten, das mit Pointern und
nicht mit Indizes zu implementieren.
A. B. schrieb:> gcc 10.1 übersetzt die verschiedenen Schreibweisen in identischen Code,> akzeptiert aber nur die lange Schreibweise als nicht deprecated.
und jetzt? Was schließt du daraus?
Hallo,
nö, es ging mir darum die Aussage von S.R.
Beitrag "Re: avr-gcc-10 - "volatile deprecated [-Wvolatile]" Warnungen"
und weit vorher von paar anderen Usern nachzuvollziehen bzw. zu
widerlegen, die Compilierungen widerlegen es jedoch. Das heißt meine
Gedanken das eh alles das Gleiche ist stimmt. Hat A.B. nochmal
bestätigt.
Deswegen kann ich den gesamten Aufriss von gcc und der Warnung
nachwievor nicht nachvollziehen. Wir drehen uns im Kreis.
Veit D. schrieb:> die Compilierungen widerlegen es jedoch.
"Deprecated" heißt ja auch nur, daß es ZUKÜNFTIG mal gänzlich entfernt
werden könnte und man sich deswegen VORBEUGEND darum kümmern sollte.
Veit D. schrieb:> und weit vorher von paar anderen Usern nachzuvollziehen bzw. zu> widerlegen, die Compilierungen widerlegen es jedoch. Das heißt meine> Gedanken das eh alles das Gleiche ist stimmt.
Damit hast du nur gezeigt, dass ganz bestimmte Compilerimplementierungen
dies tun.
Das sagt über den Standard und wie dieser sein sollte überhaupt nichts
aus.
Es sagt auch nichts über die Standardkonformität der Compiler aus.
S. R. schrieb:> Veit D. schrieb:>> Also wo ist das Problem das standardmäßig vor der eigentlichen>> Übersetzung mit der Langschreibweise zu ersetzen?>> In deiner Annahme, dass sie das gleiche wären.> Sind sie nicht, vor allem in C++ nicht (getrennte Operatorüberladung).
Die Volatile-Deprecation erstreckt sich nicht auf überladene Operatoren.
Das wäre auch unsinnig, da sich die Semantik von überladenen Operatoren
beliebig von derjenigen der Basisoperatoren unterscheiden kann.
Dies kann man sich zunutze machen, um für memory-mapped I/O-Register
auch zukünftig Compound-Assignments verwenden zu können: Man verpackt
das zugrunde liegende Speicherobjekt einfach in eine Klasse, für die die
fraglichen Operatoren überladen werden.
Ich habe das einmal exemplarisch für die AVR-Familie versucht:
Im angehängten Header io-c++20.h wird eine Template-Klasse deklariert,
in der I/O-Register unterschiedlicher Bitbreite verpackt werden können.
Des Weiteren werden drei Makros aus der AVR-Libc so umdefiniert, dass
sie diese Klasse nutzen.
Wird nun in einem Anwendungsprogramm io-c++20.h anstelle von avr/io.h
inkludiert, werden die Beschränkungen durch die neuen Volatile-Regeln
umgangen, ohne dass dabei (außer der Include-Zeile) etwas am Quellcode
geändert werden muss. Auch die AVR-Libc muss dazu nicht angefasst
werden.
Falls das Standardisierungsgremium die neuen Regeln irgendwann bindend
macht (was ich nicht hoffe), könnte das ein Ansatz sein, die AVR-Libc
(und auch andere I/O-Bibliotheken) "C++2x-aware" zu machen, so dass
deren Nutzer ihren Quellcode ohne Änderungen weiterverwenden können.
Aber Vorsicht: Der angehängte Header ist ganz sicher weder vollständig
noch fehlerfrei, sondern dient lediglich als Proof-of-Concept. Also
bitte nicht hauen, wenn ihr den Header testet und dabei euer gesamter
AVR-Bestand in Flammen aufgeht ;-)
Das beiliegende Beispiel funktioniert mit GCC 10.1.0 und Clang 10.0.0.
Anders als Clang hat der GCC allerdings Probleme, die Operatporen <<=
und >>= in einfache 8-Bit-Shifts umzusetzen. Die anderen Operatoren,
insbesondere die häufig verwendeten |= und &= werden aber wie erwartet
übersetzt.
Anwendungsbeispiel:
c und aliasing regeln schrieb:> müsste ja gegen strict aliasing verstoßen..
Aliasing kann überhaupt nur dann vorliegen wie der Name andeutet), wenn
unter wenigstens ZWEI inkompatiblen Zeigertypen auf dieselbe
Speicherstelle zugegriffen wird.
Yalu X. schrieb:> Anders als Clang hat der GCC allerdings Probleme, die Operatporen <<=> und >>= in einfache 8-Bit-Shifts umzusetzen.>> [...] Generierter Assemblercode für den ATmega8: [...]>> in r24,0x18 in r24, 24> ldi r25,0 lsr r24> asr r25 lsr r24> ror r24 out 24, r24> asr r25> ror r24> out 0x18,r24
Altbekanntes Problem, das nicht in avr-gcc auftritt sondern nur mit
avr-g++:
http://gcc.gnu.org/PR82658
Jörg W. schrieb:> Wenn wir das Problem nicht lösen können, definieren wir es weg?>> Klingt für mich eher nach Vogel Strauß als nach einer sinnvollen> Alternative.>> Ich schrieb doch schon, von mir aus soll man sowas als> implementation-defined festlegen, oder implementation-dependant oder was> auch immer, aber nicht einfach nur den Deckel aufmachen, und das Problem> in der Tonne versenken wollen.
100% ACK
SFRs unterschiedlicher Controller(-Familien) sind einfach zu
unterschiedlich, als dass sich das sinnvoll über den Sprachstandard
regeln ließe.
Ergo: volatile sollte Implementation Defined sind, was effektiv bedeutet
dass Compiler der (E)ABI des Hardware-Herstellers folgen. Für AVR gibt
es aber m.W. kein solches EABI — weder von Atmel noch von µChip.
Und zur Synchronisation von Prozesse über Core-Grenzen hinweg taugt
volatile eh nicht, da zu "schwach".
Wie sieht's eigentlich mit den 3 avr-libc Innovationen aus: math.h
(64-Bit double), Pimp für Multilib und mehr Support für Devices aus
avrxmega3?
Jörg W. schrieb:> Johann L. schrieb:>> Wie sieht's eigentlich mit den 3 avr-libc Innovationen aus>> Ähem, hmm, ja,
Gibt's denn Probleme mit den Änderungen?
Johann L. schrieb:> Jörg W. schrieb:>> Johann L. schrieb:>>> Wie sieht's eigentlich mit den 3 avr-libc Innovationen aus>>>> Ähem, hmm, ja,>> Gibt's denn Probleme mit den Änderungen?
Vermutlich wären sie groß genug für eine neue Release.
Was Jörg auch indirekt bestätigt hat.
Carl D. schrieb:> Johann L. schrieb:>> Jörg W. schrieb:>>> Johann L. schrieb:>>>> Wie sieht's eigentlich mit den 3 avr-libc Innovationen aus>>>>>> Ähem, hmm, ja,>>>> Gibt's denn Probleme mit den Änderungen?>> Vermutlich wären sie groß genug für eine neue Release.
Muss ja keine neue Release sein, in trunk würd schon reichen.
Yalu X. schrieb:> cbi 0x18,5 cbi 24, 5> in r24,0x18 ldi r24, 1
Genau, so macht man das (nicht). Nicht den Inhalt von r24 testen,
einfach rein damit. Und dann sich beschweren und kreischen, wieso die
eigene Anwendung zickt... Die hardwaremäßige Eingangsseite hast du
bestimmt einfach auf den Pin gelötet und betest dann, dass alles schon
funktioniert, bis das Geld auf dem Konto ist? So macht man den Ruf von
Selbständigen kaputt, hauptsache schnell und billig.
Carsten P. schrieb:> Genau, so macht man das (nicht). Nicht den Inhalt von r24 testen,> einfach rein damit. Und dann sich beschweren und kreischen, wieso die> eigene Anwendung zickt...
Ehm, was bitte?
Schlecht geschlafen?
Carsten P. schrieb:> Genau, so macht man das (nicht).
Hast du dir Yalus Beitrag denn überhaupt durchgelesen, bevor du dich
hier nach 1,5 Monaten über sowas beklagst?
Das ist die Ausgabe des Compilers! Der darf das, denn das ist in seinem
ABI so festgelegt, wofür R24 verwendet werden kann.
Hallo,
gibt es bezüglich volatile deprecated Neuigkeiten? Hat sich am Standard
nochmal etwas geändert? Ich finde es nämlich mit der Zeit ziemlich
lästig jede volatile Variable links und rechts vom Operator doppelt zu
schreiben. Vorallendingen bei struct Konstruktionen die schon mal länger
werden können. Bei Änderungen vergisst man oder übersieht etwas. Damit
sind Ausführungsfehler vorprogrammiert was lange Zeit zu unbemerkten
Problemen führen kann und wird.
Nun wollte ich volatile_load und volatile_store ausprobieren aber das
kennt mein avr-gcc 10.2 inkl. C++20 Option nicht.
Wie kann man load/store verwenden?
Laut Beschreibung soll load/store im Allgemeinen performanter sein, was
immer das bedeuten mag. Ich stelle mir darunter 'schneller' vor,
ansonsten macht das ja keinen Sinn. Das wollte ich untersuchen.
Sorry, wenn ich erst jetzt reingrätsche, aber ich hatte diese Diskussion
letztes Jahr nicht mitbekommen. Allerdings adressiert diese Änderung ein
Problem, das sich deutlicher mit 8051 als mit AVRs cbi/sbi erklären
lässt.
Mir fiel schon vor langer Zeit auf, dass bei 8051 die übliche Äquivalenz
von a &= b mit a = a & b nicht gewährleistet ist, wenn man sie als
Hochsprachen-Schreibweise der 8051 Befehle versteht. Und das betrifft
direkt die übliche Umsetzung von C.
Wenn man bei 8051 einen Port liest, wird der Zustand der Pins gelesen.
Wenn man ihn schreibt, wird der Zustand des Output-Registers
geschrieben. Modifikationsbefehle wie AND besitzen aber eine
Besonderheit: Es wird nicht etwa der Zustand der Pins gelesen, der Wert
verändert und ins Ausgangsregister geschrieben. Sondern es werden die
Bits im Ausgangsregister modifiziert.
Wenn bei 8051 ein Port-Pin eine Open-Collector-Leitung treibt, ist der
Zustand des Ausgangsregisters nicht zwingend mit dem Zustand der Leitung
identisch. Das ist bei anderen Prozessoren ein meist bewusst verwendeter
Fall, bei 8051 aber im Prinzip immer so, weil alle Portpins so definiert
sind.
Versteht man also
port &= value (1)
als Äquivalent zu
AND port, value
dann ist das nicht identisch mit
port = port & value (2)
weil in (1) das Ausgangsregister gelesen wird, in (2) aber der
Pinzustand gelesen werden muss.
Betrachtet man andererseits (1) als abgekürztes Äquivalent von (2), ist
es eigentlich verboten, für (1) den AND Befehl des 8051 zu nutzen, denn
der hat eine völlig andere Bedeutung. Es gibt somit keinen Weg, die
Arbeitsweise dieser 8051 Befehle direkt in C auszudrücken.
Nur hält sich natürlich kein realer Compiler für 8051 an diese
Besonderheit und verwendet bei (1) sehr wohl den AND Befehl. Wenn er
dann aber auch für (2) den AND Befehl verwendet, dann misachtet ganz
offen er den notierten Willen des Programmierers.
Und damit sind wir bei AVRs CBI/SBI. Wenn man (1) als Notation für diese
Befehle versteht, dann darf bei (2) der Compiler eigentlich nicht in
CBI optimieren, weil das eine andere Bedeutung hat. Und der assign-op in
(1) wäre überhaupt nur zulässig, wenn der Ports diese Befehle
unterstützt - das sind aber nur die ersten 32 Adressen mit einer
Einzelbitkonstante.
Damit landet man also in der Falle, sowohl bei 8051 als auch bei AVR.
Die Äquivalenz von (1) und (2) verbietet es somit eigentlich, bei 8051
in die AND/OR Befehle zum Port hin zu übersetzen, und verbietet es
ebenso, bei AVR in die CBI/SBI Befehle zu übersetzen. Macht freilich
jeder Compiler trotzdem.
Korrekt lösbar ist das nur, wenn bei 8051 und AVR diese Befehle nicht
über das übliche Sprachkonstrukt nutzbar sind, sondern über spezielle
Pseudofunktionen, wie eben das früher genutzte Makros CBI, hinter dem
dann aber nicht der Assign-Op stehen darf, sondern ein
Compiler-Intrinsic, oder ASM. Wer also die Bedeutung von CBI haben will,
muss das dann auch als CBI(reg,bit) hinschreiben, nicht als reg &= mask,
und kriegt es um die Ohren gehauen, wenn reg nicht mit CBI genutzt
werden kann.
Die Definition von C++20 kann man somit auch als Versuch werten, sich
von der Spottbezeichnung von C als besserem Makroassembler zu lösen, und
eine formal korrekte Umsetzung zu erzwingen.
Veit D. schrieb:> gibt es bezüglich volatile deprecated Neuigkeiten? Hat sich am Standard> nochmal etwas geändert?
Lies halt nach. Ich sag mal, ohne nachzulesen, nein, hat es nicht.
Veit D. schrieb:> Laut Beschreibung soll load/store im Allgemeinen performanter sein, was> immer das bedeuten mag. Ich stelle mir darunter 'schneller' vor,> ansonsten macht das ja keinen Sinn. Das wollte ich untersuchen.
Auf einem AVR? Ohne Betriebssystem, Multitasking bzw. -threading,
mehrstufigen Caches, out-of order-execution, pre-fetch, und was richtige
Prozessoren und Betriebssysteme heutzutage alles so treiben ?
Auf dem AVR wird nichts performanter.
Oliver
Ach so, Nachtrag:
wenn google weder std::volatile_load<T> noch
std::experimental::volatile_load<T> kennt, ist das ein ziemlich
deutlicher Hinweis darauf, daß das zwar irgendwo diskutiert wird, aber
halt (noch?) ungelegte Eier sind. Das wirst du dir wohl selber schreiben
müssen.
Oliver
Hallo,
@ Oliver
Danke für die Antwort. Dann hat sich das schon erledigt.
Schade das etwas "verboten" wird ohne zeitgleich die angestrebte Lösung
zur Verfügung zu stellen. ;-) Es ist damit ein sehr unschöner Zustand
im Moment.
@ A.K.
Naja, Kurzschreibweise sagt eigentlich im Namen schon aus das es
Äquivalent zu seiner Langschreibweise ist. Sprich es muss das gleiche
Ergebnis bringen. Zumindestens sagt mir das meine Logik. Aber dafür bin
ich ein viel zu kleines Licht. Was ich aber weiß ist, das sbi und cbi
genau die Kurzschreibweisen verwenden. Beim AVR. Was anderes kenne ich
nicht. ;-)
Oliver S. schrieb:> Ach so, Nachtrag:>> wenn google weder std::volatile_load<T> noch> std::experimental::volatile_load<T> kennt, ist das ein ziemlich> deutlicher Hinweis darauf, daß das zwar irgendwo diskutiert wird, aber> halt (noch?) ungelegte Eier sind. Das wirst du dir wohl selber schreiben> müssen.
Beispiel:
In Header-Datei vol_access:
Hallo,
@ MaWin:
Gut das jemand aufpasst. Hatte was von deprecated Macros gelesen und
blind kopiert. Entschuldigung.
@ Wilhelm:
Danke. Eine Frage dazu. Was genau macht die assert Prüfung an der
Stelle? Das kann ich nicht erkennen. Irgendeine Gültigkeit ja, aber
welche genau?
Veit D. schrieb:> Eine Frage dazu. Was genau macht die assert Prüfung an der> Stelle? Das kann ich nicht erkennen. Irgendeine Gültigkeit ja, aber> welche genau?
Schlaf mal ne Nacht drüber und wenn du es morgen nach dem zweiten Kaffee
noch nicht siehst, meld dich nochmal.
mh schrieb:> Veit D. schrieb:>> Eine Frage dazu. Was genau macht die assert Prüfung an der>> Stelle? Das kann ich nicht erkennen. Irgendeine Gültigkeit ja, aber>> welche genau?>> Schlaf mal ne Nacht drüber und wenn du es morgen nach dem zweiten Kaffee> noch nicht siehst, meld dich nochmal.
okay :-)
Die Frage, was daran dann "performanter" ist (und warum bzw. warum
nicht), kannst du dann auch gleich beantworten ;)
Veit D. schrieb:> Danke. Eine Frage dazu. Was genau macht die assert Prüfung an der> Stelle? Das kann ich nicht erkennen. Irgendeine Gültigkeit ja, aber> welche genau?
Die verhindert den volatile-Zugriff auf das Register R0 beim AVR ...
Oliver
Veit D. schrieb:> mh schrieb:>> Veit D. schrieb:>>> Eine Frage dazu. Was genau macht die assert Prüfung an der>>> Stelle? Das kann ich nicht erkennen. Irgendeine Gültigkeit ja, aber>>> welche genau?>>>> Schlaf mal ne Nacht drüber und wenn du es morgen nach dem zweiten Kaffee>> noch nicht siehst, meld dich nochmal.>> okay :-)
1
constint*constp=newint(42);
2
3
// ...
4
5
if(p){// checked-pointer-idiom
6
foo(*p);
7
}
Vielleicht erkennst Du es so?
Was ist die Vorbedingung, damit die Funktionen volatile_load() oder
volatile_store() arbeiten können? Oder anders ausgedrückt: welcher Wert
eines Zeigertyps gehört hier nicht zum zugelassenen Wertebereich des
Parameters p?
Hallo,
ich komme noch nicht ganz drauf was assert mit dem Zeiger macht. Mir
fehlt dabei entweder ein Adressoperator, ein Dereferenzierer oder ein
Datentyp cast. Obwohl mir der Datentyp cast fehlt kommt mir die Sache am
nächsten, dass assert die Adresse vom Zeiger prüft auf die er zeigt,
dass diese nicht 'nullptr' ist. Macht aber keinen richtigen Sinn. assert
wertet ein Bedingung aus die wahr sein muss. Unwahr wirft einen Fehler.
Hier wird jedoch kein Vergleich o.ä. gemacht. Nur warum sollte auf
ungleich 0 geprüft werden?
Warum ist es nicht ausreichend zu dereferenzieren und den Wert (auf den
der Zeiger zeigt) zurückzugeben? Einfach nur return *p;
Veit D. schrieb:> ich komme noch nicht ganz drauf was assert mit dem Zeiger macht
assert(x) darf man sich ungefähr so vorstellen:
if (x == 0) abort(...)
"The assert macro puts diagnostic tests into programs; it expands to a
void expression. When it is executed, if expression (which shall have a
scalar type) is false (that is, compares equal to 0), the assert macro
writes information about the particular call that failed (...) It then
calls the abort function."
Hallo,
was assert selbst macht ist mir schon klar. Eine Wahrheitsprüfung.
Nur was ist der Sinn mit dem Zeiger? Wird damit geprüft ob der Zeiger
auf eine Addresse ungleich nullptr zeigt? Wenn ja, warum? Ein Variable
als Parameter hat immer eine andere Adresse != nullptr. Wäre damit immer
wahr.
???
Veit D. schrieb:> Nur was ist der Sinn mit dem Zeiger? Wird damit geprüft ob der Zeiger> auf eine Addresse ungleich nullptr zeigt? Wenn ja, warum? Ein Variable> als Parameter hat immer eine andere Adresse != nullptr. Wäre damit immer> wahr.
In der Definition von volatile_load ist nicht der Parameter selbst
volatile, sondern das, worauf er zeigt. In
volatile int *p
ist nicht p volatile, sondern *p.
Veit D. schrieb:> Hallo,>> ich komme noch nicht ganz drauf was assert mit dem Zeiger macht. Mir> fehlt dabei entweder ein Adressoperator, ein Dereferenzierer oder ein> Datentyp cast.
Der abgeleitete Typ pointer-to (Zeiger) kann implizit nach bool
konvertiert werden:
nullptr -> false
andere Werte -> true
Das findet in meinem Beispiel mit dem if() und natürlich auch im assert
statt (wenn es enabled ist).
> Obwohl mir der Datentyp cast fehlt kommt mir die Sache am> nächsten, dass assert die Adresse vom Zeiger prüft auf die er zeigt,
Nein, es wird der Zeigerwert geprüft.
Und zwar genau aus dem Grund, den ich oben schon geschrieben habe (und
Du wohl nicht gelsen hast).
> dass diese nicht 'nullptr' ist. Macht aber keinen richtigen Sinn. assert> wertet ein Bedingung aus die wahr sein muss.
Die kann auch falsch sein, eben wenn nullptr übergeben wird.
> Unwahr wirft einen Fehler.
Ja, wenn enabled.
> Hier wird jedoch kein Vergleich o.ä. gemacht. Nur warum sollte auf> ungleich 0 geprüft werden?
s.o.
> Warum ist es nicht ausreichend zu dereferenzieren und den Wert (auf den> der Zeiger zeigt) zurückzugeben? Einfach nur return *p;
Beschäftige Dich mal mit dem Zweck von Zusicherungen (assertionen).
Hinweis: Herb Sutter hat gerade in seinem Blog (GotW) gute Beiträge
dazu.
Veit D. schrieb:> Hallo,>> was assert selbst macht ist mir schon klar. Eine Wahrheitsprüfung.> Nur was ist der Sinn mit dem Zeiger? Wird damit geprüft ob der Zeiger> auf eine Addresse ungleich nullptr zeigt? Wenn ja, warum? Ein Variable> als Parameter hat immer eine andere Adresse != nullptr.
Es geht um den Inhalt:
1
volatile_load(nullptr)
ist nicht zugelassen (weil nullptr nicht zum zugelassenen Wertebereich
von volatile_load() gehört).
@ Wilhelm
was assert macht weiß ich doch. Verwendet habe ich bis jetzt immer
static_assert, weil es Meldungen ausgeben kann. Übrigens ist der
Zeigerwert die Adresse auf die er zeigt. Ein Zeiger speichert ja nur
Adressen. Hatte ich schon richtig geschrieben. Ich fasse zusammen, es
wird überprüft ob der Zeiger auf nullptr zeigt oder nicht.
@ prx
dich habe ich so verstanden dass geprüft wird ob der Zeiger auf eine
volatile deklarierte Variable zeigt. Wenn nicht -> wirf einen Fehler.
Funktioniert aber nicht. Wirft bei mir ohne volatile keinen Fehler.
Vermutlich habe ich dich falsch verstanden.
1
uint16_tstore;
2
uint16_tload{111};
3
4
template<typenameT>
5
Tvolatile_load(constvolatileT*constp){
6
assert(p);
7
return*p;
8
}
9
10
store=volatile_load(&load);
Ganz schön schwierig drei Zeilen Code zu erklären. :-)
Jetzt müßte man das nur noch ohne Verrenkungen avr-gcc kompatibel
machen. Weil die benötigte std Lib gibts auf dem AVR nicht. assert(p)
wirft keine Fehlermeldung, nützt mir also nichts. Programm schmiert ab
bzw. bleibt stehen ohne das man den Grund sieht.
Und damit bin ich fast schon wieder beim Ausgangsproblem. Wenn das so
einen Rattenschwanz nach sich zieht, dann kann das leider keine
praktikable Lösung sein. Es verkompliziert alles und damit sinkt die
Bereitschaft das zu nutzen. Das ist überhaupt mein Problem was ich an
C++ in der Zukunft sehe. Anstatt Dinge zu Vereinfachen wird es außen
verkompliziert. Damit wird auch die Sprache immer komplizierter statt
einfacher. Das kann niemals der richtige Weg sein. Man müßte auf die
Rückwärtskompatibilität verzichten und einen harten Schnitt machen. Ab
Version C++ xx verhalten sich alle Funktionen/Methoden korrekt, nach
aktuellen besten Wissen und Gewissen, ohne äußerliche Syntaxänderungen.
Zum Bsp.
Denn bleiben wir einmal bei den Funktionen volatile_load und
volatile_store. Man muss trotzdem seine zu ändernde Variable 2x angeben.
Man hat nichts gewohnen. Also kann man das im Grunde auch sein lassen
und man schreibt sie als Lvalue und Rvalue vom Operator.
Veit D. schrieb:> @ Wilhelm> was assert macht weiß ich doch.
Da bin ich mir nicht ganz sicher ...
> Verwendet habe ich bis jetzt immer> static_assert, weil es Meldungen ausgeben kann.
... ob Du den Unterschied zu static_assert weißt
> @ prx> dich habe ich so verstanden dass geprüft wird ob der Zeiger auf eine> volatile deklarierte Variable zeigt. Wenn nicht -> wirf einen Fehler.
Nein, die Dereferenzierung eine nullpr ist UB.
Und genau das soll durch die Zusicherung verhindert werden. Hat man ein
Betriebssystem, so wird der Prozess mit einer Fehlermeldung abgebrochen.
hat man kein Betriebssystem ... es bleibt Dir überlassen, z.B. einen
bestimmten Pin wackeln zu lassen.
> Funktioniert aber nicht. Wirft bei mir ohne volatile keinen Fehler.
Mit volatile hat das erstmal gar nichts zu zu.
Wenn Du zur Compilezeit überprüfen / verhindern willst, ob der Zeiger
wirklich auf ein volatile zeigt, musst Du das mit einem type_trait
machen (Meta-Funktion) oder mir concepts/requirements komplett
unterbinden, dass das template instanziiert wird.
> Ganz schön schwierig drei Zeilen Code zu erklären. :-)
Es hängt immer vom Vorwissen des Zuhörers ab.
>> Jetzt müßte man das nur noch ohne Verrenkungen avr-gcc kompatibel> machen.
Für mein obiges Beispiel geht es auch ohne die stdlibc++, wenn Du
die auskommentierten static_assert auch weglässt.
>Weil die benötigte std Lib gibts auf dem AVR nicht. assert(p)> wirft keine Fehlermeldung, nützt mir also nichts.
Ja, wohin auch. Denk doch mal nach!
> Programm schmiert ab> bzw. bleibt stehen ohne das man den Grund sieht.
Genau. Geht nämlich in eine kleine Endlosschleife ;-)
> Bereitschaft das zu nutzen. Das ist überhaupt mein Problem was ich an> C++ in der Zukunft sehe.
Dein Problem ist im Moment, dass Dir Grundlagen fehlen.
> Denn bleiben wir einmal bei den Funktionen volatile_load und> volatile_store. Man muss trotzdem seine zu ändernde Variable 2x angeben.> Man hat nichts gewohnen. Also kann man das im Grunde auch sein lassen> und man schreibt sie als Lvalue und Rvalue vom Operator.
Du hast den Grund der deprecation nicht vertanden.
Alles klar Wilhelm. Ich weiß schon, ich bin ringsherum bekloppt. Danke.
Meine Aussagen bügelst du komplett weg. Das der Syntax immer nur wächst
und wächst und wächst ist dir demnach völlig egal.
Wilhelm M. schrieb:> Nein, die Dereferenzierung eine nullpr ist UB.>> Und genau das soll durch die Zusicherung verhindert werden.
Die weiterführende Diskussion zum Thema gibts dann hier:
Beitrag "0-pointer in c"
Oliver
Wilhelm M. schrieb:> Nein, die Dereferenzierung eine nullpr ist UB.
Ist nicht letztlich jede Dereferenzierung eines ungültigen Zeigers eine
UB?
Dann greift das assert auf einen Nullzeiger einfach mal zu kurz. Kann
man dann wohl genauso gut weglassen.
Jörg W. schrieb:> Wilhelm M. schrieb:>> Nein, die Dereferenzierung eine nullpr ist UB.>> Ist nicht letztlich jede Dereferenzierung eines ungültigen Zeigers eine> UB?
Natürlich.
>> Dann greift das assert auf einen Nullzeiger einfach mal zu kurz. Kann> man dann wohl genauso gut weglassen.
Nein. Ich habe nicht behauptet, dass damit jedweder Fehler verhindert
wird. Aber eine sehr häufige Fehlerquelle wird aufgedeckt. Eine
Verletzung einer Vorbedingung ist immer ein Fehler des Aufrufers.
Eine Zusicherung als Vorbedingung dient auch dazu, den "zu großen"
Wertebereich eines Datentyps - vor allem bei den primitiven DT -
einzugrenzen, soweit eben möglich. Bei Zeigerwerten ist dies (leider)
nicht vollständig möglich. Aber den nullptr kann man schon mal
herausnehmen, falls die folgenden Anweisungen davon ausgehen, das der
Wert kein nullptr ist. Damit hat man schon viel erreicht, wenngleich
nicht alles.
Zudem erfüllt eine Zusicherung immer auch die Funktion eines
"überprüften" Kommentars zu einem bestimmten Zustand des Objektes bzw.
der Funktion (ihrer Variablen).
Veit D. schrieb:> Alles klar Wilhelm. Ich weiß schon, ich bin ringsherum bekloppt. Danke.
Komm schon, wir sind doch hier in diesem speziellen Forum ;-) Da muss
man sowas aushalten!
> Meine Aussagen bügelst du komplett weg.
Naja, es stimmte eben nicht wirklich so ganz, war aber nur leicht
falsch.
> Das der Syntax immer nur wächst> und wächst und wächst ist dir demnach völlig egal.
Ja. Denn ich weiß um den Wert der Abwärtkompatibilität.
Veit D. schrieb:> Alles klar Wilhelm. Ich weiß schon, ich bin ringsherum bekloppt. Danke.
Der einzige, der das behauptet, bist du.
> Das der Syntax immer nur wächst> und wächst und wächst ist dir demnach völlig egal.
Hast du dich nicht vor kurzem beschwert, dass etwas deprecated wird,
ohne für Ersatz zu sorgen?
Wilhelm M. schrieb:> Aber eine sehr häufige Fehlerquelle wird aufgedeckt.
Sehe ich nicht so. Die Dinge, um die es hier bei der
"volatile"-Diskussion geht, sind doch nicht irgendwelche Zeiger, die
jemand auch mal in einer Variablen hat und dann aus Versehen nicht
passend zugewiesen hat, sondern das sind doch Hardware-Adressen. Da
übergibt keiner „aus Versehen mal“ ein nullptr.
Veit D. schrieb:> Hallo,>> irgendwie bin ich ja fast geneigt, um von der unpraktischen Syntax> loszukommen, irgendwie sowas in der Art anzulegen. Grobes Bsp.>
1
>template<typenameT>
2
>voidvolatileAdd(T&d,T&mod)
3
>{
4
>d=d+mod;
5
>}
6
>
Kann man machen. Aber: mod als in/out-Parameter? Wohl kaum.
> Damit muss man nicht irgendwelche Variablennamen doppelt tippen und hat> nur eine Zeile.>
Jörg W. schrieb:> Wilhelm M. schrieb:>> Aber eine sehr häufige Fehlerquelle wird aufgedeckt.>> Sehe ich nicht so. Die Dinge, um die es hier bei der> "volatile"-Diskussion geht, sind doch nicht irgendwelche Zeiger, die> jemand auch mal in einer Variablen hat und dann aus Versehen nicht> passend zugewiesen hat, sondern das sind doch Hardware-Adressen. Da> übergibt keiner „aus Versehen mal“ ein nullptr.
Sehe ich anders: warum sollte der Einsatz auf HW-Adressen beschränkt
sein?
Einen optimizer-killer volatile braucht es an vielen Stellen.
Jörg W. schrieb:> Wilhelm M. schrieb:>> Aber eine sehr häufige Fehlerquelle wird aufgedeckt.>> Sehe ich nicht so. Die Dinge, um die es hier bei der> "volatile"-Diskussion geht, sind doch nicht irgendwelche Zeiger, die> jemand auch mal in einer Variablen hat und dann aus Versehen nicht> passend zugewiesen hat, sondern das sind doch Hardware-Adressen. Da> übergibt keiner „aus Versehen mal“ ein nullptr.
Das hängt ganz stark davon ab, was diese Hardware-Adressen genau sind
und wie sie benutzt werden.
Hallo,
also auf Hardware Adressen ist die Geschichte mit volatile nun wirklich
nicht beschränkt. Da es jede Variable betrifft die als volatile
deklariert ist. Das kann wiederum unterschiedlichste Gründe haben.
"tagging". Danke. Mal sehen ob mir das Stichwort reicht.
Edit:
Du meinst damit aber nicht Operator Überladung? Oder doch?
Veit D. schrieb:> "tagging". Danke. Mal sehen ob mir das Stichwort reicht.>> Edit:> Du meinst damit aber nicht Operator Überladung? Oder doch?
Nein!
Du hast doch oben eine Variante vorgegeben, und darauf habe ich
geantwortet und Dir sogar gezeigt, wie die Aufrufseite für zwei
Versionen davon aussehen könnte. Daraus solltest Du eigentlich eine
einfache, vielleicht nicht allumfassende Realisierung ableiten können.
Die erste, oben gezeigte Möglichkeit ist vielleicht einfacher für Dich.
Dort geht es um ein freies Funktionstemplate mit drei Parametern. Die
ersten beiden sind wie von Dir vorgeschlagen, der dritte ist ein sog.
tag-type. Dieser dient nur dazu, die verschiedenen Überladungen dieses
Templates auszuwählen. 'add{}' erzeugt ein anonymes Objekt des Typs
'add'. 'add' ist eine sonst leere Klasse, etwa 'struct add{};'. Der Rest
dürfte jetzt klar sein.
Die andere Variante ist nicht so straight-forward, da man wie Du sicher
weißt Funktionstemplates nicht partiell spezialisieren kann. Ist seit
C++17 aber kein Problem, denn es gibt ja constexpr-if.
Wenn Du damit etwas hinbekommst, zeige ich Dir gerne meine Version von
dem oben genannten ...
Hallo,
die Implementierung bereitet mir noch Schmerzen. Funktionieren tuts
erstmal, nur werden die "Tags" als unused angemeckert, was irgendwie
logisch ist, nur ohne gehts nicht. Was müßte ich ändern?
Veit D. schrieb:> die Implementierung bereitet mir noch Schmerzen.
Mal abgesehen von der Implementierung, warum möchtest du das mit den
Tags machen? Ich sehe hier keinen Vorteil. Warum machst du nicht
Funktionstemplates volatileAdd, volatileSub, usw.
Wilhelm M. schrieb:> Deine Beschriftung LValue und RValue passt auch nicht ...
Er kann schlecht beide Lvalue nennen ... ;-)
Veit D. schrieb:> Danke, ohne jede Warnung. Wie würdest du die Paramternamen sinnvoll> benennen?> Mir fällt da aktuell nichts besseres ein.
Das bleibt Dir überlassen.
Aber: lvalue hat im Compilerbauer-Jargon eine bestimmt Bedeutung wie
auch rvalue. Nur der Parameter, den Du RValue genannt hast ist eben kein
rvalue. Ein rvalue ist ein anonymes, temporäres Objekt. Und da der
Parameter einen Namen hat, kann er kein rvalue sein. In Deinem Fall ist
es eben eine lvalue-reference.
Hallo,
mh schrieb:> Mal abgesehen von der Implementierung, warum möchtest du das mit den> Tags machen? Ich sehe hier keinen Vorteil. Warum machst du nicht> Funktionstemplates volatileAdd, volatileSub, usw.
Das wollte ich auch erst so machen.
Beitrag "Re: avr-gcc-10 - "volatile deprecated [-Wvolatile]" Warnungen"
Ich experimentiere derzeit rum und muss mich irgendwann entscheiden.
> Wilhelm M. schrieb:>> Deine Beschriftung LValue und RValue passt auch nicht ...
gut, ich bin da ziemlich flexibel. ;-)
Wie wäre es mit ... ?
Veit D. schrieb:> Das wollte ich auch erst so machen.> Beitrag "Re: avr-gcc-10 - "volatile deprecated [-Wvolatile]" Warnungen"> Ich experimentiere derzeit rum und muss mich irgendwann entscheiden.
Das war meine Schuld.
Ist hier nicht unbedingt nötig, bietet eben nur für die Zukunft einen
customization-point.
Veit D. schrieb:> Ich experimentiere derzeit rum und muss mich irgendwann entscheiden.
Der verlinkte Beitrag hilft nicht wirklich. Mit dem verlinkten Beitrag
auf c-plusplus.net hat dein Code relativ wenig zu tun. Das Beispiel dort
hat nämlich Vorteile ;-)
>> Wilhelm M. schrieb:>>> Deine Beschriftung LValue und RValue passt auch nicht ...>> gut, ich bin da ziemlich flexibel. ;-)> Wie wäre es mit ... ?template <typename T>
Du hättest wenigstens mal nachgucken können, was dieser lvalue ist, von
dem Wilhelm schreibt ...
@ Wilhelm:
Dümmer gewurden bin ich dadurch nicht. :-) Kann man bestimmt einmal
gebrauchen. Würdest du mir deine Variante zeigen wie du das machen
würdest?
@ mh:
Welchen Vorteil siehst du? Abgesehen davon das mir für den AVR die map
Lib fehlt.
Veit D. schrieb:> @ Wilhelm:> Dümmer gewurden bin ich dadurch nicht. :-) Kann man bestimmt einmal> gebrauchen. Würdest du mir deine Variante zeigen wie du das machen> würdest?
Ja, muss ich nur erstmal aufschreiben ...
In der Zwischenzeit (wer den cpp mag):
Jörg W. schrieb:>> _Pragma("GCC diagnostic push")>> Ich dachte, die kann man nur auf komplette Funktionen anwenden?
Nein: ist ein Optionen-Stack, auf den Du die aktuelle Optionenwahl
pushst, anschließend kannst Du manipulieren, und später holst Du es vom
Stack wieder runter.
_Pragma(...) ist dasselbe wie #pragma, nur dass man es halt im cpp
verwenden kann.
Es hat den Op als customization-point, so dass man auch ein closure aus
einer lambda-expression übergeben kann.
Und man kann es dann nicht mehr (fälschlicherweise) auf non-volatiles
anwenden.
Wobei ich jetzt anmerken muss, dass ich bei volatile_load / ..._store
bleibe.
Wilhelm M. schrieb:>> Ich dachte, die kann man nur auf komplette Funktionen anwenden?>> Nein: ist ein Optionen-Stack, auf den Du die aktuelle Optionenwahl> pushst, anschließend kannst Du manipulieren, und später holst Du es vom> Stack wieder runter.
Das ist mir schon klar. Ich hatte es nur so in Erinnerung, dass man
diese Optionen halt nur immer vor bzw. nach einer Funktion ändern
kann. Das Einbetten in den Makro würde aber die Änderung innerhalb einer
Funktion bewirken.
(Kann auch sein, dass ich das falsch in Erinnerung habe und dass dieses
Verhalten nur für das Ändern von Optimierungs-Einstellungen zutrifft.)
Jörg W. schrieb:> (Kann auch sein, dass ich das falsch in Erinnerung habe und dass dieses> Verhalten nur für das Ändern von Optimierungs-Einstellungen zutrifft.)
Wenn ich das grad in der Doku richtig gesehen hab, müsste das Pragma von
Wilhelm funktionieren. Das Pragma für Optimierungen steht allerdings
unter
Jedenfalls setze ich das gerne ein für sehr spezielle.
plattform-spezifische Warnungen, über die man sich Gedanken gemacht hat
und zu dem Schluss gekommen ist, dass man diese Warnung an genau dieser
einen Stelle akzeptieren kann. Das ist natürlich sehr selten. Ermöglicht
aber warnungsfreie Compilation, damit man neue Warnungen eben sofort
erkennt und beheben kann.
Johann L. schrieb:> WG21 hat also eine (teilweise) Rolle rückwärts gemacht?
Sieht sehr danach aus. Ist doch schön, wenn jemand auch mal Fehler
eingestehen und korrigieren kann.
As the compound operations are mainly used for bitwise manipulation, the
4
proposed solution is to only de-deprecate the use of bitwise compound
5
operations ( |= &= ˆ= ) as these are the ones that are useful for this
6
purpose. In the examination of the libraries += and -= did not occur,
7
however they might in commercial / closed source libraries.
Der Autor wollte also die Ausnahme von der Ausnahme einführen, und seine
Argumentation dafür stützt sich auf irgendwelche selbst erstellten und
wenig repräsentativen Statistiken zur Häufigkeit der Verwendung von |=,
&= und ^= auf der einen und +=, -=, *=, /=, %=, <<= und >>= auf der
anderen Seite.
Das verlinkte Dokument ist aber schon über zwei Jahre alt, und zum Glück
sind inzwischen sämtliche Compound-Assignments (also insbesondere auch
+=) wieder vom Volatile-Verbot befreit.
Damit hat die WG21 schon fast eine Dreiviertelrolle rückwärts geschafft.
Leider dürfen die Operatoren ++ und -- (sowohl die Präfix- als auch die
Postfix-Variante) auch weiterhin nicht auf Volatile-Operanden angewandt
werden. Hätte man die nicht gleich zusammen mit den ganz ähnlichen
Operatoren += und -= de-deprecaten können?
Als nur mittelmäßiger C++-Programmierer, der den mittlerweile auf stolze
2182 Seiten angewachsenen Standard-Draft nur partiell gelesen hat, kann
und muss ich das wohl nicht verstehen ;-)
Was ich ebenfalls ein Bisschen komisch finde:
1
inti;
2
volatileintv;
3
4
i=v+=1;// Erlaubt
5
i=v=v+1;// Fehler
Das kratzt mich allerdings wenig, da ich solche Konstrukte sowieso nicht
verwende.