Forum: Mikrocontroller und Digitale Elektronik Arduino "static const uint16_t PINK= 0xF81F" failed


von Axel R. (axlr)


Angehängte Dateien:

Lesenswert?

EDIT: geht nur beim "MEGA" nicht. Nano, Micro usw. funktioniert mit 
"PINK" asl (Farb)Definition.

Hey Leute,
bin gerade am RGB-LED Spielen und ich hab echt nen lustigen Fehler(?) 
entdeckt:
 - Arduino V 1.8.19
 - Bord Mega2560
 - Windows10

Pink als Definition scheint iwie nicht zu gehen.
Weiss jemand, warum? Bin ich wirklich neugierig und gespannt.
LG
Äxl DG1RTO

Ich hab den eigentlichen Code auf das notwenigste heruntergebrochen.
eingefügt und angehangen.
1
static const uint16_t PINK   = 0xF81F;
2
//static const uint16_t PANK   = 0xF81A;
3
//static const uint16_t PUNK   = 0xF81B;
4
//static const uint16_t PONK   = 0xF81C;
5
static const uint16_t BLUE     = 0x001F;
6
static const uint16_t RED     = 0xF800;
7
8
void setup() {
9
    // put your setup code here, to run once:
10
11
}
12
13
void loop() {
14
    // put your main code here, to run repeatedly:
15
16
}

liefert...
1
PINK_TEST:2:23: error: expected unqualified-id before 'volatile'
2
 static const uint16_t PINK   = 0xF81F;
3
                       ^
4
PINK_TEST:2:23: error: expected ')' before 'volatile'
5
PINK_TEST:2:23: error: expected ')' before 'volatile'
6
exit status 1
7
expected unqualified-id before 'volatile'

Auskommentiert und "PINK" testhalber mit "PONK" benannt ( andere 
Kombinationen gehen auch), funktionierts:
1
//static const uint16_t PINK   = 0xF81F;
2
//static const uint16_t PANK   = 0xF81A;
3
//static const uint16_t PUNK   = 0xF81B;
4
static const uint16_t PONK   = 0xF81C;
5
static const uint16_t BLUE     = 0x001F;
6
static const uint16_t RED     = 0xF800;
7
8
void setup() {
9
    // put your setup code here, to run once:
10
11
}
12
13
void loop() {
14
    // put your main code here, to run repeatedly:
15
16
}

und liefert erwartungsgemäß;
1
Der Sketch verwendet 662 Bytes (0%) des Programmspeicherplatzes. Das Maximum sind 253952 Bytes.
2
Globale Variablen verwenden 9 Bytes (0%) des dynamischen Speichers, 8183 Bytes für lokale Variablen verbleiben. Das Maximum sind 8192 Bytes.

: Bearbeitet durch User
von Sebastian R. (sebastian_r569)


Lesenswert?

Der ATMega2560 (Arduino Mega) hat z.B. ein Pin-Register K. 
Dementsprechend ist PINK das Inputregister Pins PK0 ... PK7

von Nachdenklicher (Gast)


Lesenswert?

Ich verwende zwar das vergurkte SW-Ökosystem von Arduino nicht, sondern 
maximal deren Boards und Bootloader, die dann mit normalem AVR-GCC 
programmiert werden, aber das ist ein echt niedlicher Fallstrick.

Der Mega2560 hat ja recht viele I/Os, und darunter auch einen Port K. 
Mit den Registern PORT, DDR und PIN... warte.. PIN-K? PINK? Den Namen 
gibt's blöderweise schon. ;-)

von Axel R. (axlr)


Lesenswert?

HAHA, Darauf komme erstmal einer. Herzlichen Dank!
habe parallel "recherchiert"
https://forum.arduino.cc/t/error-expected-unqualified-id-before-volatile/49149/8?msclkid=b6d28ff6c6dc11ec9b5ab25eb52ec4d1

Kann zu. Vielleicht kommt ja noch jemand in die Bedrullie.
Dankeschön derweil

von Nachdenklicher (Gast)


Lesenswert?

Auch wenn ich nur knapp den 2. Platz gemacht habe - gern geschehen. Aber 
so Sachen passieren halt. Man denkt in dem Moment einfach nicht an 
andere Deutungsmöglichkeiten des gewünschten Namens, zumal der Anteil 
der AVR-Typen, bei denen es bis Port K geht, doch recht überschaubar 
ist. Da hilft's einfach manchmal, wenn man jemanden fragt, der die Sache 
aus größerer Entfernung sieht.
Und bei so einem Positivbeispiel, wie man das Problem beschreibt und die 
Frage stellt, hilft man doch gerne. :-) Viel Spaß beim Weiterbasteln!

von Axel R. (axlr)


Lesenswert?

Ich häng mal noch die Definitionsdatei des Mega2560 als Link mit rein:
https://github.com/arduino/ArduinoCore-avr/blob/master/variants/mega/pins_arduino.h
Jemand fragte bei AVR-Freaks.net nach ähnlichem: Hier ging es darum, 
einen PING zu speichern, also quasi die Antwort oderso in einer 
variablen abzulegen. Er musste dann notgedrungenerweise ein Zing draus 
machen. "Ping" gab es schon. Port-G eben.

von HildeK (Gast)


Lesenswert?

Mein ehemaliger SW-Kollege hätte diese Variable u16_PINK genannt; 
entsprechend natürlich alle Variablennamen entsprechend erweitert. Für 
ihn zur Kennzeichnung des Typs uint16_t und so wäre eine Verwechslung 
mit dem Pinregister ausgeblieben.

Obwohl mir diese 'Namensverlängerung' selber eher nicht zusagt (wird 
imho unübersichtlicher), hier wäre er nicht aufgelaufen :-).

Es erinnert mich auch zu sehr an die frühen Fortran-Erfahrungen mit den 
impliziten Typdefinition über den ersten Buchstaben der Variablen; 
Integer mussten mit i, j, ..., n beginnen ...

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


