From: jdoenias@next.com (Josh Doenias)
Newsgroups: comp.sys.next.programmer
Subject: Re: archiving structs with bit fields
Date: 29 Sep 92 22:00:31 GMT

In article <1992Sep28.163408.6449@infodev.cam.ac.uk> wfc@cl.cam.ac.uk  
(William Clocksin) writes:
>Does anybody know where I can find good examples of using typed streams  
to  
>archive structs that contain bit fields?  I am particularly interesting  
in  
>knowing how to deal with multiple ClassVersions in which the bit fields  
in the  
>structs are differently defined.

This is a point that warrants a little discussion.  Bitfields are an  
extremely useful and powerful feature of the language, but one must be  
careful when using them.  Especially in regards to reading them from and  
writing them to disk.

With the upcoming release of NeXTStep on intel based machines, this issue  
becomes especially important.  The Intel chips have a different endianness  
from the Motorola ones, so if you write out bit fields on one  
architecture, they can come out reversed when read into a different  
architecture.

What we recommend is the following solution.
Let's say that we have the following structure declaration:

  struct BitFieldStruct {
    unsigned int  firstFlag:1;
    unsigned int  secondFlag:1;
    unsigned int  thirdFlag:1;
    unsigned int  someRange:4;
    unsigned int  someOtherRange:2;
    }

You may be tempted to write it as an int:

  NXWriteType(thestream, "i", (int *)(&myStruct));

THIS IS NOT A GOOD WAY TO GO!!  If you do this, you will have to byte-swap  
the value when reading the file on a little-endian machine.  You really  
don't want to be forced to do this.

What is better is to access each value separately and write them out:

  unsigned int outputValue;

  outputValue = myStruct.firstFlag;
  NXWriteType(thestream,"i",&outputValue);
  outputValue = myStruct.secondFlag;
  NXWriteType(thestream,"i",&outputValue);
  ...

And read them in using the opposite scheme :

  unsigned int inputValue;

  NXReadType(thestream,"i",&inputValue);
  myStruct.firstFlag = inputValue;
  ...


This will guarantee that you don't have any endian-ness dependancies in  
your files.

Now back to the original question.  If you want to have different class  
versions that have different bit field structures in them, just use the  
above method for reading and writing the bit fields, but use  
NXTypedStreamClassVersion() to determine the structure:

  int version = NXTypedStreamClassVersion(thestream,[self name]);
  switch (version) {
    case MYOBJECT_VERSION1: [self readVersionOneStruct:thestream]; break;
    case MYOBJECT_VERSION2: [self readVersionTwoStruct:thestream]; break;
    ...
    }

Remember that, when it comes to porting your software, the external files  
that you generate can be one of the largest problems you'll have to deal  
with.  That's why NeXT supplies functionality like TypedStreams in the  
first place.

Another point is that you may already have a version of your software out  
which generates files that have some sort of endian-ness or other  
architecture depandant features.  If this is the case, and you want to  
support old files, you will have to address this issue.
If, however, when you are designing your software, you carefully think  
about these sorts of issues, you can circumvent these problems from  
arising in the future.  Remember what mom always said about an ounce of  
prevention.

-- josh

Newsgroups: comp.sys.next.programmer