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.
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
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. ;-)
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
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. ;-)
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.
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.
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?
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?
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.
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?
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.
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 ;-)
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.
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.
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.
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
structA{intx;inty;intz;};
2
Aa{.y=2,.x=1};// error: designator order does not match declaration order
Ich habe davon gesprochen, dass die Reihenfolge in beiden Fällen gleich ist. Ich habe nicht davon gesprochen, dass u.U. eine Warnung auftritt wogegen im anderen Fall ein Fehler.
Also: die Reihenfolge der Initialisierung ist (und muss) immer gleich sein.
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 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.
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.
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?
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.
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.
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_tdata={
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.
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.