Struct Hilfe

Gast #5501558
Lesenswert?

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
Gast #5501587
Lesenswert?

> 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.
#5501622
Lesenswert?

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.
Gast #5501702
Lesenswert?

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
Gast #5501786
Lesenswert?

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
Gast #5501809
Lesenswert?

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.
Gast #5501874
Lesenswert?

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. ?
Gast #5501907
Lesenswert?

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.
Gast #5501941
Lesenswert?

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.
Persönliche Seite #5502117
Lesenswert?

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.
Gast #5502282
Lesenswert?

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.
#5502309
Lesenswert?

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.
Gast #5502795
Lesenswert?

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, ....
Persönliche Seite #5502816
Lesenswert?

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.

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