Forum: PC-Programmierung c++ Datenströme


von typen (Gast)


Lesenswert?

Hallo,

mir ist schon öfter aufgefallen das ich so meine liebe Mühe mit dem 
umwandeln von verschiedenen Datentypen habe wenn es darum geht Daten zu 
übertragen.

Als Beispiel eine SPI Kommunikation. Die Funktion zum senden und 
empfangen von Daten erwartet ein char array. In diesem array habe ich 2 
Byte. Die Funktion sendet und ich bekomme auch einzelne Bytes zurück. In 
meinem Fall kommen 8 Bytes zurück die ebenfalls in einem char array 
landen.

Jetzt kommt mein Problem. Jetzt muss ich diese Bytes irgendwie wieder 
zusammen bringen. Zum Debuggen würde ich diese gerne als binäre Kette 
aus Nullen und Einsen sehen. Habe ich mein Debugging fertig und ich 
bekomme die richtigen Werte binär gesendet muss ich aus den einzelnen 
Bytes ein Double machen um damit weiter zu arbeiten.

Doch wie ist dafür der richtige Weg?

von Noch einer (Gast)


Lesenswert?

Da gibt keinen richtigen Weg.

Das erste Problem ist schon mal, du kannst dich nicht darauf verlassen, 
dass der Chip die Double genau so kodiert wie dein Prozessor. Dann diese 
kleinen Gemeinheiten. z.B. Bytearray darf an ungeraden Adressen stehen, 
Double nur an geraden.

Da verwendet man unterschiedliche Lösungen.
Wenn die Interna zusammen passen, nimmst du eine Union.
Du bastelst die Zahl mit <<, | und Unsigned-Casts zusammen.
Du nimmst eine Library wie ASN.1 .

Alles nicht so das, was man als gute Lösung bezeichnen könnte.

Beitrag #5258534 wurde von einem Moderator gelöscht.
Beitrag #5258535 wurde von einem Moderator gelöscht.
von Luftikus (Gast)


Lesenswert?

Stephano B. schrieb Unfug im Beitrag #5258534:
>

Dich gibts ja immernoch? Ich dachte du wärst endlich mal gebannt
nach deiner Aktion heute morgen

@topic:

Der Standardweg läuft über Structs. Du definierst dir deine
Nachricht, welche auch komplexer sein kann, füllst diese
mit den Übertragenen Daten und liest aus dieser aus.

z.b.:
1
struct Message
2
{
3
    double messdaten;
4
    // hier beliebig viele Einträge
5
    uint8_t channel;
6
    float temperature;
7
};
8
9
// füllen
10
Message msg;
11
Read(&msg);
12
13
// und auslesen
14
double ergebnis = 25 * msg.messdaten;

Hat man bereits ein char Buffer kann man diesen einfach
"uminterpretieren" und so auslesen, ohne Kopiervorgang
1
char buffer[8];
2
3
// irgentwie füllen
4
5
for (uint8_t i = 0; i < sizeof(buffer); ++i)
6
    buffer[i] = ReadSPIByte();
7
8
// uminterpretieren, C version
9
10
Message *ptr1 = (Message *)buffer;
11
12
// uminterpretiere, C++ version
13
14
Message *ptr2 = reinterpret_cast<Message *>(buffer);
15
16
// was damit machen, ob ptr1 oder ptr2 ist egal, da selber Inhalt
17
LCDSendData(ptr1->messdaten);

Fall 1 kommt auch mit dem Fall klar, dass double unter einem
festen Alignment stehen muss.

typen schrieb:
> Zum Debuggen würde ich diese gerne als binäre Kette
> aus Nullen und Einsen sehen.

Wo willst du diese Ausgeben? Wenn du Onchip debuggen
kannst, reicht der Debugger. Wenn du z.b. ein LCD oder
einen Seriellport hast, müsstest du die bits einzeln zusammen bauen.

Stephano B. schrieb im Beitrag #5258534:
> C++ kommt mir wie ein zusammengemurkstes Flickwerk vor

Tja weil du eben

Stephano B. schrieb im Beitrag #5258534:
> noch nie Software
> geschrieben hast

;-)

von mh (Gast)


Lesenswert?

Luftikus schrieb:
> Message *ptr2 = reinterpret_cast<Message *>(buffer);

Glückwunsch zur Freifahrt ins Undefined Behaviour Land.

