Placement new in generic union class
-
Hier wird das Thema kurz erwähnt
https://isocpp.org/wiki/faq/dtors#placement-newDANGER: You are taking sole responsibility that the pointer you pass to the “placement new” operator points to a region of memory that is big enough and is properly aligned for the object type that you’re creating. Neither the compiler nor the run-time system make any attempt to check whether you did this right. If your Fred class needs to be aligned on a 4 byte boundary but you supplied a location that isn’t properly aligned, you can have a serious disaster on your hands (if you don’t know what “alignment” means, please don’t use the placement new syntax). You have been warned.
Ich bin mit dem alignment aber noch nicht 100% vertraut, was bewirkt dieses:
aligned_storageintern?
Verschiebt es die Startadresse des Objektes, so das sie immer module 0 ergibt?
-
Nash26 schrieb:
- (...)
Du hast gesehen dasVariantkeine struct ist, sondern ein Union?
Wozu brauch ich dann nochstd::aligned_storage?
Du hast gelesen dass ich "1) Keine Ahnung. Ich sehe aber keinen Grund aligned_storage nicht zu verwenden." geschrieben habe

Nash26 schrieb:
In deinem konkreten Beispiel sehe ich kein Problem mit nicht trivial kopierbaren Objekten.
Du siehst zusätzlich kein Problem mit nicht trivial kopierbaren Objekten? Oder hast du dich da verschrieben?
Ich denke ich hab' es schon ausreichend beschrieben.
Du kannst da mit placement new ein T reinmachen was nicht trivial kopierbar ist.
Du kannst dann weiterhin das Variant Ding mit dem auto-generierten Ctor kopieren.
Nur dann ist in der trivial kopierten Kopie natürlich kein T drinnen.
Demzufolge kannst du es dann auch nicht als T ansprechen.
Wenn du aber wiederrum mit placement new kopierst, dann sollte es auch OK sein.Guck dir z.B. einfach mal die Implementierung von
boost::optionalan.
- (...)
-
Ok, und was ist wenn ich folgendes Objekt in die union packe?
struct alignas(128) Fred { int i; };Ich denke das sollte dann wirklich Probleme geben, was auch immer das heißen mag? Crash? Langsamer Daten zugriff?
Ich formuliere meine Frage allgemeiner:
Was ist die Bedingung dafür, dass ich eine beliebigen Klasse auf diese union (die alle primitive Datentypen beinhaltet) mit
placement newabspeichern kann?Muss ich den Code auf dein spezielles Variant ändern mit
aligned_storage?
oder kann ich meine Union weiter verwenden?In welchen Kapitel im Standard finde ich dazu eine Information? Unter dem Punkt 9.5 Unions ist jedenfalls nichts.
-
Nash26 schrieb:
Ok, und was ist wenn ich folgendes Objekt in die union packe?
struct alignas(128) Fred { int i; };Ich denke das sollte dann wirklich Probleme geben, was auch immer das heißen mag? Crash? Langsamer Daten zugriff?
Soweit ich weiss ist
alignof > sizeofnicht möglich.
Würde zumindest nie mit Arrays funktionieren - wie sollte sonst jemals das Alignment des 2. Elements passen?Nash26 schrieb:
Ich formuliere meine Frage allgemeiner:
Was ist die Bedingung dafür, dass ich eine beliebigen Klasse auf diese union (die alle primitive Datentypen beinhaltet) mit
placement newabspeichern kann?Muss ich den Code auf dein spezielles Variant ändern mit
aligned_storage?
oder kann ich meine Union weiter verwenden?Du musst vermutlich nicht, aber ich würde es machen. Weil ich mit
aligned_storageweiss was ich bekomme, weiss wie man es verwenden muss.Und was sind die Bedingungen?
Na die Grösse muss passen und das Alignment muss passen.Ganze einfache Variante: verwende
aligned_storage<N, N>::type, wobei du dir N einfach frei aussuchst -- so viel wie du halt maximal für nen char/bool "verschwenden" willst. Dann musst du nur noch die Grösse prüfen, daalignof > sizeofeben nicht geht.Nash26 schrieb:
In welchen Kapitel im Standard finde ich dazu eine Information? Unter dem Punkt 9.5 Unions ist jedenfalls nichts.
Kann ich dir nicht auswendig sagen. Vermutlich aber verstreut über 100 Paragraphen.
-
Aktuell is meine Bedingung:
template<typename T> using can_place_in_variant = std::integral_constant<bool, sizeof(T) <= sizeof(variant) && alignof(T) == alignof(variant)>;Primitive datentypen (int, bool) wollte ich halt direkt in der union abspeichern ohne "placement new", da brauch ich mich nicht kompliziert zu casten um den Wert da herauszubekommen und es wird vielleicht auch ein wenig was kosten.
-
alignof(T) == alignof
alignof(T) <= alignofSieht sonst OK aus.
Und primitive Typen kannst du auch einfach per gecastetem Zeiger da rein schreiben bzw. wieder rauslesen.
-
hustbaer schrieb:
alignof(T) <= alignof
alignof(variant) % alignof(T) == 0?
-
@Arcoth
Ich hab in Erinnerung dass als Alignment Requirement eh nur Zweierpotenzen erlaubt sind.
Von daher sollte der "kleiner" Vergleich ausreichend sein.Falls das nicht stimmt muss man natürlich % verwenden, ja.
-
Ich hab in Erinnerung dass als Alignment Requirement eh nur Zweierpotenzen erlaubt sind.
Stimmt natürlich. §3.11/4.
Das ist echt clever von dir. Leider ist cleverer Code der die Intention nicht ausdrückt.. mMn. allgemein nicht zu empfehlen. Genauso würde ich niemalsif (a & 2)schreiben um auszudrücken, dass
aeine gerade Ganzzahl ist, nur weil der Standard festlegt, dass nur Ganzzahlrepresentierungen verwendet werden dürfen die derartiges unterstützen.
Ich möchte mich in Code so ausdrücken dass jeder versteht welcher Gedanke hinter einer Bedingung steht. Und deine Art es zu schreiben impliziert einfach, dass kein weiteres Verhältnis zwischen den Ausrichtungen bestehen muss, als dass die von T kleiner als die von X ist.Sag's mir, falls ich übertreibe.
-
Arcoth schrieb:
Leider ist cleverer Code der die Intention nicht ausdrückt.. mMn. allgemein nicht zu empfehlen.
Arcoth@unverspielt:

