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
staticconstuint16_tPINK=0xF81F;
2
//static const uint16_t PANK = 0xF81A;
3
//static const uint16_t PUNK = 0xF81B;
4
//static const uint16_t PONK = 0xF81C;
5
staticconstuint16_tBLUE=0x001F;
6
staticconstuint16_tRED=0xF800;
7
8
voidsetup(){
9
// put your setup code here, to run once:
10
11
}
12
13
voidloop(){
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
staticconstuint16_tPONK=0xF81C;
5
staticconstuint16_tBLUE=0x001F;
6
staticconstuint16_tRED=0xF800;
7
8
voidsetup(){
9
// put your setup code here, to run once:
10
11
}
12
13
voidloop(){
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.
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. ;-)
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!
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.
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 ...
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" ;-)
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).
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.
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 😀 ...
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.
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.
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.
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.
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
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:#definePINK_SFR_MEM8(0x106)
Wie Du siehst, ist das die Definition eins CPP-Symbols. Und die ist
global (nicht gescoped).
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!
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.
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.
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
staticconstuint16_tPONK=0xF81C;
6
staticconstuint16_tBLUE=0x001F;
7
staticconstuint16_tRED=0xF800;
8
9
voidsetup(){
10
// put your setup code here, to run once:
11
}
12
13
voidloop(){
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:
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
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.
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 ;-)
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.
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.
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.
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.
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.:
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
>intmain(void)
2
>{
3
>return42;
4
>}
5
>
>> wäre wiederum ziemlich sinnlos.
Ich wußte es schon immer: Alles, was strikt C-konform ist, ist ziemlich
sinnlos.
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.
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.
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.
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.
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 ;-)
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. ;-)
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.
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.
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/...).
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.
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.
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.
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.