c++ - usar - tipos de serializacion
Serializar una clase que contiene una cadena std:: (7)
No soy un experto en C ++ pero he serializado cosas un par de veces en el pasado. Desafortunadamente, esta vez estoy tratando de serializar una clase que contiene una std :: cadena, que entiendo es como serializar un puntero.
Puedo escribir la clase en un archivo y volver a leerla. Todos los campos int son correctos, pero el campo std :: string da un error de "dirección fuera de límites", presumiblemente porque apunta a datos que ya no están allí.
¿Hay una solución estándar para esto? No quiero volver a las matrices de caracteres, pero al menos sé que funcionan en esta situación. Puedo proporcionar el código si es necesario, pero espero haber explicado bien mi problema.
Estoy serializando lanzando la clase a un char * y escribiéndola en un archivo con fstream. Leer, por supuesto, es todo lo contrario.
¿Por qué no solo algo parecido a:
std::ofstream ofs;
...
ofs << my_str;
y entonces:
std::ifstream ifs;
...
ifs >> my_str;
El método de serialización más sencillo para cadenas u otros blobs con tamaño variable es serializar primero el tamaño a medida que serializa los enteros, luego simplemente copie el contenido a la secuencia de salida.
Al leer, primero lea el tamaño, luego asigne la cadena y luego rellene el número correcto de bytes de la secuencia.
Una alternativa es usar un delimitador y un escape, pero requiere más código y es más lento tanto en la serialización como en la deserialización (sin embargo, el resultado puede mantenerse legible).
No he codificado C ++ en mucho tiempo, pero quizás podrías serializar una matriz de caracteres.
Luego, cuando abras tu archivo, tu string
simplemente apuntará a la matriz.
Solo una idea.
Simplemente escribir los contenidos binarios de un objeto en un archivo no solo no es imposible de importar, sino que, como ha reconocido, no funciona con los datos del puntero. Básicamente tiene dos opciones: o bien escribe una biblioteca de serialización real, que maneja std :: cadenas correctamente, por ejemplo, usando c_str () para dar salida a la cadena real al archivo, o utiliza la biblioteca de serialización de impulso excelente. Si es posible, recomendaría este último, luego puede serializar con un código simple como este:
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/string.hpp>
class A {
private:
std::string s;
public:
template<class Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar & s;
}
};
Aquí, la función serialize
funciona para serializar y deserializar los datos, dependiendo de cómo lo llame. Consulte la documentación para más información.
Tendrá que utilizar un método de serialización más complicado que convertir una clase en un char*
y escribirlo en un archivo si su clase contiene datos exógenos ( string
hace). Y tiene razón acerca de por qué está obteniendo un error de segmentación.
Haría una función de miembro que tomaría una fstream
y leería los datos de ella, así como una función inversa que tomaría un fstream
y escribiría sus contenidos para que se restauren más tarde, así:
class MyClass {
pubic:
MyClass() : str() { }
void serialize(ostream& out) {
out << str;
}
void restore(istream& in) {
in >> str;
}
string& data() const { return str; }
private:
string str;
};
MyClass c;
c.serialize(output);
// later
c.restore(input);
También puede definir operator<<
y operator>>
para trabajar con istream
y istream
para serializar y restaurar su clase también si desea ese azúcar sintáctico.
/*!
* reads binary data into the string.
* @status : OK.
*/
class UReadBinaryString
{
static std::string read(std::istream &is, uint32_t size)
{
std::string returnStr;
if(size > 0)
{
CWrapPtr<char> buff(new char[size]); // custom smart pointer
is.read(reinterpret_cast<char*>(buff.m_obj), size);
returnStr.assign(buff.m_obj, size);
}
return returnStr;
}
};
class objHeader
{
public:
std::string m_ID;
// serialize
std::ostream &operator << (std::ostream &os)
{
uint32_t size = (m_ID.length());
os.write(reinterpret_cast<char*>(&size), sizeof(uint32_t));
os.write(m_ID.c_str(), size);
return os;
}
// de-serialize
std::istream &operator >> (std::istream &is)
{
uint32_t size;
is.read(reinterpret_cast<char*>(&size), sizeof(uint32_t));
m_ID = UReadBinaryString::read(is, size);
return is;
}
};
Estoy serializando lanzando la clase a un char * y escribiéndola en un archivo con fstream. Leer, por supuesto, es todo lo contrario.
Desafortunadamente, esto solo funciona mientras no haya indicadores involucrados. Es posible que desee dar a sus clases void MyClass::serialize(std::ostream)
y void MyClass::deserialize(std::ifstream)
y llamarlas. Para este caso, querrías
std::ostream& MyClass::serialize(std::ostream &out) const {
out << height;
out << '','' //number seperator
out << width;
out << '','' //number seperator
out << name.size(); //serialize size of string
out << '','' //number seperator
out << name; //serialize characters of string
return out;
}
std::istream& MyClass::deserialize(std::istream &in) {
if (in) {
int len=0;
char comma;
in >> height;
in >> comma; //read in the seperator
in >> width;
in >> comma; //read in the seperator
in >> len; //deserialize size of string
in >> comma; //read in the seperator
if (in && len) {
std::vector<char> tmp(len);
in.read(tmp.data() , len); //deserialize characters of string
name.assign(tmp.data(), len);
}
}
return in;
}
Es posible que también desee sobrecargar los operadores de flujo para un uso más fácil.
std::ostream &operator<<(std::ostream& out, const MyClass &obj)
{obj.serialize(out); return out;}
std::istream &operator>>(std::istream& in, MyClass &obj)
{obj.deserialize(in); return in;}