Hallo zusammen, habe ein struct angelegt. Bestehend aus mehreren Statusbits. typedef struct { unsigned ENABLE :1; unsigned RESET :1; unsigned NEW :1; unsigned EN :1; unsigned MODE :3; unsigned MODE2 :1; unsigned UP :1; unsigned DOWN :1; unsigned DIRECT :1; unsigned STAT :1; unsigned MUX :1; unsigned CHANNEL :3; }CFG; Zusammengerechnet ergibt dies zwei Bytes. Nun möchte ich zum Beispiel das definierte Struct in eine 16Bit Variable kopieren. Nun suche ich eine Möglichkeit, dem Compiler mitzuteilen, das mein Struct auch da reinpasst. Gruß Sascha
> Nun suche ich eine Möglichkeit, dem Compiler mitzuteilen, das mein > Struct auch da reinpasst. Das brauchst Du ihm nicht zu sagen, das weiss der auch so. Ich vermute Du suchst was anderes: Wenns standardwidrig sein darf man union, sonst man 3 memcpy.
Möchtest du die Daten über eine Kommunikations Schnittstelle abschicken oder abspeichern? Dann geht das nicht direkt, da C(++) das Layout von Bitfeldern nicht definiert. Unter Serialisierung ist das mit Lösung erläutert.
Kommunikations Schnittstelle ist korrekt. Es geht um SPI. Wie würde sowas denn mit dem Union aussehen? Gruß Sascha
sascha_focus schrieb: > Nun suche ich eine > Möglichkeit, dem Compiler mitzuteilen, das mein Struct auch da > reinpasst. __attribute__((packed)) wär' zu erwähnen.
sascha_focus schrieb: > Kommunikations Schnittstelle ist korrekt. Es geht um SPI. Wie würde > sowas denn mit dem Union aussehen? Ungefähr so:
1 | typedef union { |
2 | uint16_t alles; |
3 | struct bits { |
4 | unsigned ENABLE :1; |
5 | unsigned RESET :1; |
6 | unsigned NEW :1; |
7 | unsigned EN :1; |
8 | unsigned MODE :3; |
9 | unsigned MODE2 :1; |
10 | unsigned UP :1; |
11 | unsigned DOWN :1; |
12 | unsigned DIRECT :1; |
13 | unsigned STAT :1; |
14 | unsigned MUX :1; |
15 | unsigned CHANNEL :3; |
16 | };
|
17 | }CFG; |
Die saubere Lösung besteht darin, die Bits einzeln an zentraler Stelle ein- und auszupacken:
1 | uint16_t encode(CFG x) { |
2 | uint16_t tmp = 0; |
3 | if(x.ENABLE) tmp |= 0x0001; |
4 | if(x.RESET) tmp |= 0x0002; |
5 | /* usw */
|
6 | return tmp; |
7 | }
|
8 | |
9 | CFG decode(uint16_t x) { |
10 | CFG x; |
11 | memset(&x, 0, sizeof(CFG)); |
12 | if(uint16_t & 0x0001) x.ENABLE = 1; |
13 | if(uint16_t & 0x0002) x.RESET = 1; |
14 | /* usw */
|
15 | return x; |
16 | }
|
Dann kannst du statt einzelnen Bits auch bool benutzen und mit "true"/"false" arbeiten. Manche Compiler mögen das lieber. Damit garantierst du, dass es überall funktioniert und du hast das Datenformat gleichzeitig eindeutig definiert. Schnittstellen gehören immer sauber definiert. Normalerweise bin ich für die Union-Lösung, aber in deinem Fall ist sie absolut ungeeignet. Was man innerhalb seines Programms macht, bleibt einem selbst überlassen, aber in dem Moment, wo Kommunikation stattfindet, lässt man das besser sein.
Die Union Lösung ist halt in C Implementation defined d.h. unportabel, und in C++ komplett verboten. Daher ist die 2. Lösung mit den Bit-Operationen deutlich besser. Da ist nur ein Tippfehler - es sollte wohl "x" statt uint16_t heißen
Niklas Gürtler schrieb: > Da ist nur ein Tippfehler - es sollte > wohl "x" statt uint16_t heißen Das ist vollkommen korrekt. ;-) Und statt "x" zweimal zu vergeben, sollte man vielleicht eins davon anders benennen. Aber hey, wofür hat man Compiler. ;-)
:
Bearbeitet durch User
g457 schrieb: >> Nun suche ich eine Möglichkeit, dem Compiler mitzuteilen, das > mein >> Struct auch da reinpasst. > > Das brauchst Du ihm nicht zu sagen, das weiss der auch so. Ich vermute > Du suchst was anderes: Wenns standardwidrig sein darf man union, sonst > man 3 memcpy. Union geht nicht. Wenn doch, dann zuvall
WENN es für eine konkrete Plattform ist, UND du deinen Compiler kennst UND konfigurierst UND Du entsprechende asserts einbaust, DANN kannst Du Unions verwenden. Unions sind halt im Zugriff ausdrucksstärker als die portablen Aufsätze. Ja, in 10 Jahren wird man dir das vorwerfen, ... hättest Mal sofort richtig gemacht, .... Die Pharisäer, die alles richtig machen und bei deren Software keiner Änderung braucht.
Schwachsinn, nicht in 100 Jahren, sondern heute. Code sollte soweit wie möglich portablen gehalten werden. Für die Aufgabe gibt es einfache Alternativen. Warum dann so einen sch.. machen? Mach es gleich richtig, kostet nichts und tut nicht weh. ?
Schon 100 mal schrieb: > Code sollte soweit wie möglich portablen gehalten werden. Portabler Code, ja. Bei HW- oder plattformspezifischem Code nein. Ein Registerlayout mit structs (statt diesem 1<<PIN1) ist besser les- und statisch analysierbar. Schon 100 mal schrieb: > Mach es gleich richtig, kostet nichts und tut nicht weh. Dann zeig doch Mal eine Lösung, die nicht viel kostet und wartbar ist (wartbar im Sinne von an einer Stelle) Oh, sorry, meinte eigentlich kann structs verwenden, aber das Fass ist ähnlich. Ob der TO Unions braucht, wissen wir ja nicht.
Achim S. schrieb: > Dann zeig doch Mal eine Lösung, die nicht viel kostet und wartbar ist > (wartbar im Sinne von an einer Stelle) https://erlkoenig90.github.io/uSer-doc/html/tutorial.html#ConWidth
Achim S. schrieb: >> Mach es gleich richtig, kostet nichts und tut nicht weh. > Dann zeig doch Mal eine Lösung, die nicht viel kostet und wartbar ist > (wartbar im Sinne von an einer Stelle) Hab ich doch schon gemacht, Fipptehler inklusive? Oder wie würdest du das anderweitig machen?
Die vorgeschlagene Union Variante ist natürlich Groten–schlecht. Auch wenn sie auf den ersten Blick funktioniert, hat es nichts - aber auch gar nichts - mit C Programmierung zu tun. Es wäre nur eine zufällige, vom Compiler geduldete, Ausfuehrung.
Hier noch ein Beispiel, wie die Aufgabenstellung mit der µSer-Bibliothek umgesetzt werden kann:
1 | #include <iostream> |
2 | #include <cstdint> |
3 | #include <uSer/uSer.hh> |
4 | |
5 | struct CFG { |
6 | USER_STRUCT (CFG) |
7 | USER_MEM(std::uint8_t, ENABLE, uSer::Width<1>) |
8 | USER_MEM(std::uint8_t, RESET, uSer::Width<1>) |
9 | USER_MEM(std::uint8_t, NEW, uSer::Width<1>) |
10 | USER_MEM(std::uint8_t, EN, uSer::Width<1>) |
11 | USER_MEM(std::uint8_t, MODE, uSer::Width<3>) |
12 | USER_MEM(std::uint8_t, MODE2, uSer::Width<1>) |
13 | USER_MEM(std::uint8_t, UP, uSer::Width<1>) |
14 | USER_MEM(std::uint8_t, DOWN, uSer::Width<1>) |
15 | USER_MEM(std::uint8_t, DIRECT, uSer::Width<1>) |
16 | USER_MEM(std::uint8_t, STAT, uSer::Width<1>) |
17 | USER_MEM(std::uint8_t, MUX, uSer::Width<1>) |
18 | USER_MEM(std::uint8_t, CHANNEL, uSer::Width<3>) |
19 | |
20 | USER_ENUM_MEM(ENABLE, RESET, NEW, EN, MODE, MODE2, UP, DOWN, DIRECT, STAT, MUX, CHANNEL) |
21 | };
|
22 | |
23 | |
24 | int main () { |
25 | std::uint16_t raw [1]; |
26 | CFG myCfg { 1, 1, 1, 0, 3, 0, 0, 0, 0, 0, 0, 4 }; |
27 | uSer::serialize (raw, myCfg); |
28 | |
29 | std::cout << "0x" << std::hex << std::setw(4) << std::setfill('0') << raw [0] << std::endl; |
30 | }
|
Mehr Schreibarbeit als eine Union, aber dafür portabel und wohldefiniert.
S. R. schrieb: > Hab ich doch schon gemacht, Fipptehler inklusive? Ja, aber da muss ich zum enum noch 2 konvertierungsroutinen schreiben. Statt einmal den Compiler zu konfigurieren. Bei 5 structs vielleicht noch OK. Oder für einen Plattformunabhangigen Treiber. > Oder wie würdest du das anderweitig machen? Ja. Structs + asserts. Zudem ist es überhaupt nicht klar, ob der TO die Struktur überhaupt zu anderen kommuniziert oder nur in einem seriellen eeprom speichert oder dessen Bits manipuliert.
Achim S. schrieb: > Ja, aber da muss ich zum enum noch 2 konvertierungsroutinen schreiben. Das macht man einmal nach Spec und gut ist. Allein die Verifikation des Datenformats mit dem aktuellen Compiler (plus die Asserts) würde vermutlich länger dauern als die beiden Routinen zu schreiben. > Bei 5 structs vielleicht noch OK. > Oder für einen Plattformunabhangigen Treiber. Wenn du 200 solcher Structs hast, dann ist die Lage ein bisschen anders, zugegeben. Allerdings - und damit hab ich oft genug zu tun - hast du dann meist noch ganz andere Probleme. Oder es handelt sich um generierten Code, dann kann man die Routinen auch gleich mit generieren lassen.
Schon 100 mal schrieb: > Es wäre nur eine zufällige, vom Compiler geduldete, Ausfuehrung Naja, den Zufall kann man hier auch sicherstellen. Die vorgeschlagenen Lösungen sind halt alle schwer wartbar, also write only. Für portable Treiber OK, z.b.ein TCP/IP Stack. Für Systeme, die sich laufend weiter entwickeln, sind die Kosten enorm und müssen mit einkalkuliert werden. Und allignment und endianess kommen ja nochmal oben drauf, und n Bits im Byte, ....
Achim S. schrieb: > Naja, den Zufall kann man hier auch sicherstellen. Indem man bloß nie die Plattform/Compiler-Version/Optionen ändert und auch noch in 20 Jahren den gleichen Controller nutzt? Achim S. schrieb: > Und allignment und endianess kommen ja nochmal > oben drauf, und n Bits im Byte, .... Ganz genau das ist das Problem der Union. Genau deshalb sollte man es lieber mit bitweisen Operationen machen. Achim S. schrieb: > Die vorgeschlagenen Lösungen sind halt alle schwer wartbar, also write > only. Ich finde meine Lösung ist hinreichend gut wartbar. Die Syntax ist etwas wortreich, aber es ist simpel, das Format anzupassen.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.