Serializing struct containing char* - c++

I'm getting an error with serializing a char* string error C2228: left of '.serialize' must have class/struct/union I could use a std::string and then get a const char* from it. but I require the char* string.

The error message says it all, there's no support in boost serialization to serialize pointers to primitive types.
You can do something like this in the store code:
int len = strlen(string) + 1;
ar & len;
ar & boost::serialization::make_binary_object(string, len);
and in the load code:
int len;
ar & len;
string = new char[len]; //Don't forget to deallocate the old string
ar & boost::serialization::make_binary_object(string, len);

There is no way to serialize pointer to something in boost::serialization (I suspect, there is no actual way to do that too). Pointer is just a memory address, these memory addresses are generally specific for instance of object, and, what's really important, this address doesn't contain information where to stop the serialization.
You can't just say to your serializer: "Hey, take something out from this pointer and serialize this something. I don't care what size does it have, just do it..."
First and the optimal solution for your problem is wrapping your char* using std::string or your own string implementation. The second would mean writing special serializing routine for char* and, I suspect, will generally do the same as the first method does.

Try this:
struct Example
{
int i;
char c;
char * text; // Prefer std::string to char *
void Serialize(std::ostream& output)
{
output << i << "\n";
output << c << "\n";
// Output the length of the text member,
// followed by the actual text.
size_t text_length = 0;
if (text)
(
text_length = strlen(text);
}
output << text_length << "\n";
output << text << "\n";
};
void Input(std::istream& input)
{
input >> i;
input.ignore(1000, '\n'); // Eat any characters after the integer.
input >> c;
input.ignore(1000, '\n');
// Read the size of the text data.
size_t text_length = 0;
input >> text_length;
input.ignore(1000, '\n');
delete[] text; // Destroy previous contents, if any.
text = NULL;
if (text_length)
{
text = new char[text_length];
input.read(text, text_length);
}
};
Since pointers are not portable, the data must be written instead.
The text is known as a variable length field. Variable length fields are commonly output (serialized) in two data structures: length followed by data OR data followed by terminal character. Specifying the length first allows usage of block reading. With the latter data structure, the data must be read one unit at a time until the terminal character is read. Note: the latter data structure also implies that the terminal character cannot be part of the set of data items.
Some important issue to think about for serialization:
1. Use a format that is platform independent, such as ASCII text for numbers.
2. If a platform method is not available or allowed, define the exact specification for numbers, including Endianness and maximum length.
3. For floating point numbers, the specification should treat the components of a floating point number as individual numbers that have to abide by the specification for a number (i.e. exponent, magnitude and mantissa).
4. Prefer fixed length records to variable length records.
5. Prefer serializing to a buffer. Users of the object can then create a buffer of one or more objects and write the buffer as one block (using one operation). Likewise for input.
6. Prefer using a database to serializing. Although this may not be possible for networking, try every effort to have a database manage the data. The database may be able to send the data over the network.

Related

Can't write the struct to a file C++

I can't put the information I write on the struct to a file, this is the part of the code that I have the functions, on main I only have a switch to choose which functions to use. I'm sorry that the code is written in Portuguese, I can translate it if needed.
typedef struct Pessoa{
char nome[30];
int idade;
}pessoa;
FILE *arquivo;
pessoa p1[3];
int i=0;
void inserir(){
do{
cout<<"\nInsira o nome: ";
cin.clear();
cin.sync();
cin.getline(p1[i].nome,sizeof(p1[i].nome));
cout<<"\nInsira a idade: ";
cin.clear();
cin.sync();
cin>>p1[i].idade;
i++;
}while(i<3);
}//inserir
void carregar(){
fflush(stdin);
if((arquivo = fopen("contatos.dat","wb+")) !=NULL){
cout<<"It enters the write part"<<endl;//just checking if it enters the write part
fwrite(&p1,sizeof(p1),1,arquivo);
}
else{
cout<<"Erro: arquivo nao pode ser aberto";
}
}//carregar
Please note that writing entire objects (or structs in this case) to file in binary form is somewhat dangerous.
You should write each member of struct separetely to avoid padding (which may(or not) be one of reasons why your binary files become broken) and type size on different machines.
Keep in mind that the data should be formatted (serialized) in certain order (Little/Big Endian) as a target device might operate on different endianness.
The simplest way might be by dividing your data into smaller chunks (chars) by shifting bits and then writing it to a buffer.
#edited with example
A simple example:
//a function that serializes 32-bit unsigned int i to buffer buff
void uint32toLE(const uint32_t &i, uint8_t* buf)
{
//buf needs to be provided as pointer to char array,
//In my version I am incrementing pointer, therefore I suggest
//assigning address to a new pointer: char* temp=buf and replace
//buf++ with temp++ OR pass a copy of pointer
//buff++ represent post-incrementation,
// int* ptr; create pointer,
//*(ptr)=y - dereference ptr (use value not address) and assign value y
*(buf++)= (i&0x000000ff); //bit i AND 255
*(buf++)= (i&0x0000ff00) >> 8; //i AND 65280 then shift value right by 8 bits
*(buf++)= (i&0x00ff0000) >> 16; // i AND 16711680 then shift
*(buf++)= (i&0xff000000) >> 24; // i AND 4278190080 then shift too
}
The results of above is representation of 32-bit value in 8bit chunks. This somewhat protects our binary data from breaking (so long we are able to access 8-bit chars on our machine).
If you want to serialize entire objects, you need to provide functions that will serialize each individual member.
#edit
It's worth looking at serialization of binary data if you want to learn how to properly store data in that format (then you might consider XML serialization which is somewhat human readable). Be advised, it might be extremly confusing when you start working with serialization.
If you are not familiar with pointers and bit operations you should check them first, as they are basic for C/C++

Writing struct of vector to a binary file in c++

I have a struct and I would like to write it to a binary file (c++ / visual studio 2008).
The struct is:
struct DataItem
{
std::string tag;
std::vector<int> data_block;
DataItem(): data_block(1024 * 1024){}
};
I am filling tha data_block vector with random values:
DataItem createSampleData ()
{
DataItem data;
std::srand(std::time(NULL));
std::generate(data.data_block.begin(), data.data_block.end(), std::rand);
data.tag = "test";
return data;
}
And trying to write the struct to file:
void writeData (DataItem data, long fileName)
{
ostringstream ss;
ss << fileName;
string s(ss.str());
s += ".bin";
char szPathedFileName[MAX_PATH] = {0};
strcat(szPathedFileName,ROOT_DIR);
strcat(szPathedFileName,s.c_str());
ofstream f(szPathedFileName, ios::out | ios::binary | ios::app);
// ******* first I tried to write this way then one by one
//f.write(reinterpret_cast<char *>(&data), sizeof(data));
// *******************************************************
f.write(reinterpret_cast<const char *>(&data.tag), sizeof(data.tag));
f.write(reinterpret_cast<const char *>(&data.data_block), sizeof(data.data_block));
f.close();
}
And the main is:
int main()
{
DataItem data = createSampleData();
for (int i=0; i<5; i++) {
writeData(data,i);
}
}
So I expect a file size at least (1024 * 1024) * 4 (for vector)+ 48 (for tag) but it just writes the tag to the file and creates 1KB file to hard drive.
I can see the contents in while I'm debugging but it doesn't write it to file...
What's wrong with this code, why can't I write the strcut to vector to file? Is there a better/faster or probably efficient way to write it?
Do I have to serialize the data?
Thanks...
Casting a std::string to char * will not produce the result you expect. Neither will using sizeof on it. The same for a std::vector.
For the vector you need to use either the std::vector::data method, or using e.g. &data.data_block[0]. As for the size, use data.data_block.size() * sizeof(int).
Writing the string is another matter though, especially if it can be of variable length. You either have to write it as a fixed-length string, or write the length (in a fixed-size format) followed by the actual string, or write a terminator at the end of the string. To get a C-style pointer to the string use std::string::c_str.
Welcome to the merry world of C++ std::
Basically, vectors are meant to be used as opaque containers.
You can forget about reinterpret_cast right away.
Trying to shut the compiler up will allow you to create an executable, but it will produce silly results.
Basically, you can forget about most of the std::vector syntactic sugar that has to do with iterators, since your fstream will not access binary data through them (it would output a textual representation of your data).
But all is not lost.
You can access the vector underlying array using the newly (C++11) introduced .data() method, though that defeats the point of using an opaque type.
const int * raw_ptr = data.data_block.data();
that will gain you 100 points of cool factor instead of using the puny
const int * raw_ptr = &data.data_block.data[0];
You could also use the even more cryptic &data.data_block.front() for a cool factor bonus of 50 points.
You can then write your glob of ints in one go:
f.write (raw_ptr, sizeof (raw_ptr[0])*data.data_block.size());
Now if you want to do something really too simple, try this:
for (int i = 0 ; i != data.data_block.size() ; i++)
f.write (&data.data_block[i], sizeof (data.data_block[i]));
This will consume a few more microseconds, which will be lost in background noise since the disk I/O will take much more time to complete the write.
Totally not cool, though.

Use C++ strings in file handling

How to use C++ strings in file handling? I created a class that had C++ string as one of its private data members but that gave an error while reading from the file even if I am not manipulating with it at the moment and was initialised with default value in constructor. There is no problem while writing to the file. It works fine if I use C string instead but I don't want to. Is there a way to solve this?
class budget
{
float balance;
string due_name,loan_name; //string objects
int year,month;
float due_pay,loan_given;
public:
budget()
{
balance=0;
month=1;
due_name="NO BODY"; //default values
loan_name="SAFE";
year=0;
balance = 0;
due_pay=0;
loan_given=0;
}
.
.
.
};
void read_balance() //PROBLEM AFTER ENTERING THIS FUNCTION
{
system("cls");
budget b;
ifstream f1;
f1.open("balance.dat",ios::in|ios::binary);
while(f1.read((char*)&b,sizeof(b)))
{ b.show_data();
}
system("cls");
cout<<"No More Records To Display!!";
getch();
f1.close();
}
String is non-POD data-type. You cannot read/write from/in string by read/write functions.
basic_istream<charT,traits>& read(char_type* s, streamsize n);
30 Effects: Behaves as an unformatted input function (as described in
27.7.2.3, paragraph 1). After constructing a sentry object, if !good() calls setstate(failbit) which may throw an exception, and return.
Otherwise extracts characters and stores them into successive
locations of an array whose first element is designated by s.323
Characters are extracted and stored until either of the following
occurs: — n characters are stored; — end-of-file occurs on the input
sequence (in which case the function calls setstate(failbit | eofbit),
which may throw ios_base::failure (27.5.5.4)). 31 Returns: *this.
There is nothing about, how members of std::string placed. Look at, or use boost::serialiation. http://www.boost.org/doc/libs/1_50_0/libs/serialization/doc/index.html And of course you can write size of string and then write data and when read - read size, allocate array of this size, read data in this array and then create string. But use boost is better.
While reading the string members (due_name,loan_name) of your class budget your code literally fills them byte by byte. While it makes sense for floats and ints it won't work for strings.
Strings are designed to keep 'unlimited' amount of text, therefore their constructors, copy constructors, concatenations and so on must ensure to allocate the actual piece of memory to store the text and expand it if necessary (and delete upon destruction). Filling strings this way from disk will result in invalid pointers inside your string objects (not pointing to the actual memory which contains the text), actually no text will be actually read this way at all.
The easiest way to solve this is to not use C++ strings in that class. Work out the maximum length for each of the strings you will be storing, and make a char array that is one byte longer (to allow for the 0-terminator). Now you can read and write that class as binary without worrying about serialization etc.
If you don't want to do that, you cannot use iostream::read() on your class. You will need member functions that read/write to a stream. This is what serialization is about... But you don't need the complexity of boost. In basic terms, you'd do something like:
// Read with no error checking :-S
istream& budget::read( istream& s )
{
s.read( (char*)&balance, sizeof(balance) );
s.read( (char*)&year, sizeof(year) );
s.read( (char*)&month, sizeof(month) );
s.read( (char*)&due_pay, sizeof(due_pay) );
s.read( (char*)&loan_given, sizeof(loan_given) );
size_t length;
char *tempstr;
// Read due_name
s.read( (char*)&length, sizeof(length) );
tempstr = new char[length];
s.read( tempstr, length );
due_name.assign(tempstr, length);
delete [] tempstr;
// Read loan_name
s.read( (char*)&length, sizeof(length) );
tempstr = new char[length];
s.read( tempstr, length );
loan_name.assign(tempstr, length);
delete [] tempstr;
return s;
}
ostream& budget::write( ostream& s )
{
// etc...
}
Notice above that we've serialized the strings by writing a size value first, and then that many characters after.

How to read the standard istream buffer in c++?

I have the following problem. I have to implement a class that has an attribute that is a char pointer meant to point to the object's "code", as follows:
class foo{
private:
char* cod;
...
public:
foo();
void getVal();
...
}
So on, so forth. getVal() is a method that takes the code from the standard istream and fills in all the information, including the code. The thing is, the "code" that identifies the object can't be longer than a certain number of characters. This has to be done without using customized buffers for the method getVal(), so I can't do the following:
//suppose the maximum number of characters is 50
void foo::getVal()
{
char buffer[100];
cin >> buffer;
if (strlen(buffer) > 50) //I'm not sure this would work considering how the stream
of characters would be copied to buffer and how strlen
works, but suppose this tells me how long the stream of
characters was.
{
throw "Exception";
}
...
}
This is forbidden. I also can't use a customized istream, nor the boost library.
I thought I could find the place where istream keeps its information rather easily, but I can't find it. All I've found were mentions to other types of stream.
Can somebody tell me if this can be done or where the stream keeps its buffered information?
Thanks
yes using strlen would work definitely ..you can write a sample program
int main()
{
char buffer[10];
std::cout << "enter buffer:" ;
std::cin >>buffer;
if(strlen(buffer)>6)
std::cout << "size > 6";
getch();
}
for inputs greater than size 6 characters it will display size >6
uhm .... >> reads up to the first blank, while strlen counts up to the first null. They can be mixed if you know for sure no blanks are in the middle of string you're going to read and that there are no more than 100 consecutive characted. If not, you will overrun the buffer before throwing.
Also, accessing the buffer does not grant all the string to be already there (the string can go past the buffer space, requiring to partially read and refill the buffer...)
If blanks are separator, why not just read into an std::string, and react to its final state? All the dynamics above are already handled inside >> for std::string.
[EDIT after the comments below]
The only way to store a sequence of unknown size, is to dynamically allocate the space and make it grow as it is required to grow. This is, no more - no less, what sting and vector do.
Whether you use them or write your own code to allocate and reallocate where more space is required, doesn't change the substance.
I'm start thinking the only reason of those requirements is to see your capability in writing your own string class. So ... just write it:
declare a class holding a pointer a size and a capacity, allocate some space, track how much you store, and when no store is available, allocate another wider store, copy the old, destroy it, and adjust the data member accordingly.
Accessing directly the file buffer is not the way, since you don't control how the file buffer is filled in.
An istream uses a streambuf.
I find that www.cppreference.com is a pretty good place for quick C++ references. You can go there to see how to use a streambuf or its derivative filebuf.

C++ writing an object to a file then later reading it in? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How to serialize in c++?
How to implement serialization in C++
I've been toying around with C++ more and more these days and have only had a couple experiences with ofstream at this point. Most of said experiences have been doing simple file output of variables and reading them back in with ifstream. What I haven't done is anything with objects.
Let's assume that I have an object that is being written to frequently (say a game, and the object is the character) Every time the character is hit, the hp is re-written, every time they defeat an enemy they are gaining experience.... my basic idea is to write a simple text-based dungeon crawling game. But how am I going to make some kind of an autosave file?? Do I just write out every attribute of my object to a file individually and then move onto bigger and better from there? If I had to do it right now that's how I'd go about doing it, but I can't help like feeling that there's an easier way than that....
Anyone care to help me output the contents of an entire object(and it's respective attributes) to a file?
You could just write the object to a file by copying it's contents in memory.
But then you hit the tricky bits! You can't copy any items that are pointers to memory, because when you load them back in they don't own that memory. That means copying things like std::string which may internally contain their own memory allocation is also tricky.
Then even with standard types there are issues if you plan to read them on a different machine with a different number of bits, or a different byte order.
The process is called serialisation - there are a few standard techniques to make it easier.
Take a look at this code :
//! Struct for a 2d marker
struct Marker2d{
double x; //!< x coordinate of marker in calibration phantom
double y; //!< y coordinate of marker in calibration phantom
int id; //!< some unique id (used for sequence id as well as for assigned 3d id)
int code; //!< type of marker (small = 0, large = 1)
float size; //!< real size of marker in 2D image (in pixel)
double distanceToNearest; //!< distance to nearest other marker
/**
* Overloaded stream insertion operator. Abbreviation for the output of 2d marker.
\param output_out A reference to an std::ostream instance indicating the output stream
\param marker_in A constant Marker2d reference indicating the 2d marker that we want to output
\return a std::ostream reference containing the new output data
*/
friend std::ostream & operator<<(std::ostream & output_out, const Marker2d & marker_in)
{
return output_out<< std::fixed << std::setprecision(15) <<marker_in.x<<"\t"<<marker_in.y<<"\t"<<marker_in.id<<"\t"
<<marker_in.code<<"\t"<<marker_in.size<<"\t"<<marker_in.distanceToNearest;
}
/**
* Overloaded stream extraction operator.
\param s_in A reference to an std::istream instance indicating the input stream
\param marker_out A Marker2d reference indicating the 2d marker that will have its data members populated
\return a std::istream reference indicating the input stream
*/
friend std::istream& operator>>(std::istream& s_in, Marker2d & marker_out)
{
s_in >> marker_out.x >> marker_out.y >> marker_out.id >> marker_out.code >> marker_out.size >> marker_out.distanceToNearest;
return s_in;
}
};
This is a simple struct with overloaded >> and << operators. This allows you to output to a file like myOfstreamFile << obj; And read the other way around.
If you have say, a thousand of objects stored in a file you can simply put them in a container like this :
std::vector<Marker2d> myMarkers;
std::ifstream f( fileName_in.c_str() );
if(!f)
throw std::exception(std::string("AX.Algorithms.ComputeAssignmentsTest::getMarkersFromFile - Could not open file : " + fileName_in + " for reading!").c_str());
//cool one liner to read objects from file...
std::copy(std::istream_iterator<AX::Calibration::Marker2d>(f), std::istream_iterator<AX::Calibration::Marker2d>(), std::back_inserter(myMarkers));
Of course you could provide other forms of input and output e.g. save to .xml format and parse it as a dom tree etc. This is just a sample.
EDIT : This will work for relatively simple objects. Look at serialization if you need something more complex
Search the web and SO for "serialization". There are some bumps to watch out for: floating point, endianess and variable length fields (strings).
Good luck!
Behind your simple question hides a complex theme. Please take a look to boost::serialization (here for instance). Any time spent to learning boost it's very rewarding.
There's a nice library in Boost called Boost.Serialize. If you're looking for performance, it is probably not the right choice, but looking at the source code and usage may give you some ideas. Serialization/Deserialization can be tricky, especially when you have lots of nested components. JSON is a very nice format for serializing general objects.
Here's a hacky trick that will likely get coders here to rip out their hair and scream at me (this only works for static objects and not ones that use dynamic memory allocation):
class TestA
{
private:
long Numerics;
char StaticArray[10];
int Data[3];
public:
TestA(){Numerics = 10; strcpy(StaticArray,"Input data"); Data[0] = 100; Data[1] = 200; Data[2] = 300;}
void Test(){Numerics = 1000; strcpy(StaticArray,"Data input"); Data[0] = 300; Data[1] = 200; Data[2] = 100;}
void Print()
{
printf("Numerics is: %ld\nStaticArray is: %s\n%d %d %d\n",Numerics,StaticArray,Data[0],Data[1],Data[2]);
}
};
int main()
{
TestA Test;
FILE *File = fopen("File.txt","wb");
Test.Test();
Test.Print();
fwrite((char *)&Test,sizeof(Test),1,File); //Treats the object as if it is a char array
fclose(File);
TestA Test2;
File = fopen("File.txt","rb");
fread((char *)&Test2,sizeof(Test2),1,File); //Treats the object as if it is a char array
Test2.Print();
fclose(File);
return 0;
}
Which results in:
Numerics is: 1000
Static array is: Data input
300 200 100
Numerics is: 1000
Static array is: Data input
300 200 100
Opening the file reveals the written data:
è Data input w, È d
The above trick allows for easy conversion into a byte-based format. Naturally this is hacky, but classes (or objects) should be expected to supply their own object-to-char array conversion process.