Lesenswert?

HildeK schrieb:
> Es erinnert mich auch zu sehr an die frühen Fortran-Erfahrungen mit den
> impliziten Typdefinition über den ersten Buchstaben der Variablen

"GOD IS REAL UNLESS DECLARED INTEGER" ;-)

von Hoschti (Gast)


Lesenswert?

Ist zwar o.t., aber mag ich so nicht stehen lassen.

HildeK schrieb:
> Integer mussten mit i, j, ..., n beginnen ...

Ist so nicht richtig. Alle Variablen die mit i .. n anfangen, werden 
standardmäßig als Integer angenommen. Kann man aber auch ändern 
("implicit integer" war glaube ich die Direktive). Ist aber schon ein 
paar Jahrzehnte her bei mir. Man kann aber auch jede Variable explizit 
definieren. Dann wird das so genommen wie definiert, unabhängig vom 
Anfangsbuchstaben. Ich fand da damals ganz praktisch, zumal ich den Kram 
noch mit Lochkarten ("IBM 29er" für Insider) reinhacken musste. 
Terminals waren nur was für Profis, nix für Studenten. Lang lang ist's 
her (so Anfang der 80er).

von Andras H. (kyrk)


Lesenswert?

static const uint16_t PINK   = 0xF81F;

Namingconvetion einführen. Wie zumbeispiel u16_PINK oder 
fimenkürzel_PINK oder firmenkürzel_modulname_PINK wie BAG_PINK_TEST_PINK

static const uint16_t BAG_PINK_TEST_PINK   = 0xF81F;

//static const uint16_t BAG_PINK_TEST_PANK   = 0xF81A;

//static const uint16_t BAG_PINK_TEST_PUNK   = 0xF81B;

//static const uint16_t BAG_PINK_TEST_PONK   = 0xF81C;

static const uint16_t BAG_PINK_TEST_BLUE     = 0x001F;

static const uint16_t BAG_PINK_TEST_RED     = 0xF800;

BAG: Beispiel AG :)
PINK_TEST ist die Modulname.

von Kruger (Gast)


Lesenswert?

Dann nimmst du einfach HOTPINK als Variablenname. Normales PINK ist eh 
langweilig, wenn man sich diese Tabelle anschaut:

https://tutorialehtml.com/de/html-farbcodes-tabelle/

von Nachdenklicher (Gast)


Lesenswert?

HildeK schrieb:
> Mein ehemaliger SW-Kollege hätte diese Variable u16_PINK genannt;
> entsprechend natürlich alle Variablennamen entsprechend erweitert. Für
> ihn zur Kennzeichnung des Typs uint16_t und so wäre eine Verwechslung
> mit dem Pinregister ausgeblieben.

Man könnte auch statt dieser per Deklaration bekannten Info (Datentyp 
uint16_t), der in jeder gescheiten IDE (sorry, also nicht bei Arduino*) 
per Tooltip auf dem Variablennamen ersichtlich ist, auch einfach eine 
Information beifügen, die nicht schon in der Sprache festgelegt wird...

COLOR_PINK
COLOR_RED
....

Compiler und IDE wissen nicht, daß der Wert dieser Konstanten einen 
Farbwert repräsentiert, also kann man dieses für den Programmierer 
interessante Detail auch in den Namen setzen. Verwechslung mit dem 
Register ist ebenfalls ausgeschlossen. Und bei einer Suche nach "COLOR_" 
findet man sofort alle Verwendungen der Farbwerte. Und mit intelligentem 
Autocomplete, das jede gescheite IDE (ach ja, sorry, nicht bei Arduino*) 
kann, hält sich die Tipparbeit auch in Grenzen. VS Code würde aus C_PI 
schon "COLOR_PINK" machen, sofern es nicht noch COUNT_PINEAPPLES oder 
irgendeine andere Mehrdeutigkeit gibt, in der C, _, P, I in dieser 
Reihenfolge vorkommen.

Semantische Informationen sehe ich da als wesentlich hilfreicher als den 
Datentyp als Präfix zu nehmen.

*) Bevor die Arduino-Fans das in den falschen Hals bekommen - ich habe 
nichts gegen Arduino an sich, und schon gar nichts gegen diejenigen, die 
gerne damit arbeiten. Nur die Standard-IDE ist fürs Jahr 2022 doch recht 
armselig.

von HildeK (Gast)


Lesenswert?

Hoschti schrieb:
> Ist so nicht richtig.
Kann natürlich sein, dass mir das entfallen ist oder erst ab F'77 
möglich wurde.

> Lang lang ist's her (so Anfang der 80er).
Du sagst es! Bei mir ist es noch ein paar Jahre (knapp 10?) länger her 
(Fortran 66), und ach ja, Lochkarten 😀 ...

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


Lesenswert?

Andras H. schrieb:
> Namingconvetion einführen.

Korrekt wäre es eigentlich gewesen, wenn die avr-libc seinerzeit gleich 
mit standardkonformen Namen ins Rennen gegangen wäre und statt PINK dann 
_PINK benutzt hätte (liegt im implementation namespace). Hat sie aber 
halt nicht, und nun muss man sich als Applikationsprogrammierer Gedanken 
machen, welche Namen man benutzen darf und welche nicht. Namespaces 
gibt's in C auch nicht.

von Wilhelm M. (wimalopaan)


Lesenswert?

Das Problem ist der Präprozessor, denn seine Symbole sind nicht 
ge-scoped. Das ist die Wurzel allen Übels.

Das zweite Problem hier ist natürlich, dass Du eine Variablen-Definition 
machst in einem "Pseudo-Namensraum", der per "Konvention" dem 
Präprozessor vorbehalten ist, sprich Namen aus ausschließlich Kapitalen.
Wer sich über diese Grundregeln hinwegsetzt, darf sich nicht wundern.

von Rolf M. (rmagnus)


Lesenswert?

