add German design document for libpEpDatatypes.
parent
01941f70ad
commit
4cd987c24f
|
@ -0,0 +1,129 @@
|
|||
# Gedanken und Vorüberlegungen zu libpEpDatatype
|
||||
|
||||
## Anforderungen / Designziele
|
||||
|
||||
* Soll die von der pEpEngine definierten Datentypen so kapseln/wrappen,
|
||||
dass sie in C++ bequem verwendet werden können, incl. der in C++ üblichen
|
||||
Eigenschaften (RAII, exception safety, Implementierung der in der stdlib
|
||||
üblichen Operatoren und (Member-)Funktionen etc.).
|
||||
|
||||
* Die Wrapper sollen zusätzliche semantische Constraints der Daten sicherstellen
|
||||
bzw. schaffen, z.B. dass alle Strings "NFC-normalisierte UTF-8"-Strings sind.
|
||||
Ggf. sind die Daten dafür umzukopieren.
|
||||
|
||||
* Die Datentypen sollen im Zusammenspiel mit den Funktionen der public API
|
||||
der pEpEngine benutzbar sein (entweder _direkt_, z.B. über geeignete
|
||||
Konvertierungs-Operatoren oder über einfache, verständliche Access-Funktionen).
|
||||
|
||||
* Die Wrapper sollen effizient sein, in Bezug auf Speicherverbrauch und Laufzeitverhalten.
|
||||
|
||||
* libpEpDatatypes soll leicht erweiterbar sein für zukünftige Datentypen.
|
||||
|
||||
|
||||
## Implementierungs-Varianten / -Alternativen
|
||||
|
||||
* thin wrapper vs. fat wrapper
|
||||
* hangeschriebener Wrapper-Code vs. Template-Magic vs. externer Codegenerierung (YML2?)
|
||||
|
||||
Codebeispiele anhand von
|
||||
* `pEp_identity` – als einfacher Struct Typ
|
||||
* `identity_list` – als typischer Vertreter der in der Engine zahlreich
|
||||
vorhandenen (einfach) verketteten Listen.
|
||||
|
||||
|
||||
### Thin Wrapper
|
||||
|
||||
* Enthält den C-Datentyp als Data Member
|
||||
* Erlaubt Zugriff auf die Member des C-Datentyps via `operator->()`
|
||||
* (+) Code lässt sich leicht generieren, oder mit Unterstützung weniger Hilfsfunktionen per Template generieren
|
||||
* (+) ist i.A. sehr effizient, erfordert aber ggf. Zusatzfunktionen, um unnötige doppelte
|
||||
Stringkopien zu vermeiden
|
||||
* (-) Der aufgerufene C-Code kann die gewrappten Daten – unbemerkt von libpEpDatatype – modifizieren,
|
||||
so dass z.B. die Zusicherung nach NFC-normalisierten Strings nicht mehr gehalten werden kann.
|
||||
|
||||
```
|
||||
class pEp::Wrapper<pEp_identity*>
|
||||
{
|
||||
public:
|
||||
// TODO: Soll "Ownership" von `*id` übernommen werden oder eine Kopie gemacht werden?
|
||||
// TODO 2: 'explicit', um ungewollte Typumwandlungen zu vermeiden?
|
||||
Identity(pEp_identity* id);
|
||||
|
||||
// Ruft intern new_identity() mit den 4 Parametern auf.
|
||||
// Die anderen Member muss man danach manuell setzen. :-|
|
||||
// Problem: NFC-normalisierung muss vorher erfolgen -> ggf. String-Kopie.
|
||||
// new_identity() kopiert die Strings dann erneut -> unnötig.
|
||||
// ad-hoc-Lösung von Volker (2021-10-02): identity_dup_NFC(). Analog könnte man new_identity_NFC() hinzufügen. HACK?
|
||||
// Evtl. wäre ein new_identity_move_ownership() oder so sinnvoller, da generischer?
|
||||
Identity(const char* address, const char* fpr, const char* user_id, const char* username);
|
||||
|
||||
// TODO: Rückgabe von wrapped_value, als "const" oder eine Kopie?
|
||||
pEp_identity* get();
|
||||
|
||||
// alternative Syntax: Konvertierungs-Operator?
|
||||
// TODO: automatisch oder 'explicit', um ungewollte Typumwandlungen zu vermeiden?
|
||||
operator pEp_identity*();
|
||||
|
||||
private:
|
||||
pEp_identity* wrapped_value;
|
||||
};
|
||||
|
||||
using Identity = Wrapper<pEp_identity*>;
|
||||
```
|
||||
|
||||
Beispielcode für die Verwendung:
|
||||
|
||||
```
|
||||
pEp::Identity id("example@pep.me", "0xCAFEBABE", "example", "Elon Xaver Ample");
|
||||
...
|
||||
// je nach o.g. API:
|
||||
update_identity(session, id);
|
||||
// oder
|
||||
update_identity(session, id.get());
|
||||
```
|
||||
|
||||
PROBLEM: Die Funktion ändert die Member von 'id' in einer nicht von libpEpDataType
|
||||
erkennbaren Weise. UTF8-NFC _kann_ danach _nicht_ mehr garantiert werden.
|
||||
Der _derzeitige_ Code von update_identity() scheint diesbezüglich sauber zu sein.
|
||||
|
||||
Aber z.B. `message->opt_fields` wird an zahlreichen Stellen modifiziert, NFC-konformität ist
|
||||
danach nicht mehr gewährleistet.
|
||||
|
||||
Mögliche Lösung: `Wrapper<T*>::get()` gibt nicht den C-Datentyp zurück, sondern ein Proxy-Objekt,
|
||||
in dessen Destruktor alle Member-Strings auf NFC-konformität geprüft werden.
|
||||
-> Wie zuverlässig ist das?
|
||||
|
||||
|
||||
### Thin Wrapper für Listen
|
||||
|
||||
* (+) Code kann generiert werden, auch mit C++ Template Magie
|
||||
* (+) Sehr effizient, dank Move-Semantik
|
||||
|
||||
* (-) Keine C++-Semantik bezügl. Const-Correctness
|
||||
d.h. C-Code könnte die Member einer `const pEp::IdentityList` verändern und auch Elemente
|
||||
hinzufügen/entfernen, was der C++-Semantik von `const` widerspricht.
|
||||
|
||||
* public API der Engine reicht nicht aus, um komplette Schnittstelle von `std::forward_list<T>`
|
||||
zu implementieren. TODO: API erweitern? Oder "hacken" und in den Interna der Datentypen direkt
|
||||
herumfummeln? Welche Methoden, Typen und Eigenschaften von `std::forward_list<T>` werden
|
||||
überhaupt benötigt?
|
||||
|
||||
|
||||
### Fat Wrapper
|
||||
|
||||
* Enthält eigene Memberdefinitionen. Möglich wären C-Strings oder C++-Strings.
|
||||
* .get() generiert C-Datentyp und on-the-fly und gibt ihn zurück. Problem: Ownership?
|
||||
* (+) Zustand von gewrappten Daten bleibt konsistent (z.B. NFC-konformität, 'const' Correctness etc.)
|
||||
* (+) Container-API einfacher implementierbar und leistungsfähiger, da sie z.B.
|
||||
an `std::deque<T>` statt `std::forward_list<T>` angelehnt sein könnte.
|
||||
* (-) Laufzeit- und Speicheraufwand
|
||||
* (--) C-Funktionen mit In-Out-Parametern sind so nicht nutzbar. TODO: Vielleicht mit Proxy-Objekten?
|
||||
|
||||
### "Hybrider" Wrapper für Listen
|
||||
* Enthält den C-Datentyp (verkettete Liste)
|
||||
* Zusätzlich einen C++-Container (z.B. `std::deque<>`) mit Zeigern/Referenzen auf die Listenelemente
|
||||
* (+) bietet effizienteren und komfortableren Zugriff von C++ aus als von C aus
|
||||
* (-) Implementierungsaufwand
|
||||
* (-) fragil und versagt, wenn C-Code an der C-Liste herumfummelt
|
||||
|
||||
(to be continued)
|
Loading…
Reference in New Issue