Forum: Compiler & IDEs avr-gcc-10 - "volatile deprecated [-Wvolatile]" Warnungen


von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Veit D. schrieb:

> Wie jetzt? Hat Johann hingeschmissen?

Kennst du diese Diskussion denn nicht?

Beitrag "gcc11 könnte das avr Backend verlieren"

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Nop (Gast)


Lesenswert?

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?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Nop schrieb:
> Was ist z.B. mit einem Ringbuffer zwischen Applikation und Interrupt?

Da musst du doch nur die head- und tail-Zeiger volatile machen.

von Nop (Gast)


Lesenswert?

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.

von A. B. (Gast)


Angehängte Dateien:

Lesenswert?

Veit D. schrieb:

> Ich habe da mal was vorbereitet ...
> ...
> x86-64 gcc 10.1:  https://godbolt.org/z/xFAisg

Mit volatile int a,b,c,d
************************
siehe oben
oder hier https://godbolt.org/z/2JSzC_

x86-64 gcc 10.1
***************
4 x identisch und 3 x warning deprecated

von aweng (Gast)


Lesenswert?

A. B. schrieb:
> 4 x identisch und 3 x warning deprecated

Was willst du jetzt damit beweisen? Dass irgendein Compiler irgendwas 
macht?

von A. B. (Gast)


Lesenswert?

gcc 10.1 übersetzt die verschiedenen Schreibweisen in identischen Code, 
akzeptiert aber nur die lange Schreibweise als nicht deprecated.

von aweng (Gast)


Lesenswert?

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?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

aweng schrieb:
> Was schließt du daraus?

Dass die Überschrift des Threads just dieses aussagt? ;-)

von Veit D. (devil-elec)


Lesenswert?

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.

von Nop (Gast)


Lesenswert?

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.

von Bäm (Gast)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bitte ein Nutzername pro Thread!

von Yalu X. (yalu) (Moderator)


Angehängte Dateien:

Lesenswert?

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:

1
#include <stdint.h>
2
#ifdef CLASSIC
3
#  include <avr/io.h>
4
#else
5
#  include "io-c++20.h"
6
#endif
7
8
uint8_t a;
9
10
int main() {
11
12
  PORTB |= 1<<PB3;      // 8 Bit
13
  PORTB &= ~(1<<PB5);
14
  PORTB ^= 1;
15
  ++PORTB;
16
  a = PORTB++;
17
  PORTB >>= 2;
18
  OCR1A += 4;           // 16 Bit
19
20
  return 0;
21
}

Generierter Assemblercode für den ATmega8:

1
; GCC 10.1.0           Clang 10.0.0
2
3
  sbi 0x18,3           sbi    24, 3
4
5
  cbi 0x18,5           cbi    24, 5
6
7
  in r24,0x18          ldi    r24, 1
8
  ldi r25,lo8(1)       in    r25, 24
9
  eor r24,r25          eor    r25, r24
10
  out 0x18,r24         out    24, r25
11
12
  in r24,0x18          in    r24, 24
13
  subi r24,lo8(-(1)    inc    r24
14
  out 0x18,r24         out    24, r24
15
16
  in r24,0x18          in    r24, 24
17
  add r25,r24          mov    r25, r24
18
  out 0x18,r25         inc    r25
19
  sts a,r24            out    24, r25
20
                       sts    a, r24
21
22
  in r24,0x18          in    r24, 24
23
  ldi r25,0            lsr    r24
24
  asr r25              lsr    r24
25
  ror r24              out    24, r24
26
  asr r25
27
  ror r24
28
  out 0x18,r24
29
30
  in r24,0x2a          in    r24, 42
31
  in r25,0x2a+1        in    r25, 43
32
  adiw r24,4           adiw    r24, 4
33
  out 0x2a+1,r25       out    43, r25
34
  out 0x2a,r24         out    42, r24

von Veit D. (devil-elec)


Lesenswert?

Hallo,

Danke für deine Antwort & Arbeit.

von c und aliasing regeln (Gast)


Lesenswert?

Und was tut man gegen die fiesen strict-aliasing Regeln?
1
(*(_IO<uint8_t > *)(mem_addr))

müsste ja gegen strict aliasing verstoßen.. eigentlich müsste da noch 
ein bit_cast drüber gewrapped werden

von Alias (Gast)


Lesenswert?

c und aliasing regeln schrieb:
> müsste ja gegen strict aliasing verstoßen.

Nein. Warum?

von Nop (Gast)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Johann L. schrieb:
> Wie sieht's eigentlich mit den 3 avr-libc Innovationen aus

Ähem, hmm, ja, [verlegen guck]

будет, будет, все будет :)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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?

von Carl D. (jcw2)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Carl D. schrieb:
> Vermutlich wären sie groß genug für eine neue Release.

Ja, und ein Release für AVRDUDE ist schon lange überfällig. :/

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von Carsten P. (r2pi)


Lesenswert?

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.

von MaWin O. (mawin_original)


Lesenswert?

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?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Kaj (Gast)


Lesenswert?


von Veit D. (devil-elec)