Wilhelm M. schrieb:
> Das zweite Problem hier ist natürlich, dass Du eine Variablen-Definition
> machst in einem "Pseudo-Namensraum", der per "Konvention" dem
> Präprozessor vorbehalten ist, sprich Namen aus ausschließlich Kapitalen.
> Wer sich über diese Grundregeln hinwegsetzt, darf sich nicht wundern.

Ja, es scheint, als ob diese ungeschriebene Regel oft missverstanden 
wird und viele glauben, dass es dabei um Konstanten ginge statt um 
Makros, weil in C Konstanten als Makros geschrieben werden.

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


Lesenswert?

Wilhelm M. schrieb:
> Das Problem ist der Präprozessor, denn seine Symbole sind nicht
> ge-scoped.

Scope hilft hier eh nur bedingt (bei einem enum würde er helfen, in der 
Tat).

> Das zweite Problem hier ist natürlich, dass Du eine Variablen-Definition
> machst in einem "Pseudo-Namensraum", der per "Konvention" dem
> Präprozessor vorbehalten ist, sprich Namen aus ausschließlich Kapitalen.

Ob die enum-Elemente in Großbuchstaben geschrieben werden oder nicht, da 
sind sich viele style guides wohl nicht einig.
1
typedef enum {
2
        TD_ERR = -1,            /* Unspecified error. */
3
        TD_OK = 0,              /* No error. */
4
        TD_BADKEY,
5
        TD_BADPH,
6
        TD_BADSH,
7
        TD_BADTA,
8
        TD_BADTH,
9
        TD_DBERR,
10
        TD_MALLOC,
11
        TD_NOAPLIC,
12
        TD_NOCAPAB,
13
        TD_NOEVENT,
14
        TD_NOFPREGS,
15
        TD_NOLIBTHREAD,
16
        TD_NOLWP,
17
        TD_NOMSG,
18
        TD_NOSV,
19
        TD_NOTHR,
20
        TD_NOTSD,
21
        TD_NOXREGS,
22
        TD_PARTIALREG
23
} td_err_e;

… nur mal den erstbesten enum aus FreeBSD's /usr/include gegriffen.

Wie ich schon schrieb, das Problem ist, dass sich die avr-libc vor 20 
Jahren in den application namespace eingenistet hat, statt im 
implementation namespace zu bleiben. Lässt sich jetzt halt nur nicht 
mehr ändern.

: Bearbeitet durch Moderator
von Oliver S. (oliverso)


Lesenswert?

Wilhelm M. schrieb:
> Das zweite Problem hier ist natürlich, dass Du eine Variablen-Definition
> machst in einem "Pseudo-Namensraum", der per "Konvention" dem
> Präprozessor vorbehalten ist, sprich Namen aus ausschließlich Kapitalen.
> Wer sich über diese Grundregeln hinwegsetzt, darf sich nicht wundern.

Na ja, ein echter C-ler der K&R-Generation hätte geschrieben:
1
#define PINK 0xF81F

Erfüllt alle Konventionen, knallt aber trotzdem.

Oliver

von Wilhelm M. (wimalopaan)


Lesenswert?

Jörg W. schrieb:
> Wilhelm M. schrieb:
>> Das Problem ist der Präprozessor, denn seine Symbole sind nicht
>> ge-scoped.
>
> Scope hilft hier eh nur bedingt (bei einem enum würde er helfen, in der
> Tat).
>
>> Das zweite Problem hier ist natürlich, dass Du eine Variablen-Definition
>> machst in einem "Pseudo-Namensraum", der per "Konvention" dem
>> Präprozessor vorbehalten ist, sprich Namen aus ausschließlich Kapitalen.
>
> Ob die enum-Elemente in Großbuchstaben geschrieben werden oder nicht, da
> sind sich viele style guides wohl nicht einig.

Es geht hier gar nicht um enums!

Es geht um:
1
iomxx0_1.h:# define PINK   _SFR_MEM8(0x106)

Wie Du siehst, ist das die Definition eins CPP-Symbols. Und die ist 
global (nicht gescoped).

von Wilhelm M. (wimalopaan)


Lesenswert?

Oliver S. schrieb:
> Wilhelm M. schrieb:
>> Das zweite Problem hier ist natürlich, dass Du eine Variablen-Definition
>> machst in einem "Pseudo-Namensraum", der per "Konvention" dem
>> Präprozessor vorbehalten ist, sprich Namen aus ausschließlich Kapitalen.
>> Wer sich über diese Grundregeln hinwegsetzt, darf sich nicht wundern.
>
> Na ja, ein echter C-ler der K&R-Generation hätte geschrieben:#define
> PINK 0xF81F
>
> Erfüllt alle Konventionen, knallt aber trotzdem.

Genau das habe ich doch oben gesagt!

von Rolf M. (rmagnus)


Lesenswert?

Jörg W. schrieb:
> Ob die enum-Elemente in Großbuchstaben geschrieben werden oder nicht, da
> sind sich viele style guides wohl nicht einig.

Ja, leider. Das wurde ja gerade eingeführt, damit man an der 
Schreibweise gleich erkennt, dass es ein Makro ist. Wenn jetzt alles 
mögliche andere auch so geschrieben wird, wird die Regel nutzlos.

Oliver S. schrieb:
> Na ja, ein echter C-ler der K&R-Generation hätte geschrieben:
> #define  PINK 0xF81F
>
> Erfüllt alle Konventionen, knallt aber trotzdem.

Allerdings mit einer verständlicheren Ausgabe, weil sich dann schlicht 
der Präprozessor beschwert, dass der Name schon als Makro vergeben ist.

: Bearbeitet durch User
von Axel R. (axlr)


Lesenswert?

Rolf M. schrieb:
> Allerdings mit einer verständlicheren Ausgabe, weil sich dann schlicht
> der Präprozessor beschwert, dass der Name schon als Makro vergeben ist.

und der Laie wundert sich (siehe weiter unten)

