A C++ wrapper for the basic C datatypes defined by the pEpEngine.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

129 lines
5.4 KiB

  1. # Gedanken und Vorüberlegungen zu libpEpDatatype
  2. ## Anforderungen / Designziele
  3. * Soll die von der pEpEngine definierten Datentypen so kapseln/wrappen,
  4. dass sie in C++ bequem verwendet werden können, incl. der in C++ üblichen
  5. Eigenschaften (RAII, exception safety, Implementierung der in der stdlib
  6. üblichen Operatoren und (Member-)Funktionen etc.).
  7. * Die Wrapper sollen zusätzliche semantische Constraints der Daten sicherstellen
  8. bzw. schaffen, z.B. dass alle Strings "NFC-normalisierte UTF-8"-Strings sind.
  9. Ggf. sind die Daten dafür umzukopieren.
  10. * Die Datentypen sollen im Zusammenspiel mit den Funktionen der public API
  11. der pEpEngine benutzbar sein (entweder _direkt_, z.B. über geeignete
  12. Konvertierungs-Operatoren oder über einfache, verständliche Access-Funktionen).
  13. * Die Wrapper sollen effizient sein, in Bezug auf Speicherverbrauch und Laufzeitverhalten.
  14. * libpEpDatatypes soll leicht erweiterbar sein für zukünftige Datentypen.
  15. ## Implementierungs-Varianten / -Alternativen
  16. * thin wrapper vs. fat wrapper
  17. * hangeschriebener Wrapper-Code vs. Template-Magic vs. externer Codegenerierung (YML2?)
  18. Codebeispiele anhand von
  19. * `pEp_identity` – als einfacher Struct Typ
  20. * `identity_list` – als typischer Vertreter der in der Engine zahlreich
  21. vorhandenen (einfach) verketteten Listen.
  22. ### Thin Wrapper
  23. * Enthält den C-Datentyp als Data Member
  24. * Erlaubt Zugriff auf die Member des C-Datentyps via `operator->()`
  25. * (+) Code lässt sich leicht generieren, oder mit Unterstützung weniger Hilfsfunktionen per Template generieren
  26. * (+) ist i.A. sehr effizient, erfordert aber ggf. Zusatzfunktionen, um unnötige doppelte
  27. Stringkopien zu vermeiden
  28. * (-) Der aufgerufene C-Code kann die gewrappten Daten – unbemerkt von libpEpDatatype – modifizieren,
  29. so dass z.B. die Zusicherung nach NFC-normalisierten Strings nicht mehr gehalten werden kann.
  30. ```
  31. class pEp::Wrapper<pEp_identity*>
  32. {
  33. public:
  34. // TODO: Soll "Ownership" von `*id` übernommen werden oder eine Kopie gemacht werden?
  35. // TODO 2: 'explicit', um ungewollte Typumwandlungen zu vermeiden?
  36. Identity(pEp_identity* id);
  37. // Ruft intern new_identity() mit den 4 Parametern auf.
  38. // Die anderen Member muss man danach manuell setzen. :-|
  39. // Problem: NFC-normalisierung muss vorher erfolgen -> ggf. String-Kopie.
  40. // new_identity() kopiert die Strings dann erneut -> unnötig.
  41. // ad-hoc-Lösung von Volker (2021-10-02): identity_dup_NFC(). Analog könnte man new_identity_NFC() hinzufügen. HACK?
  42. // Evtl. wäre ein new_identity_move_ownership() oder so sinnvoller, da generischer?
  43. Identity(const char* address, const char* fpr, const char* user_id, const char* username);
  44. // TODO: Rückgabe von wrapped_value, als "const" oder eine Kopie?
  45. pEp_identity* get();
  46. // alternative Syntax: Konvertierungs-Operator?
  47. // TODO: automatisch oder 'explicit', um ungewollte Typumwandlungen zu vermeiden?
  48. operator pEp_identity*();
  49. private:
  50. pEp_identity* wrapped_value;
  51. };
  52. using Identity = Wrapper<pEp_identity*>;
  53. ```
  54. Beispielcode für die Verwendung:
  55. ```
  56. pEp::Identity id("example@pep.me", "0xCAFEBABE", "example", "Elon Xaver Ample");
  57. ...
  58. // je nach o.g. API:
  59. update_identity(session, id);
  60. // oder
  61. update_identity(session, id.get());
  62. ```
  63. PROBLEM: Die Funktion ändert die Member von 'id' in einer nicht von libpEpDataType
  64. erkennbaren Weise. UTF8-NFC _kann_ danach _nicht_ mehr garantiert werden.
  65. Der _derzeitige_ Code von update_identity() scheint diesbezüglich sauber zu sein.
  66. Aber z.B. `message->opt_fields` wird an zahlreichen Stellen modifiziert, NFC-konformität ist
  67. danach nicht mehr gewährleistet.
  68. Mögliche Lösung: `Wrapper<T*>::get()` gibt nicht den C-Datentyp zurück, sondern ein Proxy-Objekt,
  69. in dessen Destruktor alle Member-Strings auf NFC-konformität geprüft werden.
  70. -> Wie zuverlässig ist das?
  71. ### Thin Wrapper für Listen
  72. * (+) Code kann generiert werden, auch mit C++ Template Magie
  73. * (+) Sehr effizient, dank Move-Semantik
  74. * (-) Keine C++-Semantik bezügl. Const-Correctness
  75. d.h. C-Code könnte die Member einer `const pEp::IdentityList` verändern und auch Elemente
  76. hinzufügen/entfernen, was der C++-Semantik von `const` widerspricht.
  77. * public API der Engine reicht nicht aus, um komplette Schnittstelle von `std::forward_list<T>`
  78. zu implementieren. TODO: API erweitern? Oder "hacken" und in den Interna der Datentypen direkt
  79. herumfummeln? Welche Methoden, Typen und Eigenschaften von `std::forward_list<T>` werden
  80. überhaupt benötigt?
  81. ### Fat Wrapper
  82. * Enthält eigene Memberdefinitionen. Möglich wären C-Strings oder C++-Strings.
  83. * .get() generiert C-Datentyp und on-the-fly und gibt ihn zurück. Problem: Ownership?
  84. * (+) Zustand von gewrappten Daten bleibt konsistent (z.B. NFC-konformität, 'const' Correctness etc.)
  85. * (+) Container-API einfacher implementierbar und leistungsfähiger, da sie z.B.
  86. an `std::deque<T>` statt `std::forward_list<T>` angelehnt sein könnte.
  87. * (-) Laufzeit- und Speicheraufwand
  88. * (--) C-Funktionen mit In-Out-Parametern sind so nicht nutzbar. TODO: Vielleicht mit Proxy-Objekten?
  89. ### "Hybrider" Wrapper für Listen
  90. * Enthält den C-Datentyp (verkettete Liste)
  91. * Zusätzlich einen C++-Container (z.B. `std::deque<>`) mit Zeigern/Referenzen auf die Listenelemente
  92. * (+) bietet effizienteren und komfortableren Zugriff von C++ aus als von C aus
  93. * (-) Implementierungsaufwand
  94. * (-) fragil und versagt, wenn C-Code an der C-Liste herumfummelt
  95. (to be continued)