Serializing a class with a pointer in C++ - c++

I want to serialize an object of type Person. I want to use it later on for data saving or even game saving. I know how to do it for primitives like int, char, bool, and even c-strings like char[].
The problem is, I want the string to be as big as it needs to rather than declaring a char array of size 256 and hoping no one enters something too big. I read that serializing a class with std::string as a member doesn't work because it has an internal pointer, but is there a way to serialize my class which has a char* as a member?
I realize Boost has a serialization library, but I'd like to do this without the need of external libraries, it seems like a good activity to try.
Here's my Person class:
class Person
{
private:
char* _fname;
char* _lname;
public:
Person();
Person(const char* fname, const char* lname);
Person(const string& fname, const string& lname);
string fname() const;
void fname(const char* fname);
void fname(const string& fname);
string lname() const;
void lname(const char* lname);
void lname(const string& lname);
};

First: Use std::string in your class it will make your life so much easier in the long run.
But this advice works for both std::string and char* (with minor tweaks that should be obvious).
Basically you want to serialize data of unknown size (at compile time). This means when you de-serialize the data you must either have a technique that tells you how long the data is (prefix the object with a size) or a way to find the end of the data (a termination marker).
A termination marker is easier for serialization. But harder for de-serialization (as you must seek forward to find the end). Also you must escape any occurrences of the termination marker within your object and the de-serialization must know about the escaping and remove it.
Thus because of this complications I prefer not to use a termination marker. As a result I prefix the object with a size. The cost of this is that I must encode the size of the object in a way that will not break.
So if we prefix an object with its size you can do this:
// Place a ':' between the string and the size.
// There must be a marker as >> will continue reading if
// fname contains a digit as its first character.
// I don;t like using a space as >> skips spaces if you are not carefull
// and it is hard to tell the start of the string if the first characters in fname
// are the space character.
std::cout << strlen(fname) << ":" << fname;
Then you can de-serialize like this:
size_t size;
char mark;
std::cint >> size >> mark;
if (!std::cin || mark != ':')
{ throw BadDataException;
}
result = new char[size+1](); // Note the () to zero fill the array.
std::cin.read(result, size)
Edit 1 (based on comments) Update: to use with string:
size_t size;
char mark;
std::cint >> size >> mark;
if (!std::cin || mark != ':')
{ throw BadDataException;
}
std::string result(' ', size); // Initialize string with enough space.
std::cin.read(&result[0], size) // Just read directly into the string
Edit 2 (based on commented)
Helper function to serialize a string
struct StringSerializer
{
std::string& value;
StringSerializer(std::string const& v):value(const_cast<std::string&>(v)){}
friend std::ostream& operator<<(std::ostream& stream, StringSerializer const& data)
{
stream << data.value.size() << ':' << data.value;
}
friend std::istream& operator>>(std::istream& stream, StringSerializer const& data)
{
std::size_t size;
char mark(' ');
stream >> size >> mark;
if (!stream || mark != ':')
{ stream.setstate(std::ios::badbit);
return stream;
}
data.value.resize(size);
stream.read(&data.value[0], size);
}
};
Serialize a Person
std::ostream& operator<<(std::ostream& stream, Person const& data)
{
return stream << StringSerializer(data.fname) << " "
<< StringSerializer(data.lname) << " "
<< data.age << "\n";
}
std::istream& operator>>(std::istream& stream, Person& data)
{
stream >> StringSerializer(data.fname)
>> StringSerializer(data.lname)
>> data.age;
std::string line;
std::getline(stream, line);
if (!line.empty())
{ stream.setstate(std::ios::badbit);
}
return stream;
}
Usage:
int main()
{
Person p;
std::cin >> p;
std::cout << p;
std::ofstream f("data");
f << p;
}

You can't serialize pointer, you need to serialize data pointer points to.
You'll need to serialize whole web of objects, starting from Person (or Game) and looking into each object, which is reachable from your start object.
When deserializing, you reading data from your storage, allocate memory for that data and use address of this freshly allocated memory as a member of Person/Game object