Hallo zusammen,

Ja, in der TAAT, wenn man alle Warnungen beachtet.
1
C:\Users\ar\Documents\Arduino\PINK_TEST\PINK_TEST.ino:1:0: warning: "PINK" redefined
2
 #define PINK 0xF81F
3
 
4
In file included from c:\program files (x86)\arduino\hardware\tools\avr\avr\include\avr\iom2560.h:38:0,
5
                 from c:\program files (x86)\arduino\hardware\tools\avr\avr\include\avr\io.h:174,
6
                 from c:\program files (x86)\arduino\hardware\tools\avr\avr\include\avr\pgmspace.h:90,
7
                 from C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/Arduino.h:28,
8
                 from C:\Users\ar\AppData\Local\Temp\arduino_build_793233\sketch\PINK_TEST.ino.cpp:1:
9
c:\program files (x86)\arduino\hardware\tools\avr\avr\include\avr\iomxx0_1.h:1149:0: note: this is the location of the previous definition
10
 # define PINK   _SFR_MEM8(0x106)
11
 
12
Compiling libraries...
Knallen wird's wahrscheinlich erst, wenn man tatsächlich auf Port "K" 
zugreifen will.
der BeispielCode wird mit #define PINK.. anstandslos compilert.
Will garnicht wissen, was da passiert, wenn aus:
1
# define PINK   _SFR_MEM8(0x106)

nun
1
# define PINK 0xF81F

wird. Solange man selbst am PORTK nichts lesen will... Man kann ja aber 
nie wissen, welche der Bibliotheken am Ende dann doch PWM auf PORTK 
machen will oder so.

was mir dazu generell einfällt:
die Arduino-Umgebung ist ja für "interessierte Künstler, Schulen, ..." 
entwickelt worden, um zB mal schnell ne Istallation mit blinkenden 
lampen und sich drehenden Motoren fertig zu machen.
Von angepeilten Publikum hat ganz sicher noch nie jemand was von einem 
Namespace, einem Scope oder einem Preprozessor gehört. Ist doch logisch 
(ging mir doch auch so), dass das kräftig knallt. Das bereitwillige 
Bereitstellen von diversen Bibliotheken in C++ fördert das ja auch noch. 
Da schaut niemand mehr in ein (Fach)Buch seiner Wahl. war wohl auch 
nicht Ansinnen des Arduino-Projekts.
Die GesamtEntwicklung des Projekts "Arduino" und seine vielen Akteure, 
die nun auch gern "Arduino" draufschreiben wollen, lässt natürlich auf 
der anderen Seite zu, dass man mal eben schnell nen kleinen Webserver 
zusammenbastelt, der einem aufm Mobiltelefon im Browser selbst 
gesammelte Sensordaten als Graph darstellt. Leider, das ist die andere 
Seite der Medaille, fördert es auch die Naivität und Blauäugigkeit der 
heranwachsenden Generation ( siehe Maker-Szene ). Aber das ist n anderes 
Thema: ich will das hier nicht weiter ausführen.

Zurück zu "Code und PINK"
1
#define PINK 0xF81F
2
//static const uint16_t PINK   = 0xF81F;
3
//static const uint16_t PANK   = 0xF81A;
4
//static const uint16_t PUNK   = 0xF81B;
5
static const uint16_t PONK   = 0xF81C;
6
static const uint16_t BLUE     = 0x001F;
7
static const uint16_t RED     = 0xF800;
8
9
void setup() {
10
    // put your setup code here, to run once:
11
}
12
13
void loop() {
14
    // put your main code here, to run repeatedly:
15
}
Ausgabe (natürlich inkusive Warnung weiter oben, die längst nicht mehr 
sichtbar ist ;)) brav:
1
Linking everything together...
2
"C:\\Program Files (x86)\\Arduino\\hardware\\tools\\avr/bin/avr-gcc" -Wall -Wextra -Os -g -flto -fuse-linker-plugin -Wl,--gc-sections -mmcu=atmega2560 -o "C:\\Users\\ar\\AppData\\Local\\Temp\\arduino_build_793233/PINK_TEST.ino.elf" "C:\\Users\\ar\\AppData\\Local\\Temp\\arduino_build_793233\\sketch\\PINK_TEST.ino.cpp.o" "C:\\Users\\ar\\AppData\\Local\\Temp\\arduino_build_793233/core\\core.a" "-LC:\\Users\\ar\\AppData\\Local\\Temp\\arduino_build_793233" -lm
3
"C:\\Program Files (x86)\\Arduino\\hardware\\tools\\avr/bin/avr-objcopy" -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0 "C:\\Users\\ar\\AppData\\Local\\Temp\\arduino_build_793233/PINK_TEST.ino.elf" "C:\\Users\\ar\\AppData\\Local\\Temp\\arduino_build_793233/PINK_TEST.ino.eep"
4
"C:\\Program Files (x86)\\Arduino\\hardware\\tools\\avr/bin/avr-objcopy" -O ihex -R .eeprom "C:\\Users\\ar\\AppData\\Local\\Temp\\arduino_build_793233/PINK_TEST.ino.elf" "C:\\Users\\ar\\AppData\\Local\\Temp\\arduino_build_793233/PINK_TEST.ino.hex"
5
"C:\\Program Files (x86)\\Arduino\\hardware\\tools\\avr/bin/avr-size" -A "C:\\Users\\ar\\AppData\\Local\\Temp\\arduino_build_793233/PINK_TEST.ino.elf"
6
Der Sketch verwendet 662 Bytes (0%) des Programmspeicherplatzes. Das Maximum sind 253952 Bytes.
7
Globale Variablen verwenden 9 Bytes (0%) des dynamischen Speichers, 8183 Bytes für lokale Variablen verbleiben. Das Maximum sind 8192 Bytes.

von Oliver S. (oliverso)


Lesenswert?

