Hallo,
ich habe folgende Struct:
struct {
uint8_t bit7:1;
uint8_t bit6:1;
uint8_t bit5:1;
uint8_t bit4:1;
uint8_t bit3:1;
uint8_t bit2:1;
uint8_t bit1:1;
uint8_t bit0:1;
} Input;
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.
Wie muss ich das machen?
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.
Harald K. schrieb:> Wenn man die Struktur "Input" in Deiner Union namenlos lässt, geht's> aber:
Genau so steht es ja auch in dem Beispiel das ich gemacht hatte.
Ich hatte nur das Beispiel von turboj weiter verwendet.
Harald K. schrieb:> Nein, da muss noch ein zusätzliches .Input verwendet werden.
Sollte der TO damit überfordert sein, dann sollte er dringend vorher
(noch)mal ein C/CPP Buch lesen.
Harald K. schrieb:> 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.
N. M. schrieb:> Genau so steht es ja auch in dem Beispiel das ich gemacht hatte.
Nö, ist nicht der Fall. Sieh mal genau hin. Sowohl "Jim" als auch Du
geben der Struktur innerhalb der Union den Namen "Input".
Harald K. schrieb:> Nö, ist nicht der Fall. Sieh mal genau hin. Sowohl "Jim" als auch Du> geben der Struktur innerhalb der Union den Namen "Input".
Wie gesagt ja, da hast du Recht.
Aber das Beispiel macht richtige Zugriffe, übersetzt und macht richtige
Ausgaben.
N. M. schrieb:> Aber das Beispiel macht richtige Zugriffe, übersetzt und macht richtige> Ausgaben.
Ja, aber es muss anders geschrieben werden als das Original.
Bleibt die Struktur namenlos, ist das nicht der Fall.
Dann nämlich kann man schreiben
1
Input_utIn;
2
3
In.bit0=1;
4
5
In.Byte=0xff;
Hat die Struktur einen Namen, muss man das hingegen so schreiben:
N. M. schrieb:> Nur kenne ich die Reihenfolge der Bits andersrum.
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.
Frank O. schrieb:> 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 ...).
Alexander schrieb:> Wilhelm M. schrieb:>> In dem Funktionstemplate bit<>() benutzt man natürlich Shifts statt eine>> union.>> kannst du das näher ausführen ELI5
Templates bekommt man erst mit sechs in der Schule ;-)
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?
Alexander schrieb:> 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.
Ja, wenn Dir das nicht klar ist, wie soll Dir dann geholfen werden.
Beschreibe erstmal klar die Position der Bits in Data!
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)
Wilhelm M. schrieb:> In dem Funktionstemplate bit<>() benutzt man natürlich Shifts statt eine> union.
Alexander schrieb:> 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.
Alexander schrieb:> 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.
Alexander schrieb:> Eine Überladung für das Schreiben, und eine für das Lesen.
Das eine ist ein Kopierzuweisungsoperator, das andere ein
Typumwandlungsoperator: nix Überladung.
Marco H. schrieb:> Ganz einfach, dann sind Bitfelder ungeeignet. Dann verwendet man Masken> und dann gibt es auch keine Überraschungen.
Anbei mal eine Lib für Bitpointer mit Masken aus meinen Anfängen (1995)
mit dem Keil C51.
Harald K. schrieb:> Welches Problem soll hier eigentlich gelöst werden? Woher kommen die> Daten, was soll mit ihnen geschehen?
Das weiß der TO ja auch nicht so genau.
Jedenfalls konnte er es auf Nachfrage nicht erläutern, und hofft nun,
dass wir dieses Rätsel für ihn lösen ;-)
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
Wilhelm M. schrieb:> 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.
Wilhelm M. schrieb:> var0 = Byte0> var1 = [high = Byte2, low = Byte1];> var3 = [high = Byte4, low = Byte3];> var5 = Byte5
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.
Frank M. schrieb:> Wenn Du den Bits auch noch Namen gibst, wird es noch klarer. Hier ein> Beispiel:>
1
>#defineLED_MASK_RED(1<<2)// Maske fuer rote LED, an Pin#2
2
>#defineLED_MASK_GREEN(1<<3)// Maske fuer gruene LED, an Pin#3
3
>
Mir ist noch nicht ganz klar wie ich das in die Structs reinkriege. Ich
brauche ja eine Gruppierung.
Wilhelm M. schrieb:> 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.
Wilhelm M. schrieb:> 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.
Harald K. schrieb:> 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.
Hier sind vier Beispiele für vier verschiedene CAN Messages, mit
unterschiedlichen Längen.
Beitrag "Re: Hilfe um 8 Bitfelder in uint8_t umzuwandeln (ESP32)"
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.
Alexander schrieb:> Wilhelm M. schrieb:>> 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.
> Wilhelm M. schrieb:>> var0 = Byte0>> var1 = [high = Byte2, low = Byte1];>> var3 = [high = Byte4, low = Byte3];>> var5 = Byte5>> 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.
> Wilhelm M. schrieb:>> 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).
> Wilhelm M. schrieb:>> 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.
Ja, machst es nicht besser.
> Harald K. schrieb:>> 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ß?
Alexander schrieb:> Im Gegensatz zum TE der nur lesen möchte, möchte ich Data in ein Struct> schreiben.
Ups, das Du nicht der TE bist, ist mir noch gar nicht aufgefallen ...
Wilhelm M. schrieb:> Geht es um die Payload eines CAN-BUS Pakets? Dann beschreibe doch mal> den Aufbau.
Ja. Es gibt nicht "den" Aufbau. Vier Beispiele habe ich gezeigt. Hier
geht es aber nur darum, auf alle Bits eines Bitfelds gleichzeitig
zuzugreifen.
Wilhelm M. schrieb:> Dann hast Du noch nie in C++ programmiert.
richtig. Ich nutze Arduino Libraries.
Wilhelm M. schrieb:> Ja, je nach CAN-ID und unterschiedlicher PayLoad nimmst Du eine andere> Klasse.
Könnte schwierig werden in reinem C. aktuell nutze ich Strukturen.
Wilhelm M. schrieb:> Welche Art von PDL ist das denn?
keine.
Wilhelm M. schrieb:> Oder machst Du das zu Fuß?
Ja. mit shell script. oder Python.
Alexander schrieb:> 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:
Das obiger soll nur das Prinzip zeigen. Ob du das dispatch in der
Funktion oder außerhalb machst, musst Du entscheiden, wo es sinnvoller
ist.
Natürlich rate ich Dir, statt reines C ein rein-prozedurales C++ mit den
C++-Goodies zu verwenden: also, statt rohes C-Arrays etwa std::array<>,
...
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:
Alexander schrieb:> 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, ...
Alexander schrieb:> Der Kreis schließt sich hier. Weiß nur nicht wie man das in ein Struct> packt. Dachte Dein "Funktionstemplate bit<>()" wäre die Lösung. höhere> C++ Kunst und so..> Beitrag "Re: Hilfe um 8 Bitfelder in uint8_t umzuwandeln (ESP32)"
Aber auch dafür musst Du wissen, wohin(!) jedes Bit abgebildet werden
soll. Dieses Wissen scheinst Du aber nicht zu haben.
Wilhelm M. schrieb:> 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?
Wilhelm M. schrieb:> 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.
Alexander schrieb:> 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.
Klaus H. schrieb:> Wilhelm M. schrieb:>> In dem Funktionstemplate bit<>() benutzt man natürlich Shifts statt eine>> union.>> Kurze Frage: Ich kenn nur std::bitset<N> aus dem <bitset> Header.
Ist unbenutzbar für diesen Zweck, weil keine Garantien über das Layout
möglich sind.
> Dort gibt es IMHO aber kein bit<x>(v) sonder Zugriff Über v[x].> Welches Modul meinst Du?
Gar kein Modul aus der stdlibc++.
Ich hatte mir in etwa folgendes vorgestellt:
Wilhelm M. schrieb:> 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.
Alexander schrieb:> Input4& operator==(const uint16_t value) {> var5 = (rest4 << 8) | byte5; // <-- SOLL HIER HIN> return *this;> }
Das ist kein Zuweisungsoperator, sondern ein Vergleichsoperator.
Mir scheint, Du versuchst mit try-n-error zum Ziel zu kommen, aber das
wird Dir nicht gelingen.
Und nochmals: das Du ja C++ programmierst ("Arduino" ist C++), vergiss
den Ansatz über die union, das ist UB (undefined behaviour).
Versuche:
Wilhelm M. schrieb:> Das ist kein Zuweisungsoperator, sondern ein Vergleichsoperator.
yep. der ist zum Lesen, nicht zum Schreiben. siehe hier.
1
if(Input4.var5==2047){
Wilhelm M. schrieb:> Mir scheint, Du versuchst mit try-n-error zum Ziel zu kommen, aber das> wird Dir nicht gelingen.
Was wäre die Alternative? Ein Buch lesen? Im Forum fragen?
Alexander schrieb:> Wilhelm M. schrieb:>> Das ist kein Zuweisungsoperator, sondern ein Vergleichsoperator.>> yep. der ist zum Lesen, nicht zum Schreiben. siehe hier.>
1
if(Input4.var5==2047){
Nö.
> Wilhelm M. schrieb:>> Mir scheint, Du versuchst mit try-n-error zum Ziel zu kommen, aber das>> wird Dir nicht gelingen.>> Was wäre die Alternative? Ein Buch lesen? Im Forum fragen?
Dir die Grundlagen von C++ aneignen. Buch macht kluch.
Alexander schrieb:> Und was UB heißt weiß ich nicht, ...
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.
Joe L. schrieb:> 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.
>// ... 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.
Das nennt man dann Konstruktor ;-)
Etwas besser wäre dann noch:
> Joe L. schrieb:>> 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.
Besser ist es: s.o. union und UB.
Arm schrieb:> 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.
Alexander schrieb:> Joe L. schrieb:>> 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 [...]"
Joe L. schrieb:> Lies selber
Danke für's googlen. Mit 42 kann ich leben.
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.
Alexander schrieb:> but for maximum portability you should aim> to keep your programs UB-free.
Wenn maximale Portabilität nicht das primäre Ziel ist ... kann sich die
Perspektive ändern.