Pointer fields make it bit harder, but not impossible to serialize. If you don't want to use any of the serialization libraries, here is how you can do it.
You should determine the size of what is pointed to at the time of serialization (e.g. it may be of fixed size or it may be a C-string with null character at the end), then you can save a mark indicating that you're serializing an indirect object together with size and the actual content of the area pointed to.
When you stumble upon that mark during deserialization, you can allocate the right amount of memory, copy the object into it and store the pointer to the area in the deserialized object.

I recommend using a vector to encapsulate strings for
serialization.
#include <vector>
using namespace std;
map vector<unsigned char> cbuff;
inline cbuff vchFromString(const std::string &str) {
unsigned char *strbeg = (unsigned char*) str.c_str();
return cbuff(strbeg, strbeg + str.size());
}
inline std::string stringFromVch(const cbuff &vch) {
std::string res;
std::vector<unsigned char>::const_iterator vi = vch.begin();
while (vi != vch.end()) {
res += (char) (*vi);
vi++;
}
return res;
}
class Example
{
cbuff label;
Example(string labelIn)
{
SetLabel(labelIn);
}
IMPLEMENT_SERIALIZE
(
READWRITE(label);
)
void SetLabel(string labelIn)
{
label = vchFromString(labelIn);
}
string GetLabel()
{
return (stringFromVch(label));
}
};

Related

C++ Vectors with Array Elements?

I am doing a project in my class that requires the program to stores teams into a vector. So I would have something like this:
string bballteam;
vector<string> teamVector;
and then I want the user to input team names. So I can just prompt
cin >> bballTeam;
teamVector.push_back(bballTeam);
to store the user input into the vector. However, I would like for the user input to be an array so that I can store players (like a roster). What are some ways I can implement this?
I acknowledge that you can't have arrays in a vector.
What you need to know is the following:
If you have many different attributes for one Object, then a struct or class must be used. If you have for example a "Player", then you would put the attributes of "Player" in a struct. Like this:
struct Player {
std::string firstName{};
std::string name {};
unsigned int age{};
double score{};
};
Here you have one Type with many attributes or properties.
If you need many "Objects" of same Type, then you would store those in an "array" type, like a C-Style array, or an std::array, or, if you need some variable size, a std::vector.
So, again
Many attributes of different Type will be stored in a struct
Many obejects with the same Type will be stored in n array.
Of course all these structures or arrays can be nested. You may have an array of struct or a struct containing arrays.
This depends on how you want to abstract the reality. It is about "relations".
For the input: Objects of the same Type are entered in "loops", e.g. a for or a while loop.
Object attributes of different Type will be entered one after the other.
If you have a struct or class and want to enter all its attributes with a simple extraction (>>) operation, then you will observe that the existing extract operator >> does not know, how to work with your Type. That, because you defined a new Type, for example "Player". So we need to tell the compiler how to "Get" the data. And for that, we need to define the input functionality for the new Type. This is done with so called operator overloading. Sorry, you need to read about that. But, it is simple for input and for output. Within a struct you can normally use the following function signature:
struct Player {
std::string firstName{};
std::string name {};
unsigned int age{};
double score{};
// Extraction (input)
friend std::istream& operator >> (std::istream& is, Player& p);
// Insertion (output)
friend std::ostream& operator << (std::ostream& os, const Player& p);
};
You need to program the details of these functions.
So, next, the Team. Here you need many objects of the same Type, so many "Players".
You could write std::vector<Player> team{}; to define a team having many players.
And a complete example for the stuff learned so far:
#include <iostream>
#include <string>
#include <vector>
#include <iomanip>
struct Player {
std::string firstName{};
std::string name{};
unsigned int age{};
double score{};
// Extraction (input)
friend std::istream& operator >> (std::istream& is, Player& p) {
std::getline(is >> std::ws, p.firstName);
std::getline(is, p.name);
return is >> p.age >> p.score;
}
// Insertion (output)
friend std::ostream& operator << (std::ostream& os, const Player& p) {
return os << p.firstName << ' ' << p.name << ' ' << p.age << ' ' << p.score;
}
};
int main() {
// Define the team
std::vector<Player> team{};
// Give user instructions and get number of players for input
std::cout << "How many players do you want to enter?\n";
int numberOfPlayers{}; std::cin >> numberOfPlayers;
// Get the player data and add to team
for (int i = 0; i < numberOfPlayers; ++i) {
std::cout << "\n\nEnter player data << " << i + 1 << " One at a line : First name, name, age, score:\n";
Player player;
std::cin >> player;
team.push_back(player);
}
// Show complete team:
std::cout << "\n\nTeam:\n";
for (const Player& p : team)
std::cout << p << '\n';
}
Since you did not state your requirements very clearly, I cannot answer further. But you should have understood the basics now and can add further structs and arrays as needed.
Happy coding

