Eigene Klassen, die Pointer enthalten in Binary-Datei speichern?
-
Am einfachsten ist, wenn du eine Funktion write machst, die das Ding in den Stream schreibst. Ausnahmsweise hat Hacker aber damit Recht, dass du einen Vector benutzen solltest. Das könnte in etwa so aussehen:
struct foo { std::vector<int> bar; int baz; void write(std::ostream& os) { auto size = bar.size(); os.write(reinterpret_cast<const char*>(&size), sizeof(size)); // Größe schreiben, um nacher zu wissen, wie viele Elemente gelesen werden sollen for(auto it = bar.begin(); it != bar.end(); ++it) os.write(reinterpret_cast<const char*>(&*it), sizeof(*it)); // Elemente schreiben os.write(reinterpret_cast<const char*>(&baz), sizeof(baz)); // baz schreiben } };Beim Einlesen musst du natürlich die Größe wieder einlesen und dann entsprechend oft Elemente.
-
314159265358979 schrieb:
int baz; //... os.write(reinterpret_cast<const char*>(baz), sizeof(baz)); // baz schreibenfail
-
Ich habe doch nur ein & vergessen, kann doch mal passieren Schnucki.
-
314159265358979 schrieb:
Ich habe doch nur ein & vergessen, kann doch mal passieren Schnucki.
Trotzdem 2 Casts zuviel.
-
Erklär mal bitte.
-
reinterpret_cast ist ein Konstrukt, dass sparsam eingesetzt werden sollte. Außerdem sollte man sich nicht unnötig wiederholen. Mithin ist eine eigene Funktion dafür angebracht. Erfahrungsgemäß werden gerade solche Codestücke wie oben gerne 1:1 in produktiven Code mit den entsprechenden Folgen übernommen.
Schöner wäre z.B.template <typename T> typename std::enable_if<std::is_fundamental<T>, std::ostream>::type& write_unformatted(std::ostream& os, const T& value) { os.write(&reinterpret_cast<const char&>(value), sizeof value); return os; }Damit werden automatisch erst einmal alle fundamentalen Typen erschlagen (nur für diese macht das ja einigermaßen Sinn). Für weitere Typen kann man dann geeignete Overloads anbieten (die logischerweise ohne Casts auskommen)
struct foo { std::vector<int> bar; int baz; }; template <typename T, typename A> ostream& write_unformatted(std::ostream& os, const std::vector<T, A>& v) { write_unformatted(os, v.size()); for(auto it = v.begin(); it != v.end(); ++it) write_unformatted(os, *it); return os; } ostream& write_unformatted(std::ostream& os, const foo& v) { write_unformatted(os, v.bar); write_unformatted(os, v.baz); return os; }
-
Naja, wenn du sowas meintest

-
Wenn schon auf Kleinigkeiten herum reiten, dann bitte so:
template <typename T> typename std::enable_if<std::is_fundamental<T>, std::ostream>::type& write_unformatted(std::ostream& os, const T& value) { void const* temp = static_cast<void const*>(&value); os.write(static_cast<char const*>(temp), sizeof(value)); return os; }
-
Das ist UB.
-
Ethon schrieb:
Wenn schon auf Kleinigkeiten herum reiten, dann bitte so:
template <typename T> typename std::enable_if<std::is_fundamental<T>, std::ostream>::type& write_unformatted(std::ostream& os, const T& value) { void const* temp = static_cast<void const*>(&value); os.write(static_cast<char const*>(temp), sizeof(value)); return os; }Warum? Es geht nicht darum, Dinge zu verstecken. Wenn es gefährlich (bzw. in irgenweiner Weise speziell) ist, sollte der Code das auch widerspiegeln. Umgekehrt geht es nicht an, sich diesbezgl. 100 mal zu wiederholen (gerne dann in 500-Zeilen-Monsterfunktionen). Dann wird nämlich die Aufmerksamkeit bei jeder einzelnen Zeile stark nachlassen.
-
Ich hab jediglich den Cast von reinterpret auf 2 statics geändert, was ja in der Theorie die sauberere & sichere Version ist.

-
Sieht interessant aus^^ Damit kann ich mir sicher etwas zusammenbasteln.
Was ist denn 'auto' für ein Datentyp? Ich kannte das nur als Präfix, um die Art der Speicherung anzugeben... also z.B. auto int a oder register int b ... so in dem Kontext halt...
-
Richi schrieb:
Sieht interessant aus^^ Damit kann ich mir sicher etwas zusammenbasteln.
Was ist denn 'auto' für ein Datentyp? Ich kannte das nur als Präfix, um die Art der Speicherung anzugeben... also z.B. auto int a oder register int b ... so in dem Kontext halt...Für einen C++0x Compiler (unterstützen praktisch alle schon) bedeutet es, dass er den Typ selbst aus dem Statement herleiten soll.
auto i = 1; // int auto str = "Hallo Welt"; //const char*etc. Ist halt vor allem bei Iteratortypen ein gewaltiger Vorteil, da man nicht die ewig langen Namen ausschreiben muss.
-
314159265358979 schrieb:
Ich habe doch nur ein & vergessen
Blöderweise aber an einer Stelle wo es der Compiler nicht bekritteln wird, und es erst zur Laufzeit kracht.
Bzw. wenn du Pech hast kracht es zur Laufzeit gar nicht, und es wird statt dessen irgendein Speicherbereich gelesen bzw. überschrieben, wo andere wichtige Daten stehen.
-
hustbaer schrieb:
314159265358979 schrieb:
Ich habe doch nur ein & vergessen
Blöderweise aber an einer Stelle wo es der Compiler nicht bekritteln wird, und es erst zur Laufzeit kracht.
Bzw. wenn du Pech hast kracht es zur Laufzeit gar nicht, und es wird statt dessen irgendein Speicherbereich gelesen bzw. überschrieben, wo andere wichtige Daten stehen.Deswegen 2x static_cast statt reinterpret_cast
-
Ich bin immer noch der Meinung, dass die static_cast Variante UB ist. Aber camper kennt sich da bestimmt besser aus. Und ja @hustbaer, da hast du natürlich Recht. Dafür eine eigene Funktion ist bei mehr als 1 Operation besser. Allerdings kann man dann gleich (zumindest bei std::vector) .data() verwenden.
-
314159265358979 schrieb:
Ich bin immer noch der Meinung, dass die static_cast Variante UB ist. Aber camper kennt sich da bestimmt besser aus. Und ja @hustbaer, da hast du natürlich Recht. Dafür eine eigene Funktion ist bei mehr als 1 Operation besser. Allerdings kann man dann gleich (zumindest bei std::vector) .data() verwenden.
Wieso sollte das UB sein?
static_cast castet beliebige Pointer von und zu void Pointern, was soll daran UB sein?
-
Ich meine irgendwann gelesen zu haben, dass lediglich ein cast von cv void* zu cv T* erlaubt ist, wenn der zuvor zu cv void* gecastete Zeiger ein Zeiger auf cv T* war.
-
314159265358979 schrieb:
Ich meine irgendwann gelesen zu haben, dass lediglich ein cast von cv void* zu cv T* erlaubt ist, wenn der zuvor zu cv void* gecastete Zeiger ein Zeiger auf cv T* war.
Bei POD-Typen wohl kaum relevant.

Sonst wäre der reinterpret mindestens genauso falsch.
-
Ethon schrieb:
Sonst wäre der reinterpret mindestens genauso falsch.
ist er auch. Wenn cast, dann static_cast. reinterpret_cast ist AFAIK immer implementation defined.
314159265358979 schrieb:
Ich meine irgendwann gelesen zu haben, dass lediglich ein cast von cv void* zu cv T* erlaubt ist, wenn der zuvor zu cv void* gecastete Zeiger ein Zeiger auf cv T* war.
Der cast, insbesodnere static_cast ist immer erlaubt. Der Zugriff danach aber nicht. Du darfst jeden Zeiger nach char* casten, den char Zeiger dann aber nicht verwenden. Allerdings glaube ich nicht, dass das fuer PODs UB ist, sondern nur implementation defined. (ist ja auch klar, der Standard sagt nichts ueber Byte Order aus)