mikrocontroller.net

Forum: Projekte & Code Artikel und Bibliothek zu Serialisierung


Autor: Niklas G. (erlkoenig) Benutzerseite
Datum:

Bewertung
4 lesenswert
nicht lesenswert
Hallo zusammen,

im Forum kommen öfters Beiträge mit Fragen und Unklarheiten oder gar 
Falschaussagen darüber, wie Daten in C(++) zur Übertragung in Bytes 
umgewandelt, d.h. serialisiert, werden können, wie z.B.:

Beitrag "unaligned acces bei einem imx6q"
Beitrag "union mit Struktur und Array gleicher Größe?"
Beitrag "Re: Ansi C: Convertierung int to string"

Um zukünftige Diskussionen abzukürzen habe ich die diversen 
Möglichkeiten in einem Artikel zusammengefasst: Serialisierung. 
Außerdem habe ich den bereits hier ( 
Beitrag "Re: Endianess beim Kopieren automatisieren" ) gezeigten Ansatz 
erweitert und eine komplette C++-Bibliothek implementiert, welche die 
portable und standardkonforme Umwandlung von Datenstrukturen in 
Binärdaten ermöglicht:

* https://github.com/Erlkoenig90/uSer Projektseite
* https://erlkoenig90.github.io/uSer-doc/html/index.html 
Online-Dokumentation
* https://erlkoenig90.github.io/uSer-doc/html/tutorial.html Tutorial
* https://erlkoenig90.github.io/uSer-doc/html/Examples.html Beispiele

Über Feedback würde ich mich freuen!

Autor: Tobias (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für den Artikel!

Autor: Niklas G. (erlkoenig) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sehr gerne :) Wenn du das in einem Projekt nutzt oder 
Verbesserungsvorschläge oder Fehler gefunden hast würde ich mich über 
Rückmeldungen freuen...

Autor: Leo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Niklas (erlkoenig)

Vielen Dank auch von mir für den sehr gelungenen Artikel.

Ich habe jedoch noch eine Frage:
Als Fazit der 3x Möglichkeiten der Serialisierung schreibst du, dass die 
memcpy-Variante die "korrekteste" sei.

Ich kenne folgende Implementierung der memcpy-Funktion:
void *memcpy(void *dst, const void *src, size_t len)
{
   size_t i;

   if ((uintptr_t)dst % sizeof(long) == 0 &&
       (uintptr_t)src % sizeof(long) == 0 &&
        len % sizeof(long) == 0)
   {
      long *d = dst;
      const long *s = src;

      for (i=0; i<len/sizeof(long); i++)
      {
         d[i] = s[i];
      }
   }
   else
   {
      char *d = dst;
      const char *s = src;

      for (i=0; i<len; i++)
      {
         d[i] = s[i];
      }
   }
   return dst;
}

Übersehe ich etwas oder entspricht dies nicht deinem Beispiel 
Serialisierung durch Pointer-Casts. Beide chasten in einen uint8_t* 
bzw. char*. Warum sollte dann aber memcpy am "korrektesten sein?
(Mal abgesehen, dass diese schön verpackt ist als Funktion und keine 
lose Schleife darstellt)

Ich danke schon einmal im Voraus für eine Antwort.
VG Leo

Autor: Niklas G. (erlkoenig) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Angenommen man hat eine Pointer-Cast-Variante dieser Art:
typedef struct {
  uint8_t A;
  uint16_t B;
} DataPacket;

DataPacket* deserialize (const uint8_t* raw) {
  return (DataPacket*) raw;
}

int main () {
  const uint8_t raw [100];
  fread (raw, ...);
  DataPacket* dp = deserialize (raw+1); // 1. Byte überspringen
  printf ("%" PRIu16, dp->B);
}
Dann ist der Lesezugriff auf B ggf. unaligned und führt zu undefined 
behaviour. Man muss genau darauf achten dass der umgecastete Pointer 
korrektes Alignment hat; memcpy() vermeidet das komplett. Würde man z.B. 
uint16_t statt uint8_t nutzen weil man ein Interface hat das immer 
16bits auf einmal überträgt, verletzt das außerdem die strict aliasing 
regel, ist dann also undefined behaviour egal ob korrektes alignment 
oder nicht.

Eine 100% konforme memcpy()-Implementation castet auf einen char-Typ und 
kopiert char-weise, so wie der else-Zweig in deinem Code. Konkrete 
Implementationen der C Standard Library können das optimieren wie im 
if-Zweig, aber das ist dann eben nicht portabel und eben für die eine 
Plattform der C-Library spezifisch. Tatsächlich ist dieser Code dem 
Standard nach sogar falsch, denn er verletzt die strict aliasing Regel - 
mit etwas Pech könnte der Compiler ihn kaputt optimieren. Daher sollte 
man so etwas im user code lieber nicht machen.

Autor: Leo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Niklas,
vielen Dank für die schnelle Antwort.
Einen schönen Abend noch.

VG Leo

Autor: Niklas G. (erlkoenig) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gerne... ich hoffe es ist hilfreich :)

Autor: Jan K. (jan_k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Super Artikel, vielen Dank. Denke, das klärt für viele Leute einiges 
auf. Planst du, die Lib auch für Floating Point zu implementieren?

Autor: Niklas G. (erlkoenig) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke :) Floating-Point-Unterstützung plane ich derzeit nicht, weil das 
ziemlich kompliziert wäre. Wenn du eine portable Routine hast, die float 
<-> uint8_t[4] o.ä. konvertiert, kann ich die natürlich sehr gerne 
einbauen.
Da ja der C++-Standard kein bestimmtes Float-Format vorschreibt, man in 
den serialisierten Daten aber vermutlich IEEE 754 haben möchte, müsste 
man mithilfe der Standard Floating-Point-Funktionen (frexp & Co) die 
floats auseinandernehmen und daraus IEE754-Floats konstruieren. Die 
Frage ist dann auch, ob der Compiler das wegoptimiert falls die 
Plattform doch IEE754 nutzt...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.