How to serialize and deserialize an object into/from binary files manually?

I've been trying to write the below object into a file and got lot of trouble since strings are dynamically allocated.
class Student{
string name, email, telephoneNo;
int addmissionNo;
vector<string> issued_books;
public:
// There are some methods to initialize name, email, etc...
};
So I got to know that I can't just write into a file or read from a file an object with serialization. So I searched all over the internet about serialization with cpp and got to know about Boost library.But I wanted to do it my own (I know writing a library that already exist is not good, but I wanna see what's going on inside the code). So I got to know about overloading iostream << and >>. And I also know that serialize/deserialize into/from text.
But I want to serialize into a binary file. So I tried overloading ostream write and istream read. But then I got size issues(as write and read needs the sizeof the object it writes/reads).Then I also got to know about stringstream can help to serialize/deserialize objects into/from binary. But I don't how to do that?
So my real question is How to serialize and deserialize an object into/from binary files without third party libraries?
I have found a solution serialize and deserialize an object into/from a file. Here is an explaination
As I told you this is my class. And I have added two functions which overload the iostream's write and read.
class Student{
string name, email, telephoneNo;
int addmissionNo;
vector<string> issuedBooks;
public:
void create(); // initialize the private members
void show(); // showing details
// and some other functions as well...
// here I'm overloading the iostream's write and read
friend ostream& write(ostream& out, Student& obj);
friend istream& read(istream& in, Student& obj);
};
But I have also told you that I have tried this already. The problem I have was how to read without object member's size. So I made changes as below (Please read comments also).
// write: overload the standard library write function and return an ostream
// #param out: an ostream
// #param obj: a Student object
ostream& write(ostream& out, Student& obj){
// writing the objet's members one by one.
out.write(obj.name.c_str(), obj.name.length() + 1); // +1 for the terminating '\0'
out.write(obj.email.c_str(), obj.email.length() + 1);
out.write(obj.telephoneNo.c_str(), obj.telephoneNo.length() + 1);
out.write((char*)&obj.addmissionNo, sizeof(obj.addmissionNo)); // int are just cast into a char* and write into the object's member
// writing the vector of issued books
for (string& book: obj.issuedBooks){
out.write(book.c_str(), book.length() + 1);
}
return out;
}
// read: overload the standard library read function and return an istream
// #param in: an istream
// #param obj: a Student object
istream& read(istream& in, Student& obj){
// getline is used rather than read
// since getline reads a whole line and can be give a delim character
getline(in, obj.name, '\0'); // delimiting character is '\0'
getline(in, obj.email, '\0');
getline(in, obj.telephoneNo, '\0');
in.read((char*)&obj.addmissionNo, sizeof(int));
for (string& book: obj.issuedBooks){
getline(in, book, '\0');
}
return in;
}
As you can see I have wrote length+1 for the terminating '\0'. It is usefull in read function as we have used getline instead of read. So getline reads until the '\0'. So no need of a size. And here I'm writing and reading into/from a file.
void writeStudent(Student s, ofstream& f){
char ch; // flag for the loop
do{
s.create(); // making a student
f.open("students", ios::app | ios::binary); // the file to be written
write(f, s); // the overloaded function
f.close();
cout << "Do you want to add another record? (y/n): ";
cin >> ch;
cin.ignore();
} while(toupper(ch) == 'Y'); // loop until user stop adding records.
}
void readStudent(Student s, ifstream& f){
char ch; // flag for the loop
do{
f.open("students", ios::in | ios::binary);
cout << "Enter the account no of the student: ";
int no;
cin >> no;
int found = 0;
while (read(f, s)){
if (s.retAddmissionNo() == no){
found = 1;
s.show();
}
}
if (!found)
cout << "Account Not found!\n";
f.close();
cout << "Do you want another record? (y/n): ";
cin >> ch;
} while(toupper(ch) == 'Y');
}
That's how I solved my problem. If something wrong here please comment. Thank you!