Lesenswert?

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.
1
// statt
2
buffer[USARTn].lastRxError = buffer[USARTn].lastRxError | lastRxError;
3
4
// dann
5
auto temp = volatile_load(buffer[USARTn].lastRxError);
6
temp |= lastRxError;
7
volatile_store(buffer[USARTn].lastRxError, temp);

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.

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

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

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

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

von Veit D. (devil-elec)


Lesenswert?

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.  ;-)
1
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))  
2
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))

Die haben jedoch darauf schon reagiert und neue Makros erstellt.
https://www.nongnu.org/avr-libc/user-manual/group__avr__sfr.html
1
#define bit_is_set (sfr, bit) (_SFR_BYTE(sfr) & _BV(bit))
2
#define bit_is_clear (sfr, bit) (!(_SFR_BYTE(sfr) & _BV(bit)))

von Wilhelm M. (wimalopaan)


Lesenswert?

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:
1
#pragma once
2
#include <cassert>
3
#include "type_traits"
4
5
namespace std {
6
    namespace experimental {
7
        template<typename T>
8
        T volatile_load(const volatile T* const p) {
9
            assert(p);
10
//            static_assert(std::is_trivially_copyable_v<T>);
11
            return *p;
12
        }
13
        
14
        template<typename T>
15
        void volatile_store(volatile T* const p, const T v) {
16
            assert(p);
17
//            static_assert(std::is_trivially_copyable_v<T>);
18
            *p = v;
19
        }
20
        
21
    } // namespace experimental
22
} // namespace std

von MaWin (Gast)


Lesenswert?

Veit D. schrieb:
> Die haben jedoch darauf schon reagiert und neue Makros erstellt.

Die machen doch etwas ganz anderes.

von Veit D. (devil-elec)


Lesenswert?

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?

von mh (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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   :-)

von Oliver S. (oliverso)


Lesenswert?

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

: Bearbeitet durch User
von mh (Gast)


Lesenswert?

Oliver S. schrieb:
> Die verhindert den volatile-Zugriff auf das Register R0 beim AVR ...

Das ist sicher der einzige Grund ...

von Wilhelm M. (wimalopaan)


Lesenswert?

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
const int* const p = new int(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?

von Veit D. (devil-elec)


Lesenswert?

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;

von (prx) A. K. (prx)


Lesenswert?

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."

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

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.
???

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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).

von Veit D. (devil-elec)


Lesenswert?

@ 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_t store;
2
uint16_t load {111};
3
4
template<typename T>
5
T volatile_load(const volatile T* const p) {
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.

von (prx) A. K. (prx)


Lesenswert?

Veit D. schrieb:
> Vermutlich habe ich dich falsch verstanden.

Ja.

> Ganz schön schwierig drei Zeilen Code zu erklären.  :-)

Ja.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
von Wilhelm M. (wimalopaan)


Lesenswert?

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).

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von mh (Gast)


Lesenswert?

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?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
von Veit D. (devil-elec)


Lesenswert?

Hallo,

irgendwie bin ich ja fast geneigt, um von der unpraktischen Syntax 
loszukommen, irgendwie sowas in der Art anzulegen. Grobes Bsp.
1
template<typename T>
2
void volatileAdd(T &d, T &mod)
3
{
4
   d = d + mod;
5
}

Damit muss man nicht irgendwelche Variablennamen doppelt tippen und hat 
nur eine Zeile.
1
volatileAdd(var, mod);

Im C++ Forum habe ich das hier entdeckt.
https://www.c-plusplus.net/forum/topic/263375/operator-per-parameter-%C3%BCbergeben
Leider fehlt die map Lib. Nur kann man da nicht etwas mit using zaubern?
Die Idee wäre eine Funktion mit Operator als 3. Parameter.
1
void volatileMod(a, b, '+') { ... }
Wobei immer der 2. Parameter mit dem Ersten modifiziert wird. Genau das 
was man bis jetzt getippt hat.
1
a += b;
2
bzw.
3
a = a + b;

Geht da was?

von Wilhelm M. (wimalopaan)


Lesenswert?

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<typename T>
2
> void volatileAdd(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.
>
1
> volatileAdd(var, mod);
2
>
>
> Im C++ Forum habe ich das hier entdeckt.
> 
https://www.c-plusplus.net/forum/topic/263375/operator-per-parameter-%C3%BCbergeben
> Leider fehlt die map Lib. Nur kann man da nicht etwas mit using zaubern?

Was mit using? Hast Du was mit Medien studiert ;-)

> Die Idee wäre eine Funktion mit Operator als 3. Parameter.
>
1
> void volatileMod(a, b, '+') { ... }
2
>
> Wobei immer der 2. Parameter mit dem Ersten modifiziert wird. Genau das
> was man bis jetzt getippt hat.
>
1
> a += b;
2
> bzw.
3
> a = a + b;
4
>
>
> Geht da was?

Na klar, etwa mit tagging:
1
volatileOp(a, b, add{});