Hast anscheinend die 15 Jahre, die die meisten dafür brauchen, auf nur 5 drücken können.
Hoffe, ich hatte mit meiner Meckerei einen kleinen Anteil (vermutlich dadurch, daß Du genau das Gegenteil meiner Wünsche machtest und gegen Wände gelaufen bist).
-
@Arcoth
Ich verstehe dein Argument.
Ich weiss nicht ob ich sagen würde dass du übertreibst. Wenn der einzige Nachteil deiner Variante der wäre, dass sie ein klitzekleines bisschen "komplizierter" ist, dann würde ich dir vielleicht zustimmen.Nur ... genau so wie meine Variante impliziert dass a <= b ausreicht, und nicht noch die versteckte Bedingung dass a und b beides Zweierpotenzen sein müssen notwendig ist...
Genau so impliziert deine Variante dass es nötig ist den Test b % a == 0 zu machen, und dass es eben keine weitere Vorschrift vom Standard gibt die einem das Leben hier etwas einfacher macht.Wenn ein armer Programmierer das jetzt nicht weiss, und deinen Code liest, und das Programm erweitern muss...
und dann davon ausgeht dass er überall mit allen möglichen Alignment Requirements rechnen muss...
und nicht auf die Idee kommt mal im Standard nachzugucken was da wirklich die Regeln sind...
dann würde er vermutlich viel viel zu komplizierten Code schreiben.Was mMn. klar gegen deine Variante spricht.
Aber Gott-sei-Dank gibt es ja sowas wie Kommentare

Vorschlag:alignof(T) <= alignof(variant) /* alignment requirements are always a power of two, so checking a <= b is sufficient */oder, wenn du unbedingt willst
alignof(variant) % alignof(T) == 0 /* actually alignment requirements are always a power of two, so b <= a would suffice, but a % b == 0 more clearly expresses what we are really testing here */Wobei ich auch hier die a <= b Variante vorziehe, weil sie den Fokus auf die einfachere, ausreichende Variante setzt.
ps:
Arcoth schrieb:
Stimmt natürlich. §3.11/4.
Das ist echt clever von dir.Gar nicht clever, ich kenne es einfach nicht anders. Also jeglicher Code den ich bisher gesehen habe der Alignment Requirements prüft verwendet einfach a <= b Tests.
BTW: Nachdem sich bei C++ in den letzten Jahren einiges getan hat, und auch hoffentlich weiterhin was tut, und so viel tut dass sich die ganzen §-Nummern im Standard gröber verschieben, wäre es vielleicht günstig immer die Standard-Version mit anzugeben.
Also entweder die "n-Nummer" oder einfach "C++XY".EDIT: Fixed: a < b --> a <= b
-
volkard schrieb:
Hast anscheinend die 15 Jahre, die die meisten dafür brauchen, auf nur 5 drücken können.
Ich finde die Entwicklung von HaSoCoth ... erstaunlich. Ernsthaft.
@Arcoth
-
volkard schrieb:
Hast anscheinend die 15 Jahre, die die meisten dafür brauchen, auf nur 5 drücken können.
Vier

-
Arcoth schrieb:
Genauso würde ich niemals
if (a & 2)schreiben um auszudrücken, dass
aeine gerade Ganzzahl ist, nur weil der Standard festlegt, dass nur Ganzzahlrepresentierungen verwendet werden dürfen die derartiges unterstützen.würde ich auch nicht, aber aus einem anderen Grund.
(Tip: !(a&1))
-
Wenn ein armer Programmierer das jetzt nicht weiss, und deinen Code liest, und das Programm erweitern muss...
und dann davon ausgeht dass er überall mit allen möglichen Alignment Requirements rechnen muss...Bin nicht dran gewohnt oder ausgerichtet mit Leuten zu arbeiten, die unbegründete Annahmen aus einer Code-Zeile entnehmen und auf dieser Basis ein Projekt erweitern/umschreiben.
großbuchstaben schrieb:
würde ich auch nicht, aber aus einem anderen Grund.
(Tip: !(a&1))Ups, da war ich im Eifer des Gefechts ganz woanders, sorry. Es hilft nicht, dass ich gerade mit etwas völlig anderem Beschäftigt bin.
hustbaer schrieb:
volkard schrieb:
Hast anscheinend die 15 Jahre, die die meisten dafür brauchen, auf nur 5 drücken können.
Ich finde die Entwicklung von HaSoCoth ... erstaunlich. Ernsthaft.
@ArcothKlingt zynisch. Und da ich annehmen darf, dass ein Gentlemen wie du nicht aus Versehen zynisch ist...
-
Daran war nichts zynisch gemeint.
Du musst ja auch selbst merken wie sich z.B. der Ton mit dem wir uns unterhalten MASSIV verbesser hat in den letzten Jahren.ps: Falls du dich am "HaSoCoth" gestossen hast - das war nicht böse gemeint. Der kleine Insiderwitz sei mir bitte verziehen

-
Arcoth schrieb:
Wenn ein armer Programmierer das jetzt nicht weiss, und deinen Code liest, und das Programm erweitern muss...
und dann davon ausgeht dass er überall mit allen möglichen Alignment Requirements rechnen muss...Bin nicht dran gewohnt oder ausgerichtet mit Leuten zu arbeiten, die unbegründete Annahmen aus einer Code-Zeile entnehmen und auf dieser Basis ein Projekt erweitern/umschreiben.
Das selbe Argument funktioniert auch für den Standpunkt dass deine Variante unnötig kompliziert ist, und auch ein einfaches a <= b ohne weiteres Kommentar völlig ausreicht.
Wenn ich deinen Code - ohne weiteres Kommentar - irgendwo lesen würde, dann würde es mich kurz verwirren. Und danach würde ich mir denken "weiss der nicht dass a <= b reicht, oder macht er Dinge einfach gerne unnötig kompliziert?".
-
hustbaer schrieb:
alignof(T) == alignof
alignof(T) <= alignofSieht sonst OK aus.
Und primitive Typen kannst du auch einfach per gecastetem Zeiger da rein schreiben bzw. wieder rauslesen.Wie kann ich einen Wert (double, und custom type) in einem std::aligned_storage
speichern und wieder rausholen?
-
struct Foo { template <class T> void put_trivial(T t) { *reinterpret_cast<T*>(&m_storage) = t; } template <class T> T get_trivial() const { return *reinterpret_cast<T const*>(&m_storage); } std::aligned_storage<...>::type m_storage; };Und nicht triviale Typen dann entsprechend mit placement
new, bzw. nach dem das Objekt einmal erstellt ist kannst du genau so denreinterpret_cast<T*>verwenden um an einen gültigenT*Zeiger zu kommen.
-
Also ich hab inzwischen sowas hier:
struct Foo { template <class T> void put_trivial(T t) { reinterpret_cast<T&>(m_storage) = t; } template <class T> T get_trivial() const { return reinterpret_cast<T const&>(m_storage); } std::aligned_storage<...>::type m_storage; };und für Pointer:
struct Foo { template <class T> void put_trivial(T* t) { reinterpret_cast<T*&>(m_storage) = t; } template <class T> T* get_trivial() const { return reinterpret_cast<T const*&>(m_storage); } std::aligned_storage<...>::type m_storage; };Das ist doch auch okay, oder?