Can an istream variable be a class variable

While developing a program in C++ using VS2010 , can I define
std::istream streamRead(ReadBuf&); // struct ReadBuf : public std::streambuf declared before
and use this streamRead in multiple functions in my program?
If not, can anyone suggest me how to read a stream using getline. I have to read the same stream from different functions.
Thank you in advance.
EDIT:
The struct declared in my header file is as below:
struct ReadBuf : public std::streambuf
{
ReadBuf(PBYTE s,size_t n)
{
setg((char*)s,(char*) s,( char*)s + n);
}
};
I have a buffer in memory and the input to my program is its pointer and size. Using the above structure, I copy it to a streambuffer. Now I have to read this streambuffer line by line. This is my requirement.
For example some of my functions are:
int GetSessionN(int session_id,SessionDetail &N_session);
int GetInstanceId(string header,SessionDetail &N_session);
int GetDriverDetails(string body_data,SessionDetail &N_session);
I have to read the first n lines from the stream using GetSessionN and then the successive n lines in the next function and so on.
This is where I initialise the object of ReadBuf. I am not able to initialize it globally.
int SetupLogReader::ProcessLogFile(PBYTE &mem_ptr, ULONG &size)
{
string read;
ReadBuf buf(mem_ptr, size);
istream streamRead(&buf);// Not able use StreamRead declared in header here.
}
you should not copy the stream when returning it in the function but reference it, i.e:
std::istream &streamRead(ReadBuf&){
if (_stream == null){
// create stream
_stream = [newly created stream];
}
return _stream;
}
Edit:
You could also use std::istringstream as it already provides the functionality you are looking for:
from istringstream manual:
std::string stringvalues = "line1\nline2";
std::istringstream iss (stringvalues);
for (int n=0; n<2; n++)
{
char val[256];
iss.getline(val, 256);
std::cout << val << '\n';
}

Reading/writing files to/from a struct/class