Axel R. schrieb:
> Knallen wird's wahrscheinlich erst, wenn man tatsächlich auf Port "K"
> zugreifen will.
> der BeispielCode wird mit #define PINK.. anstandslos compilert.
> Will garnicht wissen, was da passiert, wenn aus:1# define PINK
> _SFR_MEM8(0x106)
>
> nun
> 1# define PINK 0xF81F
>
> wird.

Bei einem Lesezugriff auf das „Register“ PINK (wofür es ja primär 
gedacht ist), passiert beim compilieren gar nichts, außer eventuell 
einer weiteren Warnung über einen nicht passenden Wertebereich. Der 
„gelesene“ Wert ist halt konstant, daher wird das Programm nicht das 
tun, was man erwartet.

Oliver

von Rolf M. (rmagnus)


Lesenswert?

Axel R. schrieb:
> Ja, in der TAAT, wenn man alle Warnungen
> beachtet.C:\Users\ar\Documents\Arduino\PINK_TEST\PINK_TEST.ino:1:0:
> warning: "PINK" redefined
>  #define PINK 0xF81F

Ja, leider setzt der GCC das nicht konsequent um. Eigentlich müsste das 
mit Fehler abbrechen, da Mehrfachdefinitionen von Makros nur mit dem 
gleichen Inhalt erlaubt sind. GCC warnt leider nur.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Jörg W. schrieb:
> Korrekt wäre es eigentlich gewesen, wenn die avr-libc seinerzeit gleich
> mit standardkonformen Namen ins Rennen gegangen wäre und statt PINK dann
> _PINK benutzt hätte (liegt im implementation namespace).

Wäre das wirklich korrekt?

Ich dachte, die Reserviertheit der Identifier mit führendem Underscore
bezieht sich auf die C-Standardbibliothek, d.h. in bestimmten Kontexten
darf ausschließlich die Standardbibliothek solche Identifier deklarieren
oder definieren. Da avr/io.h zwar mit der AVR-Libc mitgeliefert wird,
aber kein Bestandteil der C-Standards ist, wäre es IMHO sogar falsch,
ein Makro namens _PINK zu definieren, weil Makros, die mit Underscore
und Großbuchstaben beginnen, der Standardbibliothek vorbehalten sind.

Meiner Meinung nach gehört avr/io.h – wie auch jede andere Bibliothek,
die nicht durch den C-Standard abgedeckt ist – in den application
namespace, weswegen die Benennung des Registers mit PINK genau richtig
ist.

IMHO sollte auch der Undescore in _delay_ms und _delay_us aus
util/delay.h entfallen und dafür bei itoa, utoa usw. aus stdlib.h einer
hinzugefügt werden (oder besser diese Funktionen aus stdlib.h und libc.a
entfernt und in eine andere Bibliothek ausgelagert werden).

Ich bin mir allerdings nicht sicher, ob ich die diesbezüglichen Regeln
im C-Standard richtig verstanden habe. Ich habe darin auch keine
Definitionen der von dir verwendeten Begriffe "implementation namespace"
und "application namespace" gefunden (wo kommen diese eigentlich her?)
Möglicherweise ist also das oben Geschriebene kompletter Unfug ;-)

von Rolf M. (rmagnus)


Lesenswert?

Yalu X. schrieb:
> Ich dachte, die Reserviertheit der Identifier mit führendem Underscore
> bezieht sich auf die C-Standardbibliothek, d.h. in bestimmten Kontexten
> darf ausschließlich die Standardbibliothek solche Identifier deklarieren
> oder definieren.

Der Standard sagt nur, dass sie reserviert sind:
"All identifiers that begin with an underscore and either an uppercase 
letter or another underscore are always reserved for any use."

und was passiert, wenn man einen solchen selbst definiert, ist 
undefinert:
"If the program declares or defines an identifier in a context in which 
it is reserved (other than as allowed by 7.1.4), or defines a reserved
identifier as a macro name, the behavior is undefined."

Die Frage ist jetzt, ob "reserved" sich auf den kompletten Compiler 
inklusive aller ggf. mitgelieferten "System"-Bibliotheken bezieht oder 
ob letztere Teil von "the program" sind.

> Meiner Meinung nach gehört avr/io.h – wie auch jede andere Bibliothek,
> die nicht durch den C-Standard abgedeckt ist – in den application
> namespace, weswegen die Benennung des Registers mit PINK genau richtig
> ist.

Also "the program". In dem Fall hättest du recht. Allerdings werden 
solche Identifier in systemnahen Bibliotheken sehr oft genutzt.

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


Lesenswert?

Rolf M. schrieb:
> Die Frage ist jetzt, ob "reserved" sich auf den kompletten Compiler
> inklusive aller ggf. mitgelieferten "System"-Bibliotheken bezieht

So wird das üblicherweise gesehen. Eine ganz exakte Trennlinie gibt es 
allerdings nicht.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Rolf M. schrieb:
>> Meiner Meinung nach gehört avr/io.h – wie auch jede andere Bibliothek,
>> die nicht durch den C-Standard abgedeckt ist – in den application
>> namespace, weswegen die Benennung des Registers mit PINK genau richtig
>> ist.
>
> Also "the program". In dem Fall hättest du recht.

Wobei auch der Begriff "program" nicht ganz eindeutig ist. Eine klare
Definition dafür habe ich im Standard leider nicht gefunden, aber
immerhin folgenden Satz:
1
4. Conformance
2
...
3
A strictly conforming program shall use only those features of the
4
language and library specified in this International Standard.

Da die Verwendung von Bibliotheken, die nicht zum C-Standard gehören, ja
sicher nicht verboten ist, können diese eigentlich nur zum "program"
gehören.

> Allerdings werden solche Identifier in systemnahen Bibliotheken sehr
> oft genutzt.

Das widerspricht halt ein wenig der ursprünglichen Intention hinter den
reservierten Identifiern, die ja dazu dienen, Name-Clashes mit
implementationsspezifisch durch den Compiler oder die
Standard-Bibliothek vordefinierten Makros und Funktion/Objekten mit
external linkage zu vermeiden.

