Ich möchte "Handles" zu Daten in einem Objektpuffer verwenden und speichern, um den Overhead der Zuweisung zu reduzieren. Der Handle ist einfach ein Index in ein Array mit dem Objekt. Allerdings muss ich nach-Neu-Zuweisungen erkennen, da dies sehr leicht möglich ist. Der übliche Ansatz scheint Bitfelder zu verwenden. Dies führt jedoch zu zwei Problemen:Portable Bitfelder für Handles
- Bitfelder sind Implementierung definiert
- Bit Verschiebung über große/Little-Endian-Maschinen nicht tragbar ist.
Was ich brauche:
- Shop Griff in einer Datei (Datei-Handler entweder Integer-Typen verwalten können (Byteaustausch) oder Byte-Arrays)
- Speicher 2 Werte im Griff mit minimalem Platz
Was ich bekommen habe:
template<class T_HandleDef, typename T_Storage = uint32_t>
struct Handle
{
typedef T_HandleDef HandleDef;
typedef T_Storage Storage;
Handle(): handle_(0){}
private:
const T_Storage handle_;
};
template<unsigned T_numIndexBits = 16, typename T_Tag = void>
struct HandleDef{
static const unsigned numIndexBits = T_numIndexBits;
};
template<class T_Handle>
struct HandleAccessor{
typedef typename T_Handle::Storage Storage;
typedef typename T_Handle::HandleDef HandleDef;
static const unsigned numIndexBits = HandleDef::numIndexBits;
static const unsigned numMagicBits = sizeof(Storage) * 8 - numIndexBits;
/// "Magic" struct that splits the handle into values
union HandleData{
struct
{
Storage index : numIndexBits;
Storage magic : numMagicBits;
};
T_Handle handle;
};
};
A Verwendung wäre zum Beispiel:
typedef Handle<HandleDef<24> > FooHandle;
FooHandle Create(unsigned idx, unsigned m){
HandleAccessor<FooHandle>::HandleData data;
data.idx = idx;
data.magic = m;
return data.handle;
}
Mein Ziel war es, den Griff so undurchsichtig wie möglich zu halten, einen Bool Scheck, aber sonst nichts hinzuzufügen. Benutzer des Griffs sollten nicht in der Lage sein, etwas damit zu tun, sondern ihn herumreichen.
So Probleme, die ich laufe in:
- Union ist UB -> Ersetzen seines
T_Handle
durchStorage
und Ctor hinzufügen von Speicher - Wie funktioniert die Compiler Layout des Bit-Feld zu behandeln? Ich fülle die ganze Union/Typ, so sollte es keine Auffüllung geben. Wahrscheinlich ist das einzige, was anders sein kann, welcher Typ zuerst kommt, abhängig von Endianess, korrekt?
- Wie kann ich
handle_
in einer Datei speichern und es von einem möglichen anderen endianess Maschine laden und immer nochindex
undmagic
korrekt sein? Ich denke, ich kann dieStorage
"Endian-Korrekt" speichern und korrekte Werte erhalten, IF beide Mitglieder belegen genau die Hälfte des Platzes (2 Shorts in einem Uint) Aber ich will immer mehr Platz für den Index als für den magischen Wert.
Hinweis: Es gibt bereits Fragen zu Bitfeldern und Verbindungen. Zusammenfassung:
- Bitfelder unerwartete Polsterung (hier nicht möglich, da ganzer Typ besetzt) hat
- Reihenfolge der „Mitglieder“ ist abhängig von Compiler (nur 2 Möglichkeiten hier, speichern sollte, um hängt ganz von Endianess zu übernehmen, so kann dies hier helfen oder nicht)
- Spezifisches binäres Layout von Bits kann durch manuelles Verschieben erreicht werden (oder zB Wrapper http://blog.codef00.com/2014/12/06/portable-bitfields-using-c11/) -> Ist hier keine Antwort. Ich brauche auch ein spezifisches Layout der Werte IN das Bitfeld. Ich bin also nicht sicher, was ich bekomme, wenn ich z. Erstellen Sie ein Handle als
handle = (magic << numIndexBits) | index
, und speichern/laden Sie es als Binärdatei (keine Endianess-Konvertierung). Eine BigEndian-Maschine zum Testen fehlt.
Hinweis: Kein C++ 11, aber Boost ist erlaubt.
ein uint32_t durch htonl Laufen() vor dem Speichern und durch ntohl() nach dem Laden sicherstellen würde, dass das On-Disk-Bits Big-Endian-Format immer in gespeichert ist, unabhängig von der Bytereihenfolge der CPU. –
So kann ich das gleiche Layout von 'Storage' auf der Festplatte erhalten. Aber bedeutet das auch, dass ich über Bitfeld oder Verschieben sicher auf die 2 Werte zugreifen kann? (Nebenfrage: Gibt es einen Online-Dienst, um solche Dinge zu testen?) – Flamefire
Die C/C++ Bit-Shift-Operatoren arbeiten immer mit der Native-Endian-Repräsentation der Daten, so dass die Endian-Eigenschaft des Wortes ihre Semantik nicht ändert. Endian-Ness ist nur ein Problem, wenn die Daten von einem Computer zu einem anderen übertragen werden (zB über eine Netzwerkverbindung oder indem sie in einer Datei auf einer Maschine gespeichert und auf eine andere Maschine geladen werden), und sie kann über htonl()/ntohl () Stilkorrektur der Endian-Ness als Teil der Save/Restore-Routinen. –