Der gcc soll bitte mehr Fehler finden

OP #7357502
Lesenswert?

Wilhelm M. schrieb im Beitrag "Re: avr-gcc: direkter Register Zugriff vs structure-mapping und fehlende Optimierung?"

In C++ benutze ich schon immer ein structure-mapping mit eingenen Register-Typen, so dass auch nur die richtigen Flags für die Register benutzt werden können, ansonsten Compile-Zeit-Fehler

Könnte man das in C ohne ++ auch haben? Etwas ähnliches erreicht man wohl mit Bitfeldern in der struct, aber das ist umständlich, wenn man mehrere Bits gleichzeitig setzen muss.

#7357513
Lesenswert?

Bauform B. schrieb:

Wilhelm M. schrieb im Beitrag "Re: avr-gcc: direkter Register Zugriff vs structure-mapping und fehlende Optimierung?"

In C++ benutze ich schon immer ein structure-mapping mit eingenen Register-Typen, so dass auch nur die richtigen Flags für die Register benutzt werden können, ansonsten Compile-Zeit-Fehler

Könnte man das in C ohne ++ auch haben?

Was hindert Dich denn daran, C++ zu benutzen?

#7358660
Lesenswert?

Ein T. schrieb:

Was hindert Dich denn daran, C++ zu benutzen?

Lustig, daß es für diese einfache Frage gleich vier negative Bewertungen, aber nur eine positive gibt., obwohl die Verwendung von C++ sich zunächst nur in der Nutzung eines anderen Compilers mit beinahe exakt denselben Optionen und Parametern, sowie in der Nutzung nur dieses einen Features erschöpfen, und deswegen also offensichtlich keine größeren Änderungen am Projekt und am Code erfordern würde. Die Abneigungen und Vorurteile gegen C++ scheinen bei einigen Forenteilnehmern ziemlich tief zu sitzen. ;-)

OP #7358815
Lesenswert?

Ein T. schrieb:

Was hindert Dich denn daran, C++ zu benutzen?

Dieses. Nur mal ein altes Programm von gcc auf g++ geändert:

1
cc1plus: warning: command line option '-std=c11' is valid for C/ObjC but not for C++
2
cc1plus: warning: command line option '-Wnested-externs' is valid for C/ObjC but not for C++
3
cc1plus: warning: command line option '-Wbad-function-cast' is valid for C/ObjC but not for C++
4
----
5
In file included from imghdr-syslib.c:4:
6
../include/imageheader.h:23:16: error: expected constructor, destructor, or type conversion before '(' token
7
   23 | _Static_assert (sizeof(image_struct) == 108, " Bad struct size");
8
----
9
imghdr-syslib.c:12:4: warning: C++ designated initializers only available with -std=c++2a' or '-std=gnu++2a'
10
   12 |    .top_of_stack           = &tos,   // 00  0 Initial SP (linker script)
11
      |    ^
12
imghdr-syslib.c:13:4: warning: C++ designated initializers only available with -std=c++2a' or '-std=gnu++2a'
13
   13 |    .crt0                   = crt0,   // 04  1 Initial PC "Reset-Handler

Der erste Eindruck: da geht ja einiges nicht mehr... OK, die ersten Warnungen sind nur lästig. _Static_assert war ganz nett, aber egal. Aber designated initializers würden mir wirklich fehlen.

Edit: es kommt noch "besser":

1
imghdr-syslib.c:17:4: error: either all initializer clauses should be designated or none of them should be

Und noch einen:

1
typedef volatile struct iwdg_struct {
2
// 0x00  IWDG_KR  IWDG Key register
3
   uint32_t  KR;  // 16; //  0  Key value (write only, read 0000h)
4
// 0x04  IWDG_PR  IWDG Prescaler register
5
   uint32_t  PR;  //  3; //  0  PR[2:0] (Prescaler divider)
6
// (...)
7
} iwdg_struct;
8
-----------------
9
../include/STM/iwdg.h:60:3: error: conflicting declaration 'typedef volatile struct iwdg_struct iwdg_struct'
10
   60 | } iwdg_struct;
11
      |   ^~~~~~~~~~~