Bei der AVR-Libc sehe ich diesbezüglich zwar kein Problem, da die
Standardbibliothek und die AVR-spezifischen Dinge aus einer Hand kommen
und auch mit den GCC-Entwicklen kommuniziert wird. Bei anderen
Bibliotheken finde ich die teilweise in Massen verwendeten reservierten
Identifier aber nicht so gut.

: Bearbeitet durch Moderator
von Rolf M. (rmagnus)


Lesenswert?

Yalu X. schrieb:
> 4. Conformance
> ...
> A strictly conforming program shall use only those features of the
> language and library specified in this International Standard.
>
> Da die Verwendung von Bibliotheken, die nicht zum C-Standard gehören, ja
> sicher nicht verboten ist, können diese eigentlich nur zum "program"
> gehören.

Alternativ wäre ein Programm, das zusätzliche Bibliotheken benutzt, eben 
nicht mehr "strictly conforming". Es kann aber immer noch "conforming" 
sein.
"A conforming program is one that is acceptable to a conforming 
implementation."

Steht auch in einer Fußnote:
"Strictly conforming programs are intended to be maximally portable 
among conforming implementations. Conforming programs may depend upon 
nonportable features of a conforming implementation."

>> Allerdings werden solche Identifier in systemnahen Bibliotheken sehr
>> oft genutzt.
>
> Das widerspricht halt ein wenig der ursprünglichen Intention hinter den
> reservierten Identifiern, die ja dazu dienen, Name-Clashes mit
> implementationsspezifisch durch den Compiler oder die
> Standard-Bibliothek vordefinierten Makros und Funktion/Objekten mit
> external linkage zu vermeiden.

Allerdings sind Systembibliotheken meistens ja ziemlich eng mit dem 
Compiler verbunden bzw. darauf abgestimmt, so dass da schon dafür 
gesorgt werden kann, dass es da nicht zu Problemen kommt. Das würde ich 
z.B. auch bei der glibc so sehen, die ja noch deutlich mehr beinhaltet 
als nur die ISO-C-Funktionen. Bei der avr-libc würde ich das auch so 
sehen.

> Bei der AVR-Libc sehe ich diesbezüglich zwar kein Problem, da die
> Standardbibliothek und die AVR-spezifischen Dinge aus einer Hand kommen
> und auch mit den GCC-Entwicklen kommuniziert wird. Bei anderen
> Bibliotheken finde ich die teilweise in Massen verwendeten reservierten
> Identifier aber nicht so gut.

Ja, mit "Systembibliohtek" meine ich wirklich die Bibliotheken, die für 
den Low-Level-Zugriff auf die Systemfunktionen da sind, nicht so etwas 
wie z.B. auf einem µC etwas zum ansteuern eines Character-Displays oder 
auf dem PC eine Bibliothek zum PNG dekodieren.

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


Lesenswert?

Rolf M. schrieb:
> Alternativ wäre ein Programm, das zusätzliche Bibliotheken benutzt, eben
> nicht mehr "strictly conforming". Es kann aber immer noch "conforming"
> sein.

So ist es.

Ein AVR-Programm kann praktisch nie "strictly conforming" sein, denn mit 
den dabei zur Verfügung stehenden Ressourcen kann man auf dem AVR nicht 
viel anfangen. Selbst das klassische "Hello world" würde ja nichts tun, 
weil man für eine erfolgreiche Ausgabe nach außerhalb des AVRs die 
strikte Konformität verlassen muss. Ein strikt konformes Programm wie 
bspw.:
1
int main(void)
2
{
3
   return 42;
4
}

wäre wiederum ziemlich sinnlos.

von c-hater (Gast)


Lesenswert?

Jörg W. schrieb:

> Ein AVR-Programm kann praktisch nie "strictly conforming" sein, denn mit
> den dabei zur Verfügung stehenden Ressourcen kann man auf dem AVR nicht
> viel anfangen. Selbst das klassische "Hello world" würde ja nichts tun,
> weil man für eine erfolgreiche Ausgabe nach außerhalb des AVRs die
> strikte Konformität verlassen muss. Ein strikt konformes Programm wie
> bspw.:
>
>
1
> int main(void)
2
> {
3
>    return 42;
4
> }
5
>
>
> wäre wiederum ziemlich sinnlos.

Ich wußte es schon immer: Alles, was strikt C-konform ist, ist ziemlich 
sinnlos.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Jörg W. schrieb:
> Selbst das klassische "Hello world" würde ja nichts tun, weil man für
> eine erfolgreiche Ausgabe nach außerhalb des AVRs die strikte
> Konformität verlassen muss.

Das verstehe ich nicht ganz. Es ist doch problemlos möglich, das 
printf() der Standardbibliothek so zu implementieren, dass der Text auf 
einem angeschlossenen Terminal oder LCD ausgegeben wird. Damit 
funktioniert auch das klassische Hello-World, ohne auf irgendwelche 
implementation-defined Features zugreifen zu müssen.

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


Lesenswert?

c-hater schrieb:
> Ich wußte es schon immer:

Dass du C nicht kennst?

Das hast du mit deiner Aussage deutlich genug demonstriert. Aber da 
hatte sicher auch niemand hier Zweifel angesichts des Nicks.

Yalu X. schrieb:
> Es ist doch problemlos möglich, das printf() der Standardbibliothek so
> zu implementieren, dass der Text auf einem angeschlossenen Terminal oder
> LCD ausgegeben wird.

Da es aber eben nicht das überall gleiche Standard-Ausgabegerät gibt 
bei einem AVR (zwei Beispiele hast du ja genannt), ist das eine eher 
theoretische Möglichkeit. Es würde ja bedeuten, dass man für jede dieser 
Möglichkeiten dann eine eigene Standardbibliothek mitliefern müsste.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Jörg W. schrieb:
> Es würde ja bedeuten, dass man für jede dieser Möglichkeiten dann eine
> eigene Standardbibliothek mitliefern müsste.

