Hierüber kann ich ja auf jedes einzelne Bit mit Beispiel:
if (Input.bit0==1) zugreifen, soweit klar.
Jetzt möchte ich aber z.B. alle 8 Bits gleichzeitig aus einer uint8_t lesen, wie mache ich das?
uint_8 Data = 0b10101010;
Input = Data; funktioniert so natürlich nicht.
Da im Betreff steht ESP32 versuche __attribute__((__packed__)) gegen das Padding und dann memcpy oder byte-ptr. Ist zwar verboten aber wenn es nur für Dich ist, geht's.
Wenn man die Struktur "Input" in Deiner Union /namenlos/ lässt, geht's
aber
Und wenn er der struct noch einen Konstruktor verpasst kann er die Struktur auch gleich noch initialisieren.
Er verwendet ja sowieso einen CPP Compiler.
Das darf der Compilerprogrammierer machen, wie er Bock hat oder wie er es für einfacher hält. Das ist gemeint, wenn hier jemand von undefiniertem Verhalten schreibt (undefined behavior = UB). Man kann sich noch nichtmal beschweren, wenn er seine Meinung von Compilerversion zu Compilerversion ändert. Wer den Compiler oder gar noch den Zielprozessor wechselt hat noch größere Chancen, dass Unerwartetes geschieht. Wer will, darf die Mitglieder der Struktur auch in der .h(pp)-Datei anders anordnen als in der .c(pp)-Datei, oder von 8 auf 40 Mitglieder aufstocken. Irgendwer gräbt vielleicht noch einen Compiler aus, der die Namen der Mitglieder nach dem Alphabet anordnet oder in der Reihenfolge des ersten Lesezugriffs, selbstverständlich abhängig von der Reihenfolge, in der die Dateien übersetzt werden.
Langer Rede kurzer Sinn : verlasse Dich niemals darauf, wie ein Compiler etwas hinter den Kulissen umsetzt.
1
Input.bit0=(Data>>0U)&1U;
2
Input.bit1=(Data>>1U)&1U;
3
Input.bit2=(Data>>2U)&1U;
4
Input.bit3=(Data>>3U)&1U;
5
Input.bit4=(Data>>4U)&1U;
6
Input.bit5=(Data>>5U)&1U;
7
Input.bit6=(Data>>6U)&1U;
8
Input.bit7=(Data>>7U)&1U;
Und zurück :
1
Data=((uint8_t)(Input.bit0)<<0U)|
2
((uint8_t)(Input.bit1)<<1U)|
3
((uint8_t)(Input.bit2)<<2U)|
4
((uint8_t)(Input.bit3)<<3U)|
5
((uint8_t)(Input.bit4)<<4U)|
6
((uint8_t)(Input.bit5)<<5U)|
7
((uint8_t)(Input.bit6)<<6U)|
8
((uint8_t)(Input.bit7)<<7U);
Das mit dem "Funktionstemplate bit<>()" ist noch besser, aber halt auch höhere C++ Kunst und sollte, wie Wilhelm M. (wimalopaan) schrieb, intern so wie die Beispiele hier funktionieren.
Das darf der Compilerprogrammierer machen, wie er Bock hat oder wie er
es für einfacher hält. Das ist gemeint, wenn hier jemand von
undefiniertem Verhalten schreibt (undefined behavior = UB).
Das ist "nur" implementation-defined ;-) Machts aber auch nicht besser.
Was in C++ UB ist, ist der Zugriff auf ein non-active-member der union. In C ist diese Art von type-punning explizit erlaubt. In C++ geht das wegen ggf. DT mit nicht-trivialen Konstruktoren / Destruktoren nicht (ohne weiteres, möglich ist es schon ...).
Ich hab sehr viele structs die ich füllen muss, deswegen habe ich memcpy() genutzt. funktioniert mit __attribute__((_packed_)) auch ganz gut. Allerdings habe ich ein Logikproblem, habe nun festgestellt dass es bei krummen Datentypen (ungerade Bitzahl) doch nicht so funktioniert wie ich dachte.
funktioniert (das __attribute__((_packed_)) bitte dazu denken)
1
struct{
2
boolbit7:1;// OFFSET 7, LENGTH 1
3
boolbit6:1;// OFFSET 6, LENGTH 1
4
boolbit5:1;// OFFSET 5, LENGTH 1
5
boolbit4:1;// OFFSET 4, LENGTH 1
6
boolbit3:1;// OFFSET 3, LENGTH 1
7
boolbit2:1;// OFFSET 2, LENGTH 1
8
boolbit1:1;// OFFSET 1, LENGTH 1
9
boolbit0:1;// OFFSET 0, LENGTH 1
10
}Input;
11
12
memcpy(&Input,Data,len);
funktioniert auch, wenn man das struct byteweise aufteilt
1
struct{
2
// byte 1
3
boolbit7:1;// OFFSET 7, LENGTH 1
4
boolbit6:1;// OFFSET 6, LENGTH 1
5
boolbit5:1;// OFFSET 5, LENGTH 1
6
boolbit4:1;// OFFSET 4, LENGTH 1
7
boolbit3:1;// OFFSET 3, LENGTH 1
8
boolbit2:1;// OFFSET 2, LENGTH 1
9
boolbit1:1;// OFFSET 1, LENGTH 1
10
boolbit0:1;// OFFSET 0, LENGTH 1
11
// bytes 2, 3, 4
12
uint8_tvar1:8;// OFFSET 8, LENGTH 8
13
uint8_tvar2:8;// OFFSET 16, LENGTH 8
14
uint8_tvar3:8;// OFFSET 24, LENGTH 8
15
// byte 5
16
uint8_tvar4:3;// OFFSET 37, LENGTH 3
17
boolbit36:1;// OFFSET 36, LENGTH 1
18
boolbit35:1;// OFFSET 35, LENGTH 1
19
boolbit34:1;// OFFSET 34, LENGTH 1
20
boolbit33:1;// OFFSET 33, LENGTH 1
21
bool:1;
22
// byte 6
23
uint8_tvar5:8;// OFFSET 40, LENGTH 8
24
// byte 7
25
uint8_tvar66:2;// OFFSET 54, LENGTH 2
26
uint8_tvar64:2;// OFFSET 52, LENGTH 2
27
uint8_tvar62:2;// OFFSET 50, LENGTH 2
28
bool:1;
29
boolbit48:1;// OFFSET 48, LENGTH 1
30
// byte 8
31
uint8_tvar7:8;// OFFSET 56, LENGTH 8
32
}Input2;
33
34
memcpy(&Input2,Data,len);
funktioniert bedingt, aber var1 + var3 benötigen nachher noch Konvertierung little endian <-> big endian. lässt sich lösen, kein Problem.
1
struct{
2
uint8_tvar0:8;// OFFSET 0, LENGTH 8
3
uint16_tvar1:16;// OFFSET 8, LENGTH 16
4
uint16_tvar3:16;// OFFSET 24, LENGTH 16
5
uint8_tvar5:8;// OFFSET 40, LENGTH 8
6
}Input3;
7
8
memcpy(&Input3,Data,len);
aber
1
struct{
2
// byte 1
3
uint8_t:3;
4
boolbit4:1;// OFFSET 4, LENGTH 1
5
boolbit3:1;// OFFSET 3, LENGTH 1
6
boolbit2:1;// OFFSET 2, LENGTH 1
7
boolbit1:1;// OFFSET 1, LENGTH 1
8
boolbit0:1;// OFFSET 0, LENGTH 1
9
// byte 2
10
uint8_t:3;
11
boolbit12:1;// OFFSET 12, LENGTH 1
12
boolbit11:1;// OFFSET 11, LENGTH 1
13
boolbit10:1;// OFFSET 10, LENGTH 1
14
boolbit9:1;// OFFSET 9, LENGTH 1
15
boolbit8:1;// OFFSET 8, LENGTH 1
16
// byte 3
17
boolbit23:1;// OFFSET 23, LENGTH 1
18
boolbit22:1;// OFFSET 22, LENGTH 1
19
uint8_t:6;
20
// byte 4
21
boolbit28:1;// OFFSET 28, LENGTH 1
22
boolbit27:1;// OFFSET 27, LENGTH 1
23
boolbit26:1;// OFFSET 26, LENGTH 1
24
boolbit25:1;// OFFSET 25, LENGTH 1
25
boolbit24:1;// OFFSET 24, LENGTH 1
26
// byte 4 (rest) + byte 5
27
uint16_tvar5:11;// OFFSET 29, LENGTH 11
28
}Input4;
29
30
memcpy(&Input4,Data,len);
funktioniert nicht. für bit24 ... bit28 oder var5 völlig falsche Bits. die bools sind versetzt, lässt sich lösen. aber keinen blassen Schimmer wie man var5 füllt (und endianess korrigiert). Ich brauche aber die structs für den Zugriff = Input4.var5
Außerdem sind die structs autogeneriert, daher müsste ich später ein neues Script schreiben. Aber erstmal brauche ich die Logik, selbst wenn ich es mit Bitmasken und Verschiebung machen würde ist es mir nicht klar welche Bits wohin kommen. Kann jemand das Muster erkennen?
Ich habe immer nur StructName + MemberName + MemberOffset + MemberLänge in Textform gegeben, keine konkreten Infos über den Datentyp. Das können ganze Bytes sein oder Bitfelder. Sollte doch möglich sein anhand der ersten drei Beispiele die Logik für das vierte abzuleiten. Das Muster ist erkennbar, an den krummen Datentypen scheitert die Anwendung. Die Endianess könnte auch andersrum sein, war nur geraten.
Ich habe jetzt var5 geteilt, damit werden die Bits korrekt zugeordnet und die gleiche Logik angewendet wie in den anderen drei Beispielen.
Nur wusste ich nicht wie man den Split synchronisieren kann. Ich habe mich bei ChatGPT angemeldet, heraus kam das.
Eine Überladung für das Schreiben, und eine für das Lesen.
Nun könnte ich ja aber mehrere uint16_t in dem Struct haben, und ChatGPT konnte mir nicht die Frage beantworten wie ich die zweite Überladung spezifisch für var5 definieren kann.
1
// byte 4
2
uint8_trest4:3;
3
boolbit28:1;// OFFSET 28, LENGTH 1
4
boolbit27:1;// OFFSET 27, LENGTH 1
5
boolbit26:1;// OFFSET 26, LENGTH 1
6
boolbit25:1;// OFFSET 25, LENGTH 1
7
boolbit24:1;// OFFSET 24, LENGTH 1
8
9
uint8_tbyte5:8;
10
11
// byte 4 (rest) + byte 5
12
uint16_tvar5:11;// OFFSET 29, LENGTH 11
13
14
// Überladung des Zuweisungsoperators "=" für var5
15
Input4&operator=(constuint16_tvalue){
16
var5=value;
17
rest4=(value>>8)&0b00000111;
18
byte5=value&0b11111111;
19
return*this;
20
}
21
// Überladung des Type Casting Operators für uint16_t
22
operatoruint16_t()const{
23
var5=(rest4<<8)|byte5;
24
}
25
}Input4;
Könnte man einfach eine weitere Zeile einfügen var7 = (rest6 << 8) | byte7; und was nehme ich dann als Return-Wert?
P.S. und bevor die Frage kommt, das brauche ich dann zum Schluss auch noch (oder ein union)
Nun könnte ich ja aber mehrere uint16_t in dem Struct haben, und ChatGPT
konnte mir nicht die Frage beantworten wie ich die zweite Überladung
spezifisch für var5 definieren kann.
Zwar weiß ich immer noch überhaupt nicht was Du erreichen willst bzw. welche Bytes wohin sollen (s.o. meine Frage: Du meintest, man müsste ein Muster erkennen ...).
Jedoch: überladen kann man nur, wenn die Signatur der Funktion (Name und Parameterliste) sich unterscheidet, Rückgabetyp der Funktion gehört (bei C++) nicht zur Signatur. Warum Du allerdings den Kopierzuweisungsoperator in der Art überladen willst, ist mir schleierhaft.
Die Bytes werden ja eine Bedeutung haben, daher wären geeignet benannte non-const Funktionen (aka: Mutatoren) besser:
1
structInput{
2
auto&foo(constuint16_tv){...}
3
auto&bar(constuint16_tv){...}
4
};
5
...
6
Inputi;
7
i.foo(42).bar(13);// <1>
statt
1
i=42;// <2>
Denn bei <1> erkennt man sofort, was los ist, während man bei <2> rätselt oder eben das falsche vermutet.
P.S. und bevor die Frage kommt, das brauche ich dann zum Schluss auch
noch (oder ein union)
Wilhelm M. schrieb:
In dem Funktionstemplate bit<>() benutzt man natürlich Shifts statt eine
union.
Wie schon gesagt: Du möchtest über das eine Element der union einen Wert zuweisen. Dies ist dann das sog. "active-member". Dann möchtest Du von einem anderen Element lesen, was nicht das "active-member" ist: das ist in i.a. C++ UB (in C hingegen explizit erlaubt: type-punning).
Den Rest Deines Beitrages verstehe ich leider wieder gar nicht.
Interessant, was hier für ein Aufwand getrieben wird, um Bitfelder zu retten.
@TO:
Schmink Dir die Bitfelder einfach ab. Das Zeugs ist höchst unportabel. Es gab vielleicht in den 70er Jahren mal einen Grund dafür, nämlich für schlecht optimierende C-Compiler höchst effizienten Code zu schreiben. Diese Zeiten sind aber längst vorbei.
Verwende von vornherein ein Byte und maskiere die gewünschten Bits, die Du brauchst. Das ist trivial und funktioniert auf jeder Plattform gleich. Ein C-Compiler wird Deine Masken-Operationen analog zu Bitfeldern umsetzen. Dabei entsteht im Vergleich zu Bitfeldern keine Performance-Einbuße. Gleichzeitig hast Du aber die Garantie der Portabilität.
Beispiel:
1
uint8_tvalue=0;
2
3
value|=(1<<3);// Bit 3 setzen
4
value&=~(1<<3);// Bit 3 löschen
5
if(value&(1<<3))// Auf Bit 3 testen
Wenn Du den Bits auch noch Namen gibst, wird es noch klarer. Hier ein Beispiel:
1
#define LED_MASK_RED (1<<2) // Maske fuer rote LED, an Pin#2
2
#define LED_MASK_GREEN (1<<3) // Maske fuer gruene LED, an Pin#3
Zwar weiß ich immer noch überhaupt nicht was Du erreichen willst bzw.
welche Bytes wohin sollen (s.o. meine Frage: Du meintest, man müsste ein
Muster erkennen ...).
Im Gegensatz zum TE der nur lesen möchte, möchte ich Data in ein Struct schreiben. Aktuell klappt das ganz gut mit memcpy() aber da kannte ich Union noch nicht.
Das Muster hast Du ja schon genannt, nur dass es eben nicht immer ganze Bytes sind.
Manchmal hat eine Variable eine krumme Anzahl Bits. Das habe ich nun so gelöst, indem ich die Variable in zwei 8 Bit Variablen gesplittet habe. Am Ende des Structs habe ich nun eine zusätzliche Variable eingefügt.
Warum Du allerdings den Kopierzuweisungsoperator in der Art überladen willst,
ist mir schleierhaft.
Ich habe noch nie zuvor von einer Überladung gehört und musste ChatGPT auch erst mal befragen. Die zweite Überladung würde angeblich jedesmal ausgeführt wenn ich value = Input4.var5; irgendwo auslese, da beim Lesen angeblich intern immer ein Typecast ausgeführt würde. Die erste Überladung würde bei jedem Schreiben ausgeführt, Input4.var5 = 0b11111111111; so dass im Endeffekt immer mit Input4.rest4 = 0b111; Input4.byte5 = 0b11111111; in beide Richtungen synchronisiert würde.
Von außen sollte sich das ganze wie ein normales Struct verhalten, wobei mich außen immer nur Input4.var5 interessiert, und innen immer nur Input4.rest4 + Input4.byte5. Input4.var5 steht aber zusätzlich am Ende des Structs und ist kein Teil von Data. Getestet habe ich noch nicht.
Den Rest Deines Beitrages verstehe ich leider wieder gar nicht.
Ich werde nun wieder ChatGPT befragen da ich Deinen Vorschlag mit Mutatoren auch nicht verstehe (trotz Beispielcode) aber vielleicht ist das ja das entscheidende Keyword. ChatGPT wird ja immer gelobt es könne gut mit Programmiersprachen umgehen, ist eine Verzweiflungstat.
Welches Problem soll hier eigentlich gelöst werden? Woher kommen die
Daten, was soll mit ihnen geschehen?
Die Daten kommen vom CAN-Bus eines Fahrzeuges. Data ist immer die CAN Message, jeweils mit unterschiedlichen Längen. Je nach CAN-ID soll Data in ein anderes Struct kopiert werden. Das ganze Arduino-Nutzer-freundlich, und bidirektional.
Der Clou soll sein, dass man sich überhaupt nicht um CAN Message und Aufbau kümmern muss, und auch nicht wie man ein Struct füllt. Es wird einfach die Header-Datei eingebunden und ein Struct mit memcpy() gefüllt (oder mit Zuweisung Input = Data; wenn das geht)
Evtl. könnte ich das auch noch vereinfachen, indem ich gleich die CAN Message komplett als erstes Member in das Struct mit aufnehme. Und die Verteilung der Bitfelder erfolgt dann komplett mit Shifts und Maskierung innerhalb des Structs, da könnte ich auf memcpy() verzichten. Wichtig bleibt am Ende der Zugriff über die Klarnamen der Bits Input.LED_GREEN.
Das ganze ist aber nur ein Teilproblem, da die Structs selbst auch nur generiert sind. Ich arbeite an einem Script dass die Stucts erstellt. Ich bin mir nicht sicher ob es zielführend wäre hier so tief in das Problem einzusteigen.
Bisher hat es so gut funktioniert, nun brauche ich aber das erste mal einen dieser krummen Datentypen (ungerade Bitzahl) da ist mir der Fehler erst aufgefallen.
Zwar weiß ich immer noch überhaupt nicht was Du erreichen willst bzw.
welche Bytes wohin sollen (s.o. meine Frage: Du meintest, man müsste ein
Muster erkennen ...).
Das Muster hast Du ja schon genannt, nur dass es eben nicht immer ganze
Bytes sind.
Geht es um die Payload eines CAN-BUS Pakets? Dann beschreibe doch mal den Aufbau.
Manchmal hat eine Variable eine krumme Anzahl Bits. Das habe ich nun so
gelöst, indem ich die Variable in zwei 8 Bit Variablen gesplittet habe.
Am Ende des Structs habe ich nun eine zusätzliche Variable eingefügt.
Wie Du erkannt hast, ist das Hantieren mit einer "krummen" Anzahl von Bits unpraktisch. Also schreibt man sich ein (oder mehrere Klassen), an die man die jeweils relevanten Daten zum "Auseinandernehmen" mit Bit-Shift oder einfachem Maskieren übergibt.
Warum Du allerdings den Kopierzuweisungsoperator in der Art überladen willst,
ist mir schleierhaft.
Ich habe noch nie zuvor von einer Überladung gehört und musste ChatGPT
auch erst mal befragen.
Dann hast Du noch nie in C++ programmiert.
Dann ist aber dieses Projekt kein guter Startpunkt, damit anzufangen.
Bleibe bei reinem C (egal ob als C oder C++-Code).
Das spielt aber auch gar keine Rolle: statt Elementfunktionen aufzurufen, schreibst Du einfach weiterhin (freie) Funktionen und übergibst die Objekte (ggf. dann als Output-Parameter).
Den Rest Deines Beitrages verstehe ich leider wieder gar nicht.
Ich werde nun wieder ChatGPT befragen da ich Deinen Vorschlag mit
Mutatoren auch nicht verstehe (trotz Beispielcode) aber vielleicht ist
das ja das entscheidende Keyword.
Non-const Elementfunktionen (aka Mutatoren) sind im Gegensatz zu const-Elementfunktionen welche, die den Objektzustand ändern können.
Aber wie gesagt: die Frage zeigt auch wieder, dass Du keine Ahnung von C++ hast. Deswegen bleibe bei C (s.o.).
ChatGPT wird ja immer gelobt es könne
gut mit Programmiersprachen umgehen, ist eine Verzweiflungstat.
Welches Problem soll hier eigentlich gelöst werden? Woher kommen die
Daten, was soll mit ihnen geschehen?
Die Daten kommen vom CAN-Bus eines Fahrzeuges. Data ist immer die CAN
Message, jeweils mit unterschiedlichen Längen. Je nach CAN-ID soll Data
in ein anderes Struct kopiert werden. Das ganze
Arduino-Nutzer-freundlich, und bidirektional.
Nochmal: geht es um die Payload oder um das gesamte Paket? Hört sich so an, als hättest Du keine CAN-Bus-Controller.
Der Clou soll sein, dass man sich überhaupt nicht um CAN Message und
Aufbau kümmern muss, und auch nicht wie man ein Struct füllt.
Ja, je nach CAN-ID und unterschiedlciher PayLoad nimmst Du eine andere Klasse.
Das ganze ist aber nur ein Teilproblem, da die Structs selbst auch nur
generiert sind. Ich arbeite an einem Script dass die Stucts erstellt.
Ich bin mir nicht sicher ob es zielführend wäre hier so tief in das
Problem einzusteigen.
Welche Art von PDL ist das denn? Oder machst Du das zu Fuß?
Ja. Es gibt nicht "den" Aufbau. Vier Beispiele habe ich gezeigt. Hier
geht es aber nur darum, auf alle Bits eines Bitfelds gleichzeitig
zuzugreifen.
Du hast die Payload als Array von Bytes (uint8_t) nehme ich an.
In C++ würde man nun das ganze Array einem Konstruktor einer Klasse übergeben, und der Konstruktor (oder eine andere Elementfunktion) popelt aus den vielen Bytes der Payload durch Shifts und Maskieren und ggf. wieder zu größeren Datentypen wie Zusammenfügen die einzelnen Elemente heraus.
Das kann man natürlich auch in C durch eine Initialisierungsfunktion machen:
Eine Initialisierungsfunktion brauche ich schon noch, nur soll diese nicht viel machen. Das war ja der Sinn der Structs, die Arbeit abzunehmen. Da soll nur rein:
Eine Initialisierungsfunktion brauche ich schon noch, nur soll diese
nicht viel machen. Das war ja der Sinn der Structs, die Arbeit
abzunehmen. Da soll nur rein:
1
>switch(id){
2
>caseCANID:
3
>// memcpy(&Input, Data, len);
4
>Input=Data;// mit Union?
5
>break;
6
>}
7
>
Wir drehen uns im Kreis.
Ja sicher, das geht in C.
Es ist aber gehupft wie gesprungen - und es bleibt nicht portabel. Besser mit Shifts, ...
In dem Funktionstemplate bit<>() benutzt man natürlich Shifts statt eine
union.
Kurze Frage: Ich kenn nur std::bitset<N> aus dem <bitset> Header.
Dort gibt es IMHO aber kein bit<x>(v) sonder Zugriff Über v[x].
Welches Modul meinst Du?
Aber auch dafür musst Du wissen, wohin(!) jedes Bit abgebildet werden
soll. Dieses Wissen scheinst Du aber nicht zu haben.
Doch, habe ich. Ich möchte nur wissen wie ich es umsetze. Die Überladung funktioniert nicht (wird nicht wie behauptet ausgeführt). Wenn ich es in die Initialisierungsfunktion packe funktioniert es. Ich will aber eine Funktion im Struct.
Die Überladung
funktioniert nicht (wird nicht wie behauptet ausgeführt). Wenn ich es in
die Initialisierungsfunktion packe funktioniert es.
Du meinst, einen Kontruktor? Oder eine freie Funktion? Beides geht mit Shifts. Wie schon mehrfach gesagt, ist das Verwenden von unions wie Du es willst in C++ UB.
Ich will aber eine
Funktion im Struct.
Du bist bemerkenswert beratungsresistent: wenn Du damit meinst, dass Du in C++ eine Elementfunktion benutzen möchtest zusammen mit Deinem union Ansatz: es ist UB!
Ich hatte Dir oben einen Ansatz mit Shift, Mask, Compose ausformuliert: benutze den.
Ich hatte Dir oben einen Ansatz mit Shift, Mask, Compose ausformuliert:
benutze den.
Meinst du deine Funktion initData()? Davon rede ich doch. Wie? Die ist doch in main() manuell ausgeführt. Ich brauche es aber automatisch, und die Funktion soll in das Struct verschoben werden.
So funktioniert es derzeit. Logik gefunden (BIG ENDIAN)
Input4.var5=(Input4.rest4<<8)|Input4.byte5;// <-- DAS
20
break;
21
}
22
}
23
voidloop(){
24
initData(msg.id,msg.buf,msg.len);
25
if(Input4.var5==2047){
26
...
27
}
28
Input4.var5=1024;
29
Input4.rest4=(Input4.var5>>8)&0b00000111;// <--
30
Input4.byte5=Input4.var5&0b11111111;
31
}
So soll es werden. Auf das Union kann ich auch verzichten, aber dann brauche ich memcpy() oder ich nehme Data komplett in jedes Struct mit auf. Ob es nun eine Klasse oder Struct wird, Elementfunktion, Kontruktor oder Mutator heißt ist mir egal, Hautpsache die Synchronisation wird automatisch ausgeführt. Und was UB heißt weiß ich nicht, das ist alles fachchinesisch. Es muss nicht portabel sein.
Was die Abbreviatur "UB" genau heißt -- das musst Du unstreitig nicht wissen. Vereinfacht gesagt hat das was mit 'Lernresistenz' zu tun. Näheres hierzu siehe https://www.stupidedia.org/stupi/Lernresistenz ...
// ... hier mit Shift, Mask, ... aus den Bytes von msg die Werte
5
herausholen
6
}
7
private:
8
uint8_tvar1;
9
uint16_tvar2;
10
};
Das sieht doch gut aus. Dann kann ich das memcpy ersetzen mit einer Zeile wo ich das aufrufe. Nur brauche ich es bidirektional. Aber so komme ich erstmal weiter, danke.
Was die Abbreviatur "UB" genau heißt -- das musst Du unstreitig nicht
wissen. Vereinfacht gesagt hat das was mit 'Lernresistenz' zu tun.
Näheres hierzu siehe https://www.stupidedia.org/stupi/Lernresistenz ...
Und was heißt UB (undefined behaviour) nun? Dass es auf meinem Arduino Teensy oder ESP32 funktioniert, aber auf deinem Compiler nicht? Spielt wie gesagt keine Rolle, da es für Arduino ist. Ich schrieb, es muss nicht portabel sein.
Die Bitfelder hatte ich übrigens ursprünglich doppelt generieren lassen, habe ich dann aber wegoptimiert da nicht benötigt.
Was die Abbreviatur "UB" genau heißt -- das musst Du unstreitig nicht
wissen. Vereinfacht gesagt hat das was mit 'Lernresistenz' zu tun.
Näheres hierzu siehe https://www.stupidedia.org/stupi/Lernresistenz ...
Und was heißt UB (undefined behaviour) nun?
Ist Google mal wieder kaputt?
Dass es auf meinem Arduino
Teensy oder ESP32 funktioniert, aber auf deinem Compiler nicht? Spielt
wie gesagt keine Rolle, da es für Arduino ist. Ich schrieb, es muss
nicht portabel sein.
Das es nirgendwo eine Garantie gibt, dass es funktioniert.
Die Bitfelder hatte ich übrigens ursprünglich doppelt generieren lassen,
habe ich dann aber wegoptimiert da nicht benötigt.
In C++20 ist std::bit_cast constexpr, damit kann man per static_assert
prüfen, ob die bits in einem bitfield so angelegt sind, wie man es
erwartet.
Das ist zwar richtig und sinnvoll, jedoch beweifel ich auch hier, dass der TO überhaupt etwas damit anfangen kann, oder weiß, was ein constexpr-Kontext ist. Und streng genommen müsste er sich die Arbeit dann zweimal machen ...
Das ist nur PseudoCode- aber wenn man es noch vereinfachen kann dann bitte her damit. message_t readMsg() sendMsg() sind Vorgabe aus einer Library, da kann ich nichts ändern.
Was die Abbreviatur "UB" genau heißt -- das musst Du unstreitig nicht
wissen. Vereinfacht gesagt hat das was mit 'Lernresistenz' zu tun.
Näheres hierzu siehe https://www.stupidedia.org/stupi/Lernresistenz ...
Und was heißt UB (undefined behaviour) nun? Dass es auf meinem Arduino
Teensy oder ESP32 funktioniert, aber auf deinem Compiler nicht?
Das heißt, dass Du dich aufführst wie ein Vollpfosten!
Lies selber (https://64.github.io/cpp-faq/undefined-behaviour/):
"Undefined Behaviour (also known as UB) occurs when you violate certain language rules. [...] When undefined behaviour occurs, the C and C++ standards do not place any restrictions on what your program might do. In other words, your program may crash, or continue execution, or call some seemingly unrelated piece of code, or print 42 and open xkcd in your web browser [...]"
In practice, you may be able to reason about how your compiler will respond to UB, and in some cases compilers will guarantee that certain operations are well-defined, but for maximum portability you should aim to keep your programs UB-free.