12
../include/STM/iwdg.h:41:25: note: previous declaration as 'struct iwdg_struct'
13
   41 | typedef volatile struct iwdg_struct {
14
      |                         ^~~~~~~~~~~

nun ja, das ist für mich Grund genug. Ja, das kann man bestimmt alles reparieren. Vielleicht beim nächsten Programm.

#7358952
Lesenswert?

Bauform B. schrieb:

Dieses.

Hm, okay, das sind gute Gründe, wenngleich ich glaube, daß sich die meisten davon mit einem aktuellen g++ und "-std=gnu++20" beheben lassen. HTH, YMMV.

Ansonsten habe ich leider auch keine Idee, wie sich das mit C lösen läßt. Allerdings bin ich keine Referenz, ich benutze C schon lange nur noch in sehr, sehr wenigen Ausnahmefällen. ;-)

#7359244
Lesenswert?

Bauform B. schrieb:

Der erste Eindruck: da geht ja einiges nicht mehr... OK, die ersten Warnungen sind nur lästig.

Da muss man eben die Warnungs-Einstellungen anpassen. Dass der C++-Code nicht gleichzeitig C99 sein kann, ist ja logisch.

_Static_assert war ganz nett, aber egal.

Eigentlich sollte man das eh über das Makro static_assert() verwendet. Damit hätte es dann auch in C++ funktioniert. https://en.cppreference.com/w/c/error/static_assert https://en.cppreference.com/w/cpp/language/static_assert

Aber designated initializers würden mir wirklich fehlen.

Die gibt es zwar ab C++20, haben aber den großen Nachteil, dass sie in der gleichen Reihenfolge genant sein müssen, in der auch die Members definiert sind. Das macht leider den größten Vorteil dieser Art der Initialisierung kaputt.