Natürlich muss die Standardbibliothek an das jeweilige 
Execution-Environment angepasst sein. Eine der Aufgaben der 
Standardbibliothek ist ja gerade, Unterschiede im Execution-Environment 
hinter einer einheitlichen Softwareschnittstelle zu verbergen. Das ist 
auf dem PC ganz ähnlich, nur dass dort das Execution-Environment weniger 
durch die Hardware, sondern primär durch das Betriebssystem definiert 
wird, weswegen die Standardbibliothek an das jeweilige Betriebssystem 
angepasst werden muss.

Zurück zu den Mikrocontrollern: Ein Wechsel der Ausgabehardware bedeutet 
ja nicht, dass die gesamte Standardbibliothek neu geschrieben werden 
muss. Die notwendigen Anpassungen beschränken sich auf ein paar 
Low-Level-IO-Funktionen, und die einschlägigen Standardbibliotheken wie 
bspw. die AVR-Libc oder die Newlib ermöglichen diese Anpassung sogar 
ohne Modifikation des bestehenden Quellcodes.

Man schreibt also einmal für die verwendete Ausgabehardware die 
entsprechenden IO-Funktionen als Ergänzung zur bestehenden 
Standardbibliothek (falls dies nicht schon ein anderer getan hat) und 
kann fortan "strictly conforming" Hello-World-Programme schreiben.

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


Lesenswert?

Yalu X. schrieb:
> Zurück zu den Mikrocontrollern: Ein Wechsel der Ausgabehardware bedeutet
> ja nicht, dass die gesamte Standardbibliothek neu geschrieben werden
> muss.

Natürlich nicht, aber man müsste trotzdem für alle Kombinationen aus 
MCU-Typen und "Standard"-Ausgaben die passenden Libraries liefern. 
Ziemlich unpraktikabel, auch wenn es möglich wäre.

Jörg W. schrieb:
> Das hast du mit deiner Aussage deutlich genug demonstriert.

Um nur mal ein Beispiel zu nennen: einen Compiler oder Assembler kann 
man völlig problemlos als strikt konformes C-Programm bauen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Jörg W. schrieb:
> Natürlich nicht, aber man müsste trotzdem für alle Kombinationen aus
> MCU-Typen und "Standard"-Ausgaben die passenden Libraries liefern.

Nur für diejenige Kombination, die man tatsächlich vor sich stehen hat. 
Ich erwarte ja nicht, dass du als AVR-Libc-Entwickler diese Aufgabe für 
die ganze Welt übernimmst :)

> Um nur mal ein Beispiel zu nennen: einen Compiler oder Assembler kann
> man völlig problemlos als strikt konformes C-Programm bauen.

Aber auch der muss doch von irgendwoher den Quellcode lesen und die 
Fehlermeldungen und den Binärcode irgendwohin schreiben ;-)

von Veit D. (devil-elec)


Lesenswert?

Jörg W. schrieb:
> Yalu X. schrieb:
>> Zurück zu den Mikrocontrollern: Ein Wechsel der Ausgabehardware bedeutet
>> ja nicht, dass die gesamte Standardbibliothek neu geschrieben werden
>> muss.
>
> Natürlich nicht, aber man müsste trotzdem für alle Kombinationen aus
> MCU-Typen und "Standard"-Ausgaben die passenden Libraries liefern.
> Ziemlich unpraktikabel, auch wenn es möglich wäre.

Hallo,

wenn ich einen Tipp geben darf.  :-)  Das "unpraktikabel" macht Arduino. 
Es gibt bspw. eine Print Klasse und die wird gnadenlos vererbt. Der Core 
Package Ersteller stellt die Controller Gegebenheiten zur Verfügung und 
jeder kann wie gewohnt mit Serial.print oder lcd.print umgehen. Egal 
welchen Controller er hat. Jeder Anwender kann sofort mit der Seriellen 
loslegen und muss sich um nichts kümmern. Ich denke das ist alles andere 
als unpraktikabel.  ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Rolf M. schrieb:
> Ja, leider setzt der GCC das nicht konsequent um. Eigentlich müsste das
> mit Fehler abbrechen, da Mehrfachdefinitionen von Makros nur mit dem
> gleichen Inhalt erlaubt sind. GCC warnt leider nur.

Doch, das ist absolut ok.
Das Programm ist "ill-formed", daher gibt der GCC eine "diagnostic 
message" aus, so wie es der Standard vorschreibt: in diesem Fall eine 
Warnung. Der Standard unterscheidet das nicht weiter in Warnungen oder 
harte Fehler mit Abbruch. Wenn Du mehr möchtest, kann Du "-Werror" 
setzen.

von Rolf M. (rmagnus)


Lesenswert?

Wilhelm M. schrieb:
> Das Programm ist "ill-formed", daher gibt der GCC eine "diagnostic
> message" aus, so wie es der Standard vorschreibt: in diesem Fall eine
> Warnung.

einen Begriff "ill-formed" konnte ich im C-Standard nicht finden.
Was ich gefunden habe:

"An identifier currently defined as an object-like macro shall not be 
redefined by another #define preprocessing directive unless the second 
definition is an object-like macro definition and the two replacement 
lists are identical. Likewise, an identifier currently defined as a 
function-like macro shall not be redefined by another #define 
preprocessing directive unless the second definition is a function-like 
macro definition
that has the same number and spelling of parameters, and the two 
replacement lists are identical."

und

"If a ‘‘shall’’ or ‘‘shall not’’ requirement that appears outside of a 
constraint is violated, the behavior is undefined."

Jetzt ist nur die Frage, ob das ein "constraint" ist oder nicht. Ich hab 
nicht so ganz verstanden, worauf sich dieser Begriff bezieht.
Aber soweit ich sehe, ist eine Warnung wohl tatsächlich nach Standard 
ok. Ich hätte eine Fehlermeldung dennoch besser gefunden.

