Lösung des Problems
Ich würde eine neue Ebene der Indirektion einführen, dh Funktionen from_binary
und to_binary
, und Ihre Load
und Store
in Bezug auf diese implementieren:
template <class C>
bool Load(const char fileName[], C& obj) {
if (ifstream in{fileName, ios::in | ios::binary}) {
from_binary(in, obj);
return true;
}
return false;
}
template <class T>
bool Save(const char fileName[], T obj) {
if (ofstream out{fileName, ios::out | ios::binary}) {
to_binary(out, obj);
return true;
}
return false;
}
Für POD-Datentypen wird from_binary
and to_binary
genau das tun, was Sie bereits in Load
/ getan haben Store
(achten Sie jedoch darauf: Zeiger sind PODs, aber das Speichern einer Adresse ist ziemlich bedeutungslos):
template <class T, typename = enable_if_t<is_pod_v<T>>>
void from_binary(ifstream& in, T& obj) {
in.read(reinterpret_cast<char*>(addressof(obj)), sizeof(obj));
}
template <class T, typename = enable_if_t<is_pod_v<T>>>
void to_binary(ofstream& out, T const& obj) {
out.write(reinterpret_cast<char const*>(addressof(obj)), sizeof(obj));
}
Wie in den Kommentaren erwähnt, std::string
handelt es sich nicht um einen POD-Typ. Ich werde es serialisieren, indem ich die Zeichenanzahl und dann die tatsächlichen Zeichen speichere:
void from_binary(ifstream& in, string& str) {
std::size_t stringSize{0};
from_binary(in, stringSize);
str.reserve(stringSize);
for (size_t i = 0; i!= stringSize; ++i) {
char ch{};
in.read(&ch, 1);
str.push_back(ch);
}
}
void to_binary(ofstream& out, string const& str) {
auto const stringSize = str.size();
to_binary(out, stringSize);
auto const* cStr = str.c_str();
out.write(cStr, stringSize);
}
Außerdem werde ich ein Array serialisieren/deserialisieren, indem ich to_binary
/ from_binary
für jedes Element des Arrays aufrufe:
template <class T, size_t N>
void from_binary(ifstream& in, T (&obj)[N]) {
for (auto& elem: obj) from_binary(in, elem);
}
template <class T, size_t N>
void to_binary(ofstream& out, T const (&obj)[N]) {
for (auto const& elem: obj) to_binary(out, elem);
}
Die obigen Funktionen reichen aus, um sie zu implementieren from_binary
und to_binary
für Ihre Klassen Contact
und:Data
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
template <class T, typename = enable_if_t<is_pod_v<T>>>
void from_binary(ifstream& in, T& obj) {
in.read(reinterpret_cast<char*>(addressof(obj)), sizeof(obj));
}
template <class T, typename = enable_if_t<is_pod_v<T>>>
void to_binary(ofstream& out, T const& obj) {
out.write(reinterpret_cast<char const*>(addressof(obj)), sizeof(obj));
}
void from_binary(ifstream& in, string& str) {
std::size_t stringSize{0};
from_binary(in, stringSize);
str.reserve(stringSize);
for (size_t i = 0; i!= stringSize; ++i) {
char ch{};
in.read(&ch, 1);
str.push_back(ch);
}
}
void to_binary(ofstream& out, string const& str) {
auto const stringSize = str.size();
to_binary(out, stringSize);
auto const* cStr = str.c_str();
out.write(cStr, stringSize);
}
template <class T, size_t N>
void from_binary(ifstream& in, T (&obj)[N]) {
for (auto& elem: obj) from_binary(in, elem);
}
template <class T, size_t N>
void to_binary(ofstream& out, T const (&obj)[N]) {
for (auto const& elem: obj) to_binary(out, elem);
}
template <class C>
bool Load(const char fileName[], C& obj) {
if (ifstream in{fileName, ios::in | ios::binary}) {
from_binary(in, obj);
return true;
}
return false;
}
template <class T>
bool Save(const char fileName[], T obj) {
if (ofstream out{fileName, ios::out | ios::binary}) {
to_binary(out, obj);
return true;
}
return false;
}
class Contact {
public:
int CompareTo(Contact obj) { return 1; }
string ss;
int rollNum;
};
void from_binary(ifstream& in, Contact& obj) {
from_binary(in, obj.ss);
from_binary(in, obj.rollNum);
}
void to_binary(ofstream& out, Contact const& obj) {
to_binary(out, obj.ss);
to_binary(out, obj.rollNum);
}
class Data {
public:
Data() {}
Contact arr[10];
};
void from_binary(ifstream& in, Data& obj) { from_binary(in, obj.arr); }
void to_binary(ofstream& out, Data const& obj) { to_binary(out, obj.arr); }
int main() {
const char fileName[] = "ContactMG.dat";
{
Data data;
auto const contactCount = sizeof(data.arr) / sizeof(data.arr[0]);
for (size_t c = 0; c!= contactCount; ++c) {
data.arr[c].ss = "some name " + to_string(c);
data.arr[c].rollNum = c;
}
Save(fileName, data);
}
{
Data data;
Load(fileName, data);
for (auto const& contact: data.arr)
cout << "Contact: rollNum=" << contact.rollNum
<< ", ss=" << contact.ss << '\n';
}
}
Ausgabe:
Contact: rollNum=0, ss=some name 0
Contact: rollNum=1, ss=some name 1
Contact: rollNum=2, ss=some name 2
Contact: rollNum=3, ss=some name 3
Contact: rollNum=4, ss=some name 4
Contact: rollNum=5, ss=some name 5
Contact: rollNum=6, ss=some name 6
Contact: rollNum=7, ss=some name 7
Contact: rollNum=8, ss=some name 8
Contact: rollNum=9, ss=some name 9
Obwohl dies Ihr spezielles Problem lösen kann, wächst die Anzahl der Überladungen, die Sie benötigen from_binary
, to_binary
sehr schnell, wenn Ihr Projekt wächst. Ich würde also auf jeden Fall prüfen, ob es eine umfassendere (und gut getestete) Bibliothekslösung gibt.
Keine Kommentare:
Kommentar veröffentlichen