• Advertisement

Archived

This topic is now archived and is closed to further replies.

Struct Crazyness!!

This topic is 5805 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

Ok here''s a really n00bish question... Sometimes i see people define structs and then just copy data from files straight into them... for example: fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr); now i understand what this code does... it just reads the file and copies what you''ve read to the location bitmapFileHeader which just so happens to be a struct. However, what if the data in teh struct was organised in a different way? Is it posisble to do this with classes? Does this mean that data structures should really be done with classes to make loading/saving easier? thanks -jonnii

Share this post


Link to post
Share on other sites
Advertisement
If the data in the struct was organized in a different way, there would indeed be problems.

I''m fairly sure that...
struct mystruct{
int a;
char b[21];
};
Is not the same thing as:
struct mystruct{
char b[21];
int a;
};
You can refer to the members of either of these in the same way, but they would be written to a file differently.
As long as you chose one and stuck with it, you''d be fine, but if someone else saved a file with the first definition, and you loaded into the second definition, there would be problems.

As for classes, I''m not sure, since I''ve never done it, but I think that every instance of a class contains information about it''s methods in addition to the actual data members, so attempting to directly save a class instance probably wouldn''t be a good idea. For one, it would waste space, and two, different compilers might organize the class data differently.

Share this post


Link to post
Share on other sites
You can do this only if the representation of the struct in the file and in memory match at binary level. That means that the order of the members and the packing must be the same. You can do this with simple classes (in C++ a struct is a class with public members by default) but the technique is pretty error prone.

Forever trusting who we are
And nothing else matters
- Metallica

Share this post


Link to post
Share on other sites
You need to be very sure of what you are doing when you write out or read a struct in all at once.

1) The order of elements does matter. It needs to be declared in exactly the right order.

2) Pointers will contain garbage, because saving / loading in this manner saves only the address that the pointer points to, not the actual data. For classes / structs with pointers, you need to dereference and save the data pointed to by each pointer individually.

3) The padding of the structure matters. Your compiler trys to align data on 4, 8, or 16 byte boundaries to make memory access faster. You can (and should if you are read / writing structs) control the packing with #pragma directives (consult your compiler documentation).

4) A class stored is memory in the same way as a struct (ie indivdual members sequentially in memory in the order they are declared with optional padding). Beware of saving, and especially of loading anything with virtual functions. If you load, it will obliterate the vtable and invalidate the address of all the virtual functions, causing serious problems.

To summarize: only write / read the whole struct in if it is a simple struct with no pointers and no virtual functions and you know it is not padded (or was saved with the same amount of padding).

[edited by - invective on June 2, 2002 6:46:30 AM]

Share this post


Link to post
Share on other sites
quote:
Original post by jonnii
Ok here''s a really n00bish question...

Sometimes i see people define structs and then just copy data from files straight into them... for example:

fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);



Consider any struct you have created and filled with some valid values. If you just take it and write it to a file and, at a later time, just load it into an instance of the same struct, the contents would be identical. Same applies to file haeaders. Any .bmp will be structured the same way. Ever. In the beginning there is stored some information regarding the contained data and, because every such file is strucutured identically, you just load the first bytes to a BITMAPFILEHEADER and can be sure to have all the necessary information in the right variables.


quote:
Original post by jonnii
now i understand what this code does... it just reads the file and copies what you''ve read to the location bitmapFileHeader which just so happens to be a struct.


It does not read the whole file, but sizeof (BITMAPFILEHEADER) bytes. And because every bitmap uses the same structure to be created, sizeof (BITMAPFILEHEADER) will always cover the whole bitmap file header.


quote:
Original post by jonnii
However, what if the data in teh struct was organised in a different way?


Would only be a problem if the writing and re-reading structs would differ.

quote:
Original post by jonnii
Is it posisble to do this with classes?


Saving/Loading or re-organizing?

Saving/Loading should be possible as long the class does not have a single virtual function, because it gets larger then and as soon as you re-load it you are likely to corrupt this class instance by overriding the vtable pointer.

Re-organizing is ok with your own file formats, of course. But with standard file formats you''ll have to use standard file header specification.

If you mean something like that, that''s possible of course:


  
class BmpLoader
{
public:
BmpLoader (const std::string& fileName)
{
/* locate file */


BITMAPFILEHEADER bmh;


/* read in the header */


_bmSize = bmh.bfSize;


/* ... */
}


private:
dword _bmSize;
/* ... */

}


quote:
Original post by jonnii
Does this mean that data structures should really be done with classes to make loading/saving easier?


Writing classes that care about loading / saving is quite common and very convenient, but for the actual file header data you should use the structures that were used to create the file.

hth

Share this post


Link to post
Share on other sites
To add to the previous posts, you have to watch out for packing, vtables, and multiple inheritance. Packing will insert extra space between data members to align them properly, and if that padding is not present in the file you're trying to read, you'll be in trouble. If your class/struct has virtual functions, part of the object will be a virtual table pointer (it might be before the first data member), so you have to calculate the struct size without the vtable pointer and use the address of the first data member in the struct, not the struct itself. Multiple inheritance further complicates things, because which base class comes first and which one comes last may be compiler-dependent, and if you have multiple inheritance with virtual functions you might end up with a very big mess. So for structs/classes that are to be saved to and restored from files, don't use virtual functions or multiple inheritance and you should be fine.

quote:
Original post by Gabriel Fleseriu
You can do this with simple classes


Please define a "simple" class? Another one of those struct != class posts. Didn't you say:
quote:

in C++ a struct is a class with public members by default


It doesn't matter if you have a struct or a class as long as the above mentioned constraints hold true.
quote:

but the technique is pretty error prone.


What exactly are you referring to? Reading classes from file with fread is no more error prone than doing so with structs.

Edit: that's already four previous posts.

[edited by - IndirectX on June 2, 2002 6:56:01 AM]

Share this post


Link to post
Share on other sites
quote:
Original post by invective
You can (and should if you are read / writing structs) control the packing with #pragma directives (consult your compiler documentation).

GCC does it a different way. To handle different compilers (GCC, Borland, and MSVC), I do something like this:

#ifdef __GNUC__
#define NOPADVAR __attribute__((packed))
#else
#define NOPADVAR
#endif

#if defined(_MSC_VER)
#pragma pack (push, 1)
#elif defined(__BORLANDC__)
#pragma option push -a1
#endif

struct EXAMPLE {
char a NOPADVAR;
int b NOPADVAR;
char c NOPADVAR;
};

#if defined(_MSC_VER)
#pragma pack (pop)
#elif defined(__BORLANDC__)
#pragma option pop
#endif

You can simplify that by putting the GCC part and the first ''pragma'' section in a header called ''nopad.h'' (or something), and the latter in a header called ''pad.h'' (or whatever). By just doing that, you have a very portable mechanism for disabling padding. If anyone has methods to extend that to even more compilers, please tell me .

Share this post


Link to post
Share on other sites

  • Advertisement