> Der Standard unterscheidet das nicht weiter in Warnungen oder
> harte Fehler mit Abbruch. Wenn Du mehr möchtest, kann Du "-Werror"
> setzen.

-Werror macht allerdings alle Warnungen zu Fehlern, nicht nur diese 
eine. GCC hat zwar Möglichkeiten, zu beschränken, welche Warnungen in 
Fehler umgewandelt werden, aber keine, die das speziell nur für diese 
Warnung tut.

von Wilhelm M. (wimalopaan)


Lesenswert?

Rolf M. schrieb:
> Wilhelm M. schrieb:
>> Das Programm ist "ill-formed", daher gibt der GCC eine "diagnostic
>> message" aus, so wie es der Standard vorschreibt: in diesem Fall eine
>> Warnung.
>
> einen Begriff "ill-formed" konnte ich im C-Standard nicht finden.
> Was ich gefunden habe:

Schau hier:
https://en.cppreference.com/w/cpp/language/ub

>
> "An identifier currently defined as an object-like macro shall not be
> redefined by another #define preprocessing directive unless the second
> definition is an object-like macro definition and the two replacement
> lists are identical. Likewise, an identifier currently defined as a
> function-like macro shall not be redefined by another #define
> preprocessing directive unless the second definition is a function-like
> macro definition
> that has the same number and spelling of parameters, and the two
> replacement lists are identical."

Schau hier:
https://en.cppreference.com/w/cpp/preprocessor/replace

> Aber soweit ich sehe, ist eine Warnung wohl tatsächlich nach Standard
> ok. Ich hätte eine Fehlermeldung dennoch besser gefunden.

Mach einen PR für den GCC, vielleicht wird er angenommen.

>> Der Standard unterscheidet das nicht weiter in Warnungen oder
>> harte Fehler mit Abbruch. Wenn Du mehr möchtest, kann Du "-Werror"
>> setzen.
>
> -Werror macht allerdings alle Warnungen zu Fehlern, nicht nur diese
> eine. GCC hat zwar Möglichkeiten, zu beschränken, welche Warnungen in
> Fehler umgewandelt werden, aber keine, die das speziell nur für diese
> Warnung tut.

Wenn Du im Vorhinein weiß, welche Warnungen im Code kommen werden, dann 
ist das Problem ja gelöst ;-)

Setze "-Werror" und im Code kannst Du denn an der problematischen Stelle 
ja das wieder zurücksetzen mit entsprechenden #pragma (diagnostic 
push/pop/...).

von Rolf M. (rmagnus)


Lesenswert?

Wilhelm M. schrieb:
>> einen Begriff "ill-formed" konnte ich im C-Standard nicht finden.
>> Was ich gefunden habe:
>
> Schau hier:
> https://en.cppreference.com/w/cpp/language/ub

Das ist aber C++, nicht C.

von Wilhelm M. (wimalopaan)


Lesenswert?

Rolf M. schrieb:
> Wilhelm M. schrieb:
>>> einen Begriff "ill-formed" konnte ich im C-Standard nicht finden.
>>> Was ich gefunden habe:
>>
>> Schau hier:
>> https://en.cppreference.com/w/cpp/language/ub
>
> Das ist aber C++, nicht C.

Genau, der TO startete mit Arduino, das ist C++.

von Yalu X. (yalu) (Moderator)


Lesenswert?

In der GCC-Dokumentation werden Warnings wie folgt definiert:

"Warnings are diagnostic messages that report constructions that are not
inherently erroneous but that are risky or suggest there may have been
an error."

(https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html)

Wenn man die Verletzung eines vom Standard vorgegebenen Constraints in
die Kategorie "inherently erroneous" einordnet¹, und wenn die
GCC-Entwickler ihre eigenen Definitionen ernst nehmen, sollte keine
Warning, sondern ein Error ausgegeben werden.

─────────────
¹) Wenn ich Wilhelms und Rolfs Aussagen richtig interpretiere, halten
   dies beide für angebracht.

: Bearbeitet durch Moderator
von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:
> In der GCC-Dokumentation werden Warnings wie folgt definiert:
>
> "Warnings are diagnostic messages that report constructions that are not
> inherently erroneous but that are risky or suggest there may have been
> an error."

Wie gesagt, es wird nur verlangt, dass eine "diagnostic message" 
ausgegeben wird. Ob das nun eine Warnung oder Fehler ist, liegt im 
Spielraum der Implementierung GCC. Wie die Entwickler von GCC nun das 
Redefinieren von CPP-Textersatz einordnen, ist deren Sache, denke ich. 
Es kann sinnvoll sein, daher wahrscheinlich auch nur Warnung.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Wie die Entwickler von GCC nun das Redefinieren von CPP-Textersatz
> einordnen, ist deren Sache, denke ich.

Ja, natürlich. Mir persönlich ist das auch ziemlich egal, da ich mir
üblicherweise

- bei fehlerhaftem Verhalten des gerade entwickelten Programms und

- bei der Fertigstellung eines Releases vor dem finalen Push

auch gezielt auch die Warnungen anschaue und eliminiere.

@Rolf:

Statt -Werror kannst du auch -pedantic-errors verwenden. Damit wird das
Überschreiben von Makros als Error ausgegeben. Unkritische Dinge, die
den Standard nicht verletzen (wie bspw. -Wparentheses oder
-Wunused-variable), bleiben weiterhin Warnungen.

: Bearbeitet durch Moderator
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Yalu X. schrieb:
>> Um nur mal ein Beispiel zu nennen: einen Compiler oder Assembler kann
>> man völlig problemlos als strikt konformes C-Programm bauen.
>
> Aber auch der muss doch von irgendwoher den Quellcode lesen und die
> Fehlermeldungen und den Binärcode irgendwohin schreiben ;-)

Das geht aber, da der C-Standard ja Dateien als Konzept enthält.

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.