von Luftikus (Gast)


Lesenswert?

mh schrieb:
> Glückwunsch zur Freifahrt ins Undefined Behaviour Land.

Tatsächlich nicht.

Aus dem C++ Standard:

>An object pointer can be explicitly converted to an object pointer of a different 
type.71 When a prvalue v of
>object pointer type is converted to the object pointer type “pointer to cv T”, 
the result is static_cast<cv
>T*>(static_cast<cv void*>(v)). Converting a prvalue of type “pointer to T1” to 
the type “pointer to T2”
>(where T1 and T2 are object types and where the alignment requirements of T2 are 
no stricter than those of
>T1) and back to its original type yields the original pointer value

das Array zerfällt implizit zu einem Pointer auf byte (oder char, was 
immer
man so nimmt). Unter der Voraussetzung dass das Array auf der
"anderen Seite" nach den selben Platformspezifischen Eigenheiten
aus einem Objekt vom Typ Message erzeugt wurde, und 1zu1 übertragen
wurde, ist garantiert dass wieder ein gültiges Objekt herauskommt.
Da AVRs den Typ double nicht hardwäremäßig unterstützen gibt es
auch keine Alignmentprobleme

von Rolf M. (rmagnus)


Lesenswert?

Luftikus schrieb:
> das Array zerfällt implizit zu einem Pointer auf byte (oder char, was
> immer man so nimmt). Unter der Voraussetzung dass das Array auf der
> "anderen Seite" nach den selben Platformspezifischen Eigenheiten
> aus einem Objekt vom Typ Message erzeugt wurde, und 1zu1 übertragen
> wurde, ist garantiert dass wieder ein gültiges Objekt herauskommt.

Nein. Garantiert ist, dass ich einen Zeiger auf ein Objekt in einen 
Zeiger auf char und diesen dann wieder zurück konvertieren kann, ohne 
dass es Verluste gibt.
Wenn ich die Daten wo anders hin kopiere, ist nicht garantiert, dass die 
Kopie genauso funktioniert, wie das Original.

> Da AVRs den Typ double nicht hardwäremäßig unterstützen gibt es
> auch keine Alignmentprobleme

Wie kommst du auf AVR? Du bist der erste, der den erwähnt.

von mh (Gast)


Lesenswert?

Luftikus schrieb:
> mh schrieb:
>> Glückwunsch zur Freifahrt ins Undefined Behaviour Land.
>
> Tatsächlich nicht.
>
> Aus dem C++ Standard:
> ...

Dein Zitat (8.2.10.7) aus dem Standard regelt das Konvertieren von 
Pointern, nicht den Zugriff auf Objekte durch Pointer.

Die richtige Stelle im C++17 draft (N4687) ist 6.10.8:
1
6.10 Lvalues and rvalues [basic.lval]
2
...
3
8 If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:(8.1) — the dynamic type of the object,
4
(8.2) — a cv-qualified version of the dynamic type of the object,
5
(8.3) — a type similar (as defined in 7.5) to the dynamic type of the object,
6
(8.4) — a type that is the signed or unsigned type corresponding to the dynamic type of the object,
7
(8.5) — a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
8
(8.6) — an aggregate or union type that includes one of the aforementioned types among its elements or non-static data members (including, recursively, an element or non-static data member of a subaggregate or contained union),
9
(8.7) — a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
10
(8.8) — a char, unsigned char, or std::byte type.

Punkt 8.8 regelt den Zugriff über char und byte Pointer auf andere 
Objekte. Der umgekehrte Weg ist nicht explizit aufgelistet und damit 
undefined behaviour.

von Noch einer (Gast)


Lesenswert?

Die Diskussion über Standards geht da ein bisschen in die falsche 
Richtung.

Weder in den Chips noch im C++ Standard ist definiert, wie die Zahlen 
intern dargestellt werden. Hatte sogar mal einen Chip, der die Bits 
anders herum erwartete, als das SPI-Modul des MC.

Casts und Unions funktionieren eigentlich nur, wenn du auf beiden Seiten 
den selben Prozessor und den selben Compiler verwendest.

Der einzige Tipp den man da geben kann - die Umwandlung von Bits in 
Strukturen sauber kapseln, damit sich die Hacks und Tricksereien nicht 
auf das ganze Programm verteilen.

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
Noch kein Account? Hier anmelden.