Jump to content
  • Advertisement
Sign in to follow this  
icecubeflower

binary I/O

This topic is 3410 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have a structure called an Anibox. It just plays cartoons. All it contains is the number of images (int isize), the type of Anibox (int itype), where to start looping (int iloop), and an array of integers which are the names of images loaded into OpenGL (int ipics[isize]). That's it. Just those four things. Three ints and one array of ints. (oh and also it contains the string name which is the name of itself and the file it will write to) I want to write the Anibox to a file and be able to retrieve it later. I want to write the names of the images though, not the OpenGL number which of course will be useless later. I have a separate class that holds the names of all the images loaded into OpenGL and the int that OpenGL named it. In this case, it's *picbox. picbox has a function called: string NameGrab2(int); which will return the name of the image if you send it the OpenGL name (int). So I did this:
void Anibox::Writebox()
{
   ofstream outfile;
   int ix;
   int length;
   string saver;
   char *copy;

   outfile.open(name.c_str(),ios::out|ios::binary);
   outfile.write(reinterpret_cast<char *>(&itype),sizeof(int));
   outfile.write(reinterpret_cast<char *>(&isize),sizeof(int));
   outfile.write(reinterpret_cast<char *>(&iloop),sizeof(int));
   for(ix=0;ix<isize;ix++)
   {
      saver=picbox->NameGrab2(ipics[ix]);
      length=saver.length();
      copy=new char[length+1];
      strcpy(copy,saver.c_str());
      outfile.write(reinterpret_cast<char *>(&length),sizeof(int));
      outfile.write(copy,length);
      delete[] copy;
   }
   outfile.close();
}


It works! It does exactly what I want it to do. (I think) But isn't there a better way to do it? Is there no way I can just use string.length() and the string itself directly in outfile.write? Dynamically allocating a char array just seems kind of silly.

Share this post


Link to post
Share on other sites
Advertisement
...oh yeah. I guess that is a char*. What am I smoking. I don't remember why I made a separate int to record length() either. Man.

Oh wait. I have to record string.length() into an int because write() wants everything cast as a char*. I mean this doesn't make any sense does it?:
outfile.write(reinterpret_cast<char *>(&picbox->NameGrab2(ipics[ix]).length()),sizeof(int));

I don't think you can reference a return value.

Share this post


Link to post
Share on other sites
Alright, here it is, new and improved. I don't see any way to get around copying length() into int length.


void Anibox::Writebox()
{
ofstream outfile;
int ix;
int length;

outfile.open(name.c_str(),ios::out|ios::binary);
outfile.write(reinterpret_cast<char *>(&itype),sizeof(int));
outfile.write(reinterpret_cast<char *>(&isize),sizeof(int));
outfile.write(reinterpret_cast<char *>(&iloop),sizeof(int));
for(ix=0;ix<isize;ix++)
{
length=picbox->NameGrab2(ipics[ix]).length();
outfile.write(reinterpret_cast<char *>(&length),sizeof(int));
outfile.write(picbox->NameGrab2(ipics[ix]).c_str(),length);
}
outfile.close();
}

Share this post


Link to post
Share on other sites
Okay I wrote this as the opposite of above. It reads from one of the files I outputted with the function above and recontructs the Anibox:

bool Anibox::Spawnbox(string anifile, ImageVault *picbox)
{
ifstream infile;
int ix;
string strtemp;
int length;
char *temp;
bool breturn=true;

infile.open(anifile.c_str(),ios::in|ios::binary);
if(infile.fail()==false)
{
if(ipics!=NULL)
delete[] ipics;

name=anifile;
infile.read(reinterpret_cast<char *>(&itype),sizeof(int));
infile.read(reinterpret_cast<char *>(&isize),sizeof(int));
infile.read(reinterpret_cast<char *>(&iloop),sizeof(int));
ipics=new GLuint[isize];

for(ix=0;ix<isize;ix++)
{
infile.read(reinterpret_cast<char *>(&length),sizeof(int));
temp=new char[length+1];
temp[length]='\0';
infile.read(temp,length);
strtemp="Data/Images/";
strtemp+=temp;
picbox->ImageLoader(strtemp,ipics[ix]);
delete[] temp;
}
}
else
{
breturn=false;
}
return breturn;
}