I'd like to read a file into a struct or class, but after some reading i've gathered that its not a good idea to do something like:
int MyClass::loadFile( const char *filePath ) {
ifstream file ( filePath, ios::in | ios::binary );
file.read ((char*)this, 18);
file.close();
return 0;
}
I'm guessing if i want to write a file from a struct/class this isn't kosher either:
void MyClass::writeFile( string fileName ) {
ofstream file( fileName, ofstream::binary );
file.write((char*)this, 18);
file.close();
}
It sounds like the reason i don't want to do this is because even if the data members of my struct add up to 18 bytes, some of them may be padded with extra bytes in memory. Is there a more correct/elegant way to get a file into a class/struct like this?
The preferred general technique is called serialization.
It is less brittle than a binary representation. But it has the overhead of needing to be interpreted. The standard types work well with serialization and you are encouraged to make your class serialize so that a class containing your class can easily be serialized.
class MyClass {
int x;
float y;
double z;
friend std::ostream& operator<<(std::ostream& s, MyClass const& data);
friend std::istream& operator>>(std::istream& s, MyClass& data);
};
std::ostream& operator<<(std::ostream& s, MyClass const& data)
{
// Something like this
// Be careful with strings (the input>> and output << are not symmetric unlike other types)
return str << data.x << " " << data.y << " " << data.z << " ";
}
// The read should be able to read the version printed using <<
std::istream& operator>>(std::istream& s, MyClass& data)
{
// Something like this
// Be careful with strings.
return str >> data.x >> data.y >> data.z;
}
Usage:
int main()
{
MyClass plop;
std::cout << plop; // write to a file
std::cin >> plop; // read from a file.
std::vector<MyClass> data;
// Read a file with multiple objects into a vector.
std::ifstream loadFrom("plop");
std::copy(std::istream_iterator<MyClass>(loadFrom), std::istream_iterator<MyClass>(),
std::back_inserter(data)
);
// Write a vector of objects to a file.
std::ofstream saveTo("Plip");
std::copy(data.begin(), data.end(), std::ostream_iterator<MyClass>(saveTo));
// Note: The stream iterators (std::istream_iterator) and (std::ostream_iterator)
// are templatized on your type. They use the stream operators (operator>>)
// and (operator<<) to read from the stream.
}
The answer is : there is no silver bullet to this problem.
One way you can eliminate the padding to ensure that the data members in your class is to use(in MSVC which you are using)
#pragma pack( push, 1 )
class YourClass {
// your data members here
int Data1;
char Data2;
// etc...
};
#pragma pack( pop )
The main usefulness of this approach is if your class matches a predefined format such as a bitmap header. If it is a general purpose class to represent a cat, dog, whatever then dont use this approach. Other thing if doing this is to make sure you know the length in bytes of the data types for your compiler, if your code is EVER going to be multi platform then you should use explicit sizes for the members such as __int32 etc.
If this is a general class, then in your save member, each value should be written explicitly. A tip to do this is to create or get from sourceforge or somewhere good code to help do this. Ideally, some code that allows the member to be named, I use something similar to :
SET_WRITE_DOUBLE( L"NameOfThing", DoubleMemberOfClass );
SET_WRITE_INT( L"NameOfThing2", IntMemberOfClass );
// and so on...
I created the code behind these macros, which I am not sharing for now but a clever person can create their own code to save named to stream in an unordered-set. This I have found is the perfect approach because if you add or subtract data members to your class, the save/load is not dependent on the binary representation and order of your save, as your class will doubtless evolve through time if you save sequentially this is a problem you will face.
I hope this helps.

Serializing a c++ class [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How to serialize in c++ ?
I have a class
Class Person
{
int age;
char *name;
char* Serialize()
{
//Need to convert age and name to char* eg:21Jeeva
}
void DeSerialize(char *data)
{
//Need to populate age and name from the data
}
};
In C# we can use MemoryStream,BinrayWriter/BinaryReader to achieve this. In c++ somewhere i found we can use iostream to achieve it. But couldnt able to get a proper example of it.
The purpose of the code is after calling serialize i am going to send the data over socket and in receiving end ill call DeSerialize to populate the members back.
You could take a look at Boost.Serialization. If you only need a simple text-serialization based on iostreams, you probably want to overload the stream extraction operators. For serialization this could look like this:
std::ostream & operator<<(std::ostream & stream, const Person & person) {
stream << person.age << " " << person.name;
return stream;
}
You have to make this function a friend of Person for this to work.
For deserialization, you would use this:
std::istream & operator>>(std::istream & stream, Person & person) {
stream >> person.age >> person.name;
return stream;
}
Using these, you can do the following:
Person fred;
fred.name = "Fred";
fred.age = 24;
std::stringstream buffer;
buffer << fred << std::endl;
Person fred_copy;
buffer >> fred;
You can find a small working example here.
Overloading these operators has the advantage that you can for instance use std::copy with an std::ostream_iterator to serialize an entire collection of Persons in one statement.
You can use following wrapper to convert any datatype to character stream:
template<class TYPE>
string ToChar (const TYPE& value)
{
stringstream ss;
ss << value;
return ss.str();
}
You can use the string object the way you want; like convert into char stream using c_str() or copy into array and so on.