oder explizit:
1
volatileOp<add>(a,b);

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von mh (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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?

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

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 ...

von Veit D. (devil-elec)


Lesenswert?

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?
1
volatile uint16_t var {2222};
2
uint16_t load  {1111};
3
4
struct add {};
5
struct sub {};
6
7
template <typename T>
8
void volatileOps(volatile T &Lvalue, const T &Rvalue, const add &tag) {
9
  Serial.print("ADD ");
10
  Lvalue = Lvalue + Rvalue;
11
  Serial.println(Lvalue);
12
}
13
14
template <typename T>
15
void volatileOps(volatile T &Lvalue, const T &Rvalue, const sub &tag) {
16
  Serial.print("SUB ");
17
  Lvalue = Lvalue - Rvalue;
18
  Serial.println(Lvalue);
19
}
20
21
void setup (void)
22
{
23
  Serial.begin(250000);
24
  Serial.println("\nStart #### ####");
25
26
  volatileOps(var, load, add{} );
27
  var = 2222;
28
  volatileOps(var, load, sub{} );
29
}
30
31
void loop (void)
32
{
33
}

von Wilhelm M. (wimalopaan)


Lesenswert?

Oh ha, Arduino ...

Den Namen des Parameters wegleassen ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Deine Beschriftung LValue und RValue passt auch nicht ...

von mh (Gast)


Lesenswert?

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 ... ;-)

von Veit D. (devil-elec)


Lesenswert?

Hallo,

Danke, ohne jede Warnung. Wie würdest du die Paramternamen sinnvoll 
benennen?
Mir fällt da aktuell nichts besseres ein.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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 ... ?
1
template <typename T>
2
void volatileOps(volatile T &Lvalue, const T &modifier, const add) {
3
  Lvalue = Lvalue + modifier;
4
}

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von mh (Gast)


Lesenswert?

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 ...

von Veit D. (devil-elec)


Lesenswert?

@ 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.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

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):
1
#define VOLOP(x) _Pragma("GCC diagnostic push") \
2
    _Pragma("GCC diagnostic ignored \"-Wvolatile\"") \
3
    x; \
4
    _Pragma("GCC diagnostic pop")
5
6
//...
7
VOLOP(a += 1);

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> _Pragma("GCC diagnostic push")

Ich dachte, die kann man nur auf komplette Funktionen anwenden?

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

So, quick and dirty jetzt mal:
1
struct add{
2
    template<typename T>
3
    T operator()(volatile T& a, const T& b) const {
4
        return a + b;
5
    }
6
};
7
struct sub{
8
    template<typename T>
9
    T operator()(volatile T& a, const T& b) const {
10
        return a - b;
11
    }    
12
};
13
14
template<typename T, typename Op>
15
requires std::is_volatile_v<T>
16
void volOp(T& v, const std::remove_volatile_t<T>& m, const Op op) {
17
    v = op(v, m);
18
}

Dann kann man es so verwenden:
1
    volatile uint8_t a{}; 
2
    // ...
3
    volOp(a, uint8_t{1}, add{}); // should not work if a is not volatile     
4
    volOp(a, uint8_t{1}, sub{});
5
    volOp(a, uint8_t{1}, [](auto& a, const auto& b){return a * b;});

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.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> Dümmer gewurden bin ich dadurch nicht.

Na, das freut mich!

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.)

: Bearbeitet durch Moderator
von mh (Gast)


Lesenswert?

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
1
Function Specific Option Pragmas

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

OK, dann habe ich das verwechselt.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

Dankeschön Wilhelm.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

2 1/2 Jahre und 3 C++ Versionen später:

https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2327r1.pdf
1
| P2327R1
2
| Title: De-deprecating volatile compound operations
3
|
4
| [...]
5
| The C++ 20 standard deprecated many functionalities of the volatile
6
| keyword. [...] The deprecation was not received too well in the
7
| embedded community as volatile is commonly used for communicating with
8
| peripheral devices in microcontrollers.  The purpose of this paper is
9
| to give a solution that will not undo what was achieved with P1152,
10
| and still keep the parts that are critical to the embedded community.

WG21 hat also eine (teilweise) Rolle rückwärts gemacht?

Jedenfalls hat z.B. GCC obiges P2327R1 bereits implementiert (ab v13).

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

:-)  :-)  :-)

von Yalu X. (yalu) (Moderator)


Lesenswert?

Johann L. schrieb:
> WG21 hat also eine (teilweise) Rolle rückwärts gemacht?

Eine nur teilweise Rolle rückwärts bewirkt, dass der Körper in eine
instabile und auf die Dauer ziemlich schmerzhafte Stellung gerät :)

Genau das trifft auf den Lösungsvorschlag in

> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2327r1.pdf

zu:
1
3 Solution
2
3
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
  int i;
2
  volatile int v;
3
4
  i = v += 1;      // Erlaubt
5
  i = v = v + 1;   // Fehler

Das kratzt mich allerdings wenig, da ich solche Konstrukte sowieso nicht
verwende.

Beitrag #7540192 wurde vom Autor gelöscht.
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
Noch kein Account? Hier anmelden.