Again, it works perfectly. But I don't like it. Is there a way to avoid the dynamic char array again? I'm gonna read about strings but I don't know if there's a c_str() trick for reading into strings.

Share this post


Link to post
Share on other sites
You're really terribly misusing stream objects here.

For your serialization (writing class to a sequence of bytes):

void Anibox::Writebox()
{
ofstream outfile(name.c_str(), ios::out | ios::binary);
outfile << itype << L"\n";
outfile << isize << L"\n";
outfile << iloop << L"\n";
for(int i = 0; i < isize; ++i)
{
std::string thename = picbox->NameGrab2(ipics);
outfile << thename << L"\n";
}
}






And to deserialize:

bool Anibox::Spawnbox(string anifile, ImageVault *picbox)
{
ifstream infile(anifile.c_str(), ios::in | ios::binary);

if(infile.fail())
return false;

delete[] ipics;

name=anifile;
infile >> itype;
infile >> isize;
infile >> iloop;

ipics = new GLuint[isize];

for(int i = 0; i < isize; ++i)
{
std::string temp;
getline(infile, temp);
temp = L"Data/Images/" + temp;
picbox->ImageLoader(temp, ipics);
}

return true;
}





Note that we have no need to store the name string's length anymore. Also observe that we need no temporary buffers; in general if you are writing C++ and you start using char* for anything, you are probably making a huge mistake. std::string is incredibly well supported in the C++ standard library, and chances are you can use it directly for better performance and better safety.

You may also want to think of a way to avoid hard coding that "Data/Images/" path into your Anibox code. Maybe ImageLoader() should add it internally?

Share this post


Link to post
Share on other sites
What are all those capital L's before your string literals? I've never seen those before.

I read that when doing binary I/O I was supposed to use read() and write()

I don't think there's any way for string to use read(). I guess I'll try your way.

Share this post


Link to post
Share on other sites
Here's a sample of what your write function created. I thought I tried using the << >> operators with ints but I guess I didn't. So I guess it works this way.

00x80628c4100x80628c400x80628c4water1.jpg0x80628c4water2.jpg0x80628c4water3.jpg
0x80628c4water4.jpg0x80628c4water5.jpg0x80628c4water6.jpg0x80628c4water7.jpg
0x80628c4water8.jpg0x80628c4water9.jpg0x80628c4water10.jpg0x80628c4

So is this THE WAY to do it? Serialize. ios::binary. << >> operators. '\n' as delimiter and use getline()? And every other way is the dumb way? Just making sure this is the last time I have to rewrite this.

Does anybody know what that capital L is?

Share this post


Link to post
Share on other sites
I can't make it work Apoch's way. It seems like the serializing part is working. But when I try to deserialize it doesn't work. It opens the infile okay. But then it runs these three:

infile >> itype;
infile >> isize;
infile >> iloop;

And afterwards all three are still zero. It's not reading anything in. I don't get it.

*******

I tried just declaring a string and reading >> the entire file into a string and that worked. So it's reading the file. It's just not reading the serial info into ints for some reason.

Share this post


Link to post
Share on other sites
This page seems pretty sure that << and >> are for text format and read() write() are for binary format but it doesn't say why:

http://www.parashift.com/c++-faq-lite/serialization.html#faq-36.3

I'm starting to wonder if maybe I should go with text format, anyway. I just realized all I have to do is put spaces between ints and then I can use << >>. ...I didn't know that. Spaces. That's so easy. Yeah I'll probably be going with text format. This isn't a massive project and every struct makes it's own little files. I can probably master serialization some other day.

That still bugs me why I couldn't deserialize those ints. I can't figure it out.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!