../include/STM/iwdg.h:60:3: error: conflicting declaration 'typedef volatile struct iwdg_struct iwdg_struct' 60 | } iwdg_struct; | ^~~~~~~~~~~ ../include/STM/iwdg.h:41:25: note: previous declaration as 'struct iwdg_struct' 41 | typedef volatile struct iwdg_struct { | ^~~~~~~~~~~

Hmm, das scheint nur bei volatile aufzutreten. Ich nehme an, dass sich das volatile nicht auf den Typedef überträgt und C++ es deshalb als anderen Typ mit gleichem Namen sieht. Generell braucht man in C++ an der Stelle aber das typedef gar nicht.

OP #7359438
Lesenswert?

Rolf M. schrieb:

Aber designated initializers würden mir wirklich fehlen.

Die gibt es zwar ab C++20, haben aber den großen Nachteil, dass sie in der gleichen Reihenfolge genant sein müssen, in der auch die Members definiert sind. Das macht leider den größten Vorteil dieser Art der Initialisierung kaputt.

Echt jetzt? Ich kann es kaum glauben. Also auch in der allerneuesten Version :( Ist das so schwer zu implementieren? Das gibt's doch schon seit C11?

Markus F. schrieb:

Ist es nicht irgendwie - äh - inkonsequent, vom Compiler zu verlangen, mehr Fehler zu finden, ohne mehr Fehler zu finden?

Naja, also so gesehen, nun ja... Mein Wunsch ist in Erfuellung gegangen, der g++ findet sogar viel mehr Fehler als der gcc, was will ich mehr :)

#7361039
Lesenswert?

Bauform B. schrieb:

Wilhelm M. schrieb im

In C++ benutze ich schon immer ein structure-mapping mit eingenen Register-Typen, so dass auch nur die richtigen Flags für die Register benutzt werden können, ansonsten Compile-Zeit-Fehler

Könnte man das in C ohne ++ auch haben? Etwas ähnliches erreicht man wohl mit Bitfeldern in der struct, aber das ist umständlich, wenn man mehrere Bits gleichzeitig setzen muss.

Hi Bauform B, darf ich nochmal nachfragen: ich verstehe deine Frage nicht.

Dein Code nutzt doch structure mapping? Zumnindest zeigst Du später C++ Fehlermeldungen einer HAL in C. Um was geht es Dir genau? Willst Du Warnings wenn Du versehentlich falsche Konstanten einem Register zuweist?

A la

1
GPIOA.MODE = IWDG_PRESCALER_128;    // UPS

Würde mich auch interessieren, wie Wilhelm M. das gelöst hat. Hast Du eigene Header in C++ dafür erstellt?

OP #7361056
Lesenswert?

Klaus H. schrieb:

Zumnindest zeigst Du später C++ Fehlermeldungen

Das war nur meine Antwort auf die Frage "warum benutzt du nicht C++". Ich hatte da nur kurz versucht, ein altes C-Programm mit g++ statt mit gcc zu übersetzen.

Hast Du eigene Header in C++ dafür erstellt?

Nein, überhaupt nicht, ich habe außer diesem Versuch noch nichts mit C++ gemacht. Aber alle schwärmen davon und neugierig bin ich auch.

#7361075
Lesenswert?

Wilhelm M. schrieb im

In C++ benutze ich schon immer ein structure-mapping mit eingenen Register-Typen, so dass auch nur die richtigen Flags für die Register benutzt werden können, ansonsten Compile-Zeit-Fehler

Würde mich auch interessieren, wie Du das gelöst hast. Hast Du eigene Header in C++ dafür erstellt?

#7369481
Lesenswert?

Bauform B. schrieb:

Echt jetzt? Ich kann es kaum glauben. Also auch in der allerneuesten Version :( Ist das so schwer zu implementieren? Das gibt's doch schon seit C11?

Das ist in C++ so notwendig, denn das ist nun mal die Art, wie die Datenelemente eines Objektes initialisiert werden - und das ist seit Urzeiten so (s.a. Initialisierungslisten von ctor'en). Von daher ist es konsequent, das auch hier so umzusetzen. Tja, einer der Unterschiede zwischen C und C++. C++ ist halt keine Obermenge von C.

#7369490
Lesenswert?

Klaus H. schrieb:

Wilhelm M. schrieb im

In C++ benutze ich schon immer ein structure-mapping mit eingenen Register-Typen, so dass auch nur die richtigen Flags für die Register benutzt werden können, ansonsten Compile-Zeit-Fehler

Würde mich auch interessieren, wie Du das gelöst hast. Hast Du eigene Header in C++ dafür erstellt?

Dann solltest Du das auch in dem passenden Thread fragen und nicht hier, wo man das nur durch Zufall mitbekommt.

In Kürze:

In dem Aggregat-Typ einer internen Peripherie (z.B. Timer, ...) werden statt std::byte spezielle DT verwendet. Diese werden aus templates erzeugt, für z.B. DataRegister, ControlRegister, FlagRegister. Je nach Semantik der HW-Register. Parametriert werden diese templates mit den Typen der möglichen Werte dieses Registers (bei mir: Aufzählungstypen). Der aufwändigste Punkt ist also das Erzeugen der enum-Typen. Dies habe ich anfänglich zu Fuß gemacht. Hört sich nach viel Arbeit an, ging jedoch nach Bedarf (zusammen mit den Aggregaten, dem Adress-Mapping, ...) und damit überschaubar. Dann hatte ich mal ein ein Python-Script, was auch der XML-Beschreibung der Peripherie-Register das Zeug erzeugt hat. Das funktioniert aber leider nicht mehr, weil das XML-Schema sich geändert hat. Nun mache ich notwendige Ergänzungen wieder nach Bedarf ;-)

Persönliche Seite #7371938
Lesenswert?

Bauform B. schrieb:

Rolf M. schrieb:

Aber designated initializers würden mir wirklich fehlen.

Die gibt es zwar ab C++20, haben aber den großen Nachteil, dass sie in der gleichen Reihenfolge genant sein müssen, in der auch die Members definiert sind. Das macht leider den größten Vorteil dieser Art der Initialisierung kaputt.

Echt jetzt? Ich kann es kaum glauben.

Problem bei C++ ist, dass ein Initializer das Ergebnis eines Funktionsaufrufs sein darf, und so ein Funktionsaufruf kann Seiteneffekte haben. Intuitiv wüede man die Funktionsaufrufe "von links nach rechts" auswerten, und das Ergebnis wäre denn i.A. abhängig von der Reihenfolge.

Eine Design-Entscheidung wäre, die Verantwortung komplett dem Anwender zu überlassen.

Eine andere Design-Entscheidung wäre, etwas Ordnung in die Sache zu bringen und nur Initializer in ihrer natürlchen Reihenfpolge zuzulassen.

Offenbar hat man den "sichereren" Weg No. 2 gewählt.

...und solche Problemchen hat man in C nicht, weil da ein Initializer nicht das Ergebnis eines Funktionsaufrufs sein darf, auch wenn das Ergebnis zu Compilezeit bekannt ist. Keine Ahnung wie das mit C23 + constexpr gelöst ist; vermutlich dadurch, dass constexpr-Funktionen keine Seiteneffekte haben dürfen, so dass deren Aufrufe kommutieren.

OP #7371988
Lesenswert?

Johann L. schrieb:

Problem bei C++ ist, dass ein Initializer das Ergebnis eines Funktionsaufrufs sein darf, und so ein Funktionsaufruf kann Seiteneffekte haben.

Eine andere Design-Entscheidung wäre, etwas Ordnung in die Sache zu bringen und nur Initializer in ihrer natürlchen Reihenfpolge zuzulassen.

OK, wenigstens gibt es eine vernünftige Erklärung, danke dafür! Man kann eben nicht alles auf einmal haben...

#7372081
Lesenswert?

Wilhelm M. schrieb:

Bauform B. schrieb:

Echt jetzt? Ich kann es kaum glauben. Also auch in der allerneuesten Version :( Ist das so schwer zu implementieren? Das gibt's doch schon seit C11?

Das ist in C++ so notwendig, denn das ist nun mal die Art, wie die Datenelemente eines Objektes initialisiert werden - und das ist seit Urzeiten so (s.a. Initialisierungslisten von ctor'en). Von daher ist es konsequent, das auch hier so umzusetzen.

Das stimmt aber nicht. Ich wünschte, es wäre so wie bei den Initialisierungslisten von Konstruktoren. Bei denen kann man die Member auch in einer anderen Reihenfolge angeben. Die Initialisierung erfolgt dann in der Reihenfolge der Definition der Member, nicht in der Reihenfolge in der Initialisierungsliste. Bei den designated initializers bricht der Compiler dagegen mit Fehler ab, wenn die Reihenfolge nicht übereinstimmt. Also ein grundlegend unterschiedliches Verhalten.

#7372101
Lesenswert?

Rolf M. schrieb:

Wilhelm M. schrieb:

Bauform B. schrieb:

Echt jetzt? Ich kann es kaum glauben. Also auch in der allerneuesten Version :( Ist das so schwer zu implementieren? Das gibt's doch schon seit C11?

Das ist in C++ so notwendig, denn das ist nun mal die Art, wie die Datenelemente eines Objektes initialisiert werden - und das ist seit Urzeiten so (s.a. Initialisierungslisten von ctor'en). Von daher ist es konsequent, das auch hier so umzusetzen.

Das stimmt aber nicht.

Was stimmt nicht?

Ich wünschte, es wäre so wie bei den Initialisierungslisten von Konstruktoren. Bei denen kann man die Member auch in einer anderen Reihenfolge angeben. Die Initialisierung erfolgt dann in der Reihenfolge der Definition der Member, nicht in der Reihenfolge in der Initialisierungsliste.

Genau. Das sagte ich oben.

Bei den designated initializers bricht der Compiler dagegen mit Fehler ab, wenn die Reihenfolge nicht übereinstimmt. Also ein grundlegend unterschiedliches Verhalten.

Nein. Die Datenelemente werden immer in derselben Reihenfolge initialisiert. In dem einen wie in dem anderen Fall.

#7372165
Lesenswert?

Wilhelm M. schrieb:

Was stimmt nicht?

Dass das Verhalten gleich wäre.

Bei den designated initializers bricht der Compiler dagegen mit Fehler ab, wenn die Reihenfolge nicht übereinstimmt. Also ein grundlegend unterschiedliches Verhalten.

Nein. Die Datenelemente werden immer in derselben Reihenfolge initialisiert. In dem einen wie in dem anderen Fall.

Der Standard sagt unter "List-initialization":

"The ordered identifiers in the designators of the designated-initializer-list shall form a subsequence of the ordered identifiers in the direct non-static data members of T."

und bringt auch ein Beispiel:

1
struct A { int x; int y; int z; };
2
A a{.y = 2, .x = 1}; // error: designator order does not match declaration order
3
A b{.x = 1, .z = 2}; // OK, b.y initialized to 0
#7372167
Lesenswert?

Wilhelm M. schrieb:

Rolf M. schrieb:

Wilhelm M. schrieb:

Was stimmt nicht?

Dass das Verhalten gleich wäre.

Ich habe davon gesprochen, dass die Reihenfolge in beiden Fällen gleich ist.

Nö. Das hast du vielleicht gemeint, aber gesprochen hast du davon nirgends. Es ging um diese Aussage:

Rolf M. schrieb:

Aber designated initializers würden mir wirklich fehlen.

Die gibt es zwar ab C++20, haben aber den großen Nachteil, dass sie in der gleichen Reihenfolge genant sein müssen, in der auch die Members definiert sind.

und du meintest dazu:

Wilhelm M. schrieb:

Das ist in C++ so notwendig, denn das ist nun mal die Art, wie die Datenelemente eines Objektes initialisiert werden - und das ist seit Urzeiten so (s.a. Initialisierungslisten von ctor'en). Von daher ist es konsequent, das auch hier so umzusetzen.

Aber genau diese Vorgabe (Reihenfolge bei den Initializers muss der bei der Definition entsprechen) ist bei den Konstruktor-Initialisierungslisten so nicht gegeben. Also ist es eben nicht konsequent, weil unterschiedlich umgesetzt. Und genau dieser Zwang zur gleichen Reihenfolge ist das, was mich an den designated-initializers in C++ wirklich stört.

#7372174
Lesenswert?

Rolf M. schrieb:

Wilhelm M. schrieb:

Das ist in C++ so notwendig, denn das ist nun mal die Art, wie die Datenelemente eines Objektes initialisiert werden - und das ist seit Urzeiten so (s.a. Initialisierungslisten von ctor'en). Von daher ist es konsequent, das auch hier so umzusetzen.

Aber genau diese Vorgabe (Reihenfolge bei den Initializers muss der bei der Definition entsprechen) ist bei den Konstruktor-Initialisierungslisten so nicht gegeben.

Wenn Du genau liest, steht dort, wie die Objekte initialisiert werden! Und die ist nun mal immer gleich (egal wie die Initialisierungsliste geordnet ist). Den Verweis auf die Initlisten habe ich dort deswegen gezogen, weil auch dort die Reihenfolge eben auch der Deklarationsreihenfolge entspricht, egal wie die Initliste geordnet ist. Dort gibt es leider(!) nur eine Warnung.

#7372183
Lesenswert?

Wilhelm M. schrieb:

Der aufwändigste Punkt ist also das Erzeugen der enum-Typen. Dies habe ich anfänglich zu Fuß gemacht. Hört sich nach viel Arbeit an, ging jedoch nach Bedarf (zusammen mit den Aggregaten, dem Adress-Mapping, ...) und damit überschaubar. Dann hatte ich mal ein ein Python-Script, was auch der XML-Beschreibung der Peripherie-Register das Zeug erzeugt hat. Das funktioniert aber leider nicht mehr, weil das XML-Schema sich geändert hat. Nun mache ich notwendige Ergänzungen wieder nach Bedarf ;-)

Läßt sich das Python-Skript nicht an das neue XML-Schema anpassen?

#7372386
Lesenswert?

Wilhelm M. schrieb:

Klaus H. schrieb:

Würde mich auch interessieren, wie Du das gelöst hast. Hast Du eigene Header in C++ dafür erstellt?

Dann solltest Du das auch in dem passenden Thread fragen und nicht hier, wo man das nur durch Zufall mitbekommt.

Sorry, du hattest in diesem Thread schon teilgenommen und im anderen Thread ging es ja eher um ein Compiler Problem.

Kannst Du mal ein Beispiel zeigen? Ich hatte schon verschieden Varianten mit templates und structs überlegt. Aber keine davon war so, dass ich das Gefühl hatte: ja, die erhöhte Fehleraufdeckung rechtfertigt den Aufwand.

#7373084
Lesenswert?

Bauform B. schrieb:

Aber designated initializers würden mir wirklich fehlen.

Ich wüßte nicht, wie ich ohne die klarkommen sollte. Sich nur auf die Reihenfolge verlassen zu müssen, erscheint mir extrem fehlerträchtig. Ich habe oft Arrays von Structs, die sehr groß werden, da kann ich mir nicht alles merken. Und bei Änderungen würde ja alles durcheinander gewürfelt. Hier mal ein Auszug:

1
  .par.dac[DAC_VENERGY].gain = DAC_6KV_GAIN,
2
  .par.dac[DAC_VENERGY].offset = DAC_6KV_OFFSET,
3
  .par.dac[DAC_VENERGY].lower = 0.0,
4
  .par.dac[DAC_VENERGY].upper = MOD_6KV_LIMIT,
5
  .par.dac[DAC_VENERGY].val = 0.0 * MOD_6KV_SGAIN,
6
  .par.adc[ADC_VENERGY].gain = MON_6KV_GAIN,
7
  .par.adc[ADC_VENERGY].offset = MON_6KV_OFFSET,
8
  .par.adc[ADC_IENERGY].gain = MONI_6KV_GAIN,
9
  .par.adc[ADC_IENERGY].offset = MONI_6KV_OFFSET,
10
  .par.adc[ADC_IENERGY].comp = MONI_6KV_COMP,

Man sieht hier, daß die Parameter je Modul gruppiert sind, die sich aber in Structs an 3 verschiedenen Adressen befinden. Ein Zwang zu aufsteigenden Adressen würde alles vollkommen unleserlich machen.

#7373648
Lesenswert?

OK, das geht in C++ so nicht, aber ich finde die Darstellung nicht gut. Darin sehe ich keinen Vorteil. Es geht die Hierarchie der Daten verloren. Eine verschaltelte Initialisierung finde ich besser. So müsste es in C und in C++20 funktionieren.

1
data_t data = { 
2
  .par = {  
3
    .dac = {  
4
        { /*DAC_VENERGY*/
5
        .gain = DAC_6KV_GAIN, 
6
        .offset = DAC_6KV_OFFSET, 
7
        .lower = 0.0,
8
        .upper = MOD_6KV_LIMIT,
9
        .val = 0.0 * MOD_6KV_SGAIN 
10
        }
11
    },
12
    .adc = {
13
        { /* ADC_VENERGY */
14
        .gain = MON_6KV_GAIN,
15
        .offset = MON_6KV_OFFSET
16
        },
17
        { /* ADC_IENERGY*/ 
18
        .gain = MONI_6KV_GAIN,
19
        .offset = MONI_6KV_OFFSET,
20
        .comp = MONI_6KV_COMP
21
        }
22
    }
23
  }
24
};

Ein Nachteil in C++ ist IMHO das Array designators nicht funktionieren. Aber das ist für micht verschmerzbar.

#7374068
Lesenswert?

Wilhelm M. schrieb:

Was sollte da durcheinander gewürfelt werden?

Ich meinte, wenn man keine designated initializers verwenden könnte. Ich hab das hier im Forum kennen und schätzen gelernt und benutze es seitdem ausschließlich. Die Initialisierung nach Reihenfolge hatte mich schon immer geärgert und oft Fehler verursacht. Während einer Projektentwicklung kommt es regelmäßig vor, daß Elemente verschoben, hinzugefügt, umbenannt oder gelöscht werden. Ich kenne keinen, wo schon zu Anfang gleich alles in Stein gemeißelt ist. Dagegen spricht schon, daß die Anwender nach der ersten Version noch haufenweise Extrawünsche haben, die man nachträglich implementieren muß. Ich arbeite daher auch gerne mit enums, die die Order in einem Array festlegen. Ich überlasse also nichts dem Zufall. Perfekt wäre es noch, wenn man auch bei einem Funktionsaufruf die Argumente namentlich übergeben könnte, d.h. nicht zwingend nach Reihenfolge. Da oft 3-4 Argumente ausreichen, kann ich mir das grad noch merken.

Beitrag #7374072 wurde von einem Moderator gelöscht.
Beitrag #7410205 wurde von einem Moderator gelöscht.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren