aliasing und alignment
-
Folgende Idee:
template <typename T, typename U> T& binary_cast(U& v) { static_assert(std::is_trivially_copy_constructible<T>::value); static_assert(std::is_trivially_copy_constructible<U>::value); static_assert(sizeof(T) <= sizeof v); std::memmove( &reinterpret_cast<T&>(v), &v, sizeof(T) ); return reinterpret_cast<T&>(v); }könnte funktionieren, wegen 3.9/2 - und weil an die gleiche Stelle gemoved wird, kann man hoffen, dass der Compiler das wegoptimiert.
-
cooky451 schrieb:
pyhax schrieb:
Wieso? Willst du nicht einfach die Bytes der Objekte lesen / schreiben?
Es geht darum, auf einen Speicherbereich so zuzugreifen "als wären es" [typ].
Das geht nicht, end of story.
camper schrieb:
[...] könnte funktionieren, wegen 3.9/2 - und weil an die gleiche Stelle gemoved wird, kann man hoffen, dass der Compiler das wegoptimiert.
§3.9/2 garantiert aber doch auch nur, dass die Bytes beim zurückkopieren wieder den gleichen Wert ergeben und nicht wie die Bytes aussehen!?
-
dot schrieb:
Das geht nicht, end of story.
Verstehe ich nicht. Du hast doch oben selbst geschrieben, dass es mit placement new funktioniert. Was denn jetzt?
-
Placement new konstruiert ein Objekt, das ist alles. Wo genau hab ich denn geschrieben dass du damit auf definierte Art und Weise ein Objekt als von anderem Typ interpretieren kannst!?
Wie gesagt, selbst das Speicherabbild von trivialen Typen wie double ist implementation defined. Was du vorhast kann also rein prinzipiell schon nicht allgemein gehen...
-
dot schrieb:
camper schrieb:
[...] könnte funktionieren, wegen 3.9/2 - und weil an die gleiche Stelle gemoved wird, kann man hoffen, dass der Compiler das wegoptimiert.
§3.9/2 garantiert aber doch auch nur, dass die Bytes beim zurückkopieren wieder den gleichen Wert ergeben und nicht wie die Bytes aussehen!?
Betrachte
int main() { short x[2] = { 42, 43 }; int y; memcpy( &y, &x, sizeof y); cout << y << '\n'; }gibt mir irgendeine Zahl z aus.
(Annahme: sizeof(short[2])==sizeof(int))int main() { int y; cin >> y; short x[2]; memcpy( &x, &y, sizeof x ); cout << x[0] << ',' << x[1] << '\n'; }Gebe ich hier das obige Ergebnis ein, erwarte ich als Ausgabe 42,43. Man kann es dem einzelnen memcpy ja nicht ansehen, ob es nun das ursprüngliche memcpy ist, oder das, das zum Rückkopieren dient. Das kann dann aber nur funktionieren, wenn ein einfaches memcpy bereits alle Typinformationen (auch in Hinblick auf das Aliasproblem in 3.10/10) des Ursprungsobjektes vernichtet.
-
Mir ist nicht ganz klar worauf du hinaus willst. Dass das in der Praxis funktioniert bezweifle ich nicht. Mir geht es darum dass es keinen allgemein gültigen Weg gibt um die Bytes eines Objektes als die eines Objektes von anderem Typ umzuinterpretieren, der laut Standard auf allen möglichen Implementierungen genau das gleiche, definierte Verhalten produzieren wird (und rein prinzipiell schon nicht geben kann).
-
dot schrieb:
Placement new konstruiert ein Objekt, das ist alles. Wo genau hab ich denn geschrieben dass du damit auf definierte Art und Weise ein Objekt als von anderem Typ interpretieren kannst!?
**
Du hast geschrieben, dass dasEs geht darum, auf einen Speicherbereich so zuzugreifen "als wären es" [typ].
nicht geht.* Was macht placement new denn anderes? Es packt sich den Speicherbereich und du kannst hinterher drauf zugreifen, als wäre es [typ]. Solange das also nicht UB ist (was irgendwie Quatsch wäre), muss das doch funktionieren?
* Also dass man das mit char* darf ist ja klar, aber darum geht es ja nicht.
**
dot schrieb:
Das buf[0] = 2 ist ok und die Variante mit placement new ebenfalls (vorausgesetzt buf ist richtig aligned).
-
dot schrieb:
Mir ist nicht ganz klar worauf du hinaus willst. Dass das in der Praxis funktioniert bezweifle ich nicht. Mir geht es darum dass es keinen allgemein gültigen Weg gibt um die Bytes eines Objektes als die eines Objektes von anderem Typ umzuinterpretieren, der laut Standard auf allen möglichen Implementierungen genau das gleiche, definierte Verhalten produzieren wird (und rein prinzipiell schon nicht geben kann).
Das mag so sein. Allerdings ist da auch nicht das Problem des OT. Mal agesehen davon, dass es schwierig sein wird, verschiedene Objekte auf verschiedenen Plattformen als dem gleichen Typ zugehörig zu identifizieren.
Soweit ich es verstehe, ging es darum, auf einem einzigen System eine solche Konvertierung durchzuführen - ggf. mit der Maßgabe, dass der interne Aufbau (Repräsentation) der Typen bekannt ist.
-
camper schrieb:
Soweit ich es verstehe, ging es darum, auf einem einzigen System eine solche Konvertierung durchzuführen - ggf. mit der Maßgabe, dass der interne Aufbau (Repräsentation) der Typen bekannt ist.
Ne, auf (m)einem System kannste einfach reinterpret_casten, das juckt kaum einen Compiler.
(Wie TyRoXx ja bereits am Anfang bemerkt hatte.)Es geht darum einen Standardkonformen Weg zu finden - eventuell mit static_assert für Systeme auf denen es nicht geht. Aber mit der Voraussetzung: Es kompiliert, es funktioniert.
-
cooky451 schrieb:
camper schrieb:
Soweit ich es verstehe, ging es darum, auf einem einzigen System eine solche Konvertierung durchzuführen - ggf. mit der Maßgabe, dass der interne Aufbau (Repräsentation) der Typen bekannt ist.
Ne, auf (m)einem System kannste einfach reinterpret_casten, das juckt kaum einen Compiler.
(Wie TyRoXx ja bereits am Anfang bemerkt hatte.)Probleme äußern sich häufig nicht sofort und dann vielleicht erst bei hohen Optimierungsstufen. Und dann ist es ja auch immer einfach, auf Compilerbugs zu schimpfen.
Klar ist jedenfalls, dass man nicht beweisen kann, dass ein Compiler mit Aliasing wie erwartet umgehen kann.Mit einem einzigen System meinte ich übrigens nur, dass die Quell- und Zieldaten vom gleichen System stammen sollten. Das Programm als solches sollte nat. portabel sein.
-
cooky451 schrieb:
dot schrieb:
Placement new konstruiert ein Objekt, das ist alles. Wo genau hab ich denn geschrieben dass du damit auf definierte Art und Weise ein Objekt als von anderem Typ interpretieren kannst!?
**
Du hast geschrieben, dass dasEs geht darum, auf einen Speicherbereich so zuzugreifen "als wären es" [typ].
nicht geht.* Was macht placement new denn anderes? Es packt sich den Speicherbereich und du kannst hinterher drauf zugreifen, als wäre es [typ].
Ähm, Placement new erzeugt einfach ein Objekt, das wars. Und was das anbelangt ist dein Code mit placement new imo in Ordnung, du erzeugst dort eben einen neuen double, das wars. Aber du kannst nicht nachher auf irgendwas zugreifen als wäre es was anderes. Natürlich funktioniert das auf jeder mir bekannten Implementierung. Ob der Zugriff auf buf nach dem new in dem Fall undefiniert wäre weil es ein short[2] ist und wie das aussehen würde wenn es stattdessen ein char Array wäre, ist eine gute Frage. Aber ich würd nicht davon ausgehen dass man das als wohldefiniert betrachten kann. Natürlich wird es auf praktisch jedem System funktionieren, aber das tut der altbekannte union-Hack auch...
cooky451 schrieb:
Es geht darum einen Standardkonformen Weg zu finden - eventuell mit static_assert für Systeme auf denen es nicht geht. Aber mit der Voraussetzung: Es kompiliert, es funktioniert.
memcpy()...
-
dot schrieb:
memcpy()...
Wie gesagt, dann hat man auf Systemen auf denen man eigentlich nicht kopieren müsste die schlechtere Performance.
-
Und wo liegt das Problem, auf konkreten Implementierungen einfach konkretes, implementierungsabhängiges Verhalten auszunutzen, wenn das doch genau ist was du eigentlich tun willst!?
-
Nun, ich möchte einen standardkonformen Weg finden, der immer das Beste aus der jeweiligen Plattform holt. Ist das so schwer nachzuvollziehen?
-
Nein, es ist nicht schwer nachzuvollziehen. Aber auch wenn ich es nachvollziehen kann, so ändert das nix dran, dass es so einen Weg meines Wissens nach schlicht und einfach nicht gibt.
§3.10/10 sagt ausdrücklich, ganz klar, deutlich und unmissverständlich dass jeder Zugriff auf ein Objekt durch einen anderen Typ als den "richtigen" Typ des Objektes oder unsigned/signed char undefiniert ist. Was den Standard angeht war's das, end of story.
-
Auf Stack Overflow war irgendwo ein Beispiel, wo das ganze ohne lvalues gemacht wurde (in C) (Es ist ja nur für lvalues undefiniert). Würde das vlt in C++ auch gehen?
-
Wie willst du ohne lvalue auf ein Objekt zugreifen?
-
Stimmt, schreiben wird schwierig

-
lesen auch

-
http://stackoverflow.com/a/4935669 schrieb:
The following does not violate the aliasing rule, because it has no use of lvalues accessing different types anywhere
template<typename B, typename A> B noalias_cast(A a) { union N { A a; B b; N(A a):a(a) { } }; return N(a).b; } unsigned bar(float x) { return noalias_cast<unsigned>(x); }