Typecasting structures...

Alright, I am writing some code that will be reading some older files written by a DOS program years ago. The files I will be reading can be considered archives since they contain multiple pieces of data. However, each file will reference the pieces of data either by a DOS file name (char[14]) or a 32bit integer ID (int32_t). This is specified the file file's header.

Now I am trying to simplify use of the class that I am writing by using one pointer that will get allocated either as an array of file name records or an array of numeric ID records. This much is easy. Reading data with fstream is not.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef struct tagFilenameStruct
{
  char Name[14];
  int32_t Size;
} FilenameStruct;

typedef struct tagNumericStruct
{
  int32_t ID;
  int32_t Size;
} NumericStruct;

class Whatever
{
public:
  ...
  void *pRecords;
};

That much is simple. The record pointer is allocated as below.
1
2
3
4
5
6
if(this->FileHeader.Type = 1)
  this->pRecords = new FilenameStruct[this->FileHeader.Count];
else if(this->FileHeader.Type = 2)
  this->pRecords = new NumericStruct[this->FileHeader.Count];
else
  return false;

Now when I get to using "fstream.read()" I hit a problem and am not sure how to proceed.
1
2
3
4
5
6
7
8
9
10
if(this->FileHeader.Type = 1)
{
  this->_File.read((char*)this->pRecords[iLoop].Name, 14);
  this->_File.read((char*)this->pRecords[iLoop].Size, 4);
}
else
{
  this->_File.read((char*)this->pRecords[iLoop].ID, 4);
  this->_File.read((char*)this->pRecords[iLoop].Size, 4);
}

Is what I am trying to do possible, or am I going to have to write double code for every place this is referenced and have two pointers, one being NULL and one pointing to allocated data based on the file type?
Last edited on
Why not put them together?

1
2
3
4
5
6
7
8
9
10
11
12
13
struct FileReference
{
  char Name[14]; // why 14? just use 13.
  int32_t ID;
  int32_t Size;
  FileReference(): Name{0}, ID{0}, Size{0} { }
};

class Whatever:
{
public:
  ...
  std::vector <FileReference> FileReferences;

When you read stuff, yes, you'll have to use an if to do the right thing.

Hope this helps.
Last edited on
I do not want to combine them. I already thought about doing so and decided against it. I know there is a way to do what I am attempting to do, I just need some help figuring it out.
But you are doing it the unsafe, wrong way. And I already answered you about the ifs.

The best way is to create a method to read each type of file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
protected:
  void ReadType1()
  {
    this->pRecords = new FilenameStruct[this->FileHeader.Count];
    if (this->pRecords)
    {
      for (unsigned iLoop = 0; iLoop < this->FileHeader.Count; iLoop++)
      {
        this->_File.read((char*)this->pRecords[iLoop].Name, 14);
        this->_File.read((char*)this->pRecords[iLoop].Size, 4);
      }
    }
  }

  void ReadType2()
  {
    this->pRecords = new NumericStruct[this->FileHeader.Count];
    if (this->pRecords)
    {
      for (unsigned iLoop = 0; iLoop < this->FileHeader.Count; iLoop++)
      {
        this->_File.read((char*)this->pRecords[iLoop].ID, 4);
        this->_File.read((char*)this->pRecords[iLoop].Size, 4);
      }
    }
  }

[edit] I forgot to say, just call the appropriate method once you know the file type.
Good luck.
Last edited on
To the OP:

Your if statements in the third code snippet are incorrect. You're using the assignment operator =, not the equality comparison operator ==.
Last edited on
If you're comfortable with inheritance and polymorphism, you can have specialised subclasses of File, one for each type of file. These subclasses can override the File::Read method to read the appropriate record type. If you make the Read method virtual, then polymorphism does the rest for you.
1) Use polymorphism virtual void read(istream &in); (you need to change to std::vector<smart_ptr<tag> > records;)
a) Cast tagFilenameStruct *q = reinterpreter_cast<tagFilenameStruct*>(pRecords);


> just call the appropriate method once you know the file type
that logic could be stored in the object so you don't screw the calls.
You simply call `read()' and it would automagically determinate which one needs to work with.

I was looking at switching from structures to classes to solve this and it looks as though that may be the best method. I also wanted to switch to vectors, as was suggested above. I will start on both of those today.

Now I am not sure that I want to derive the classes from "File" because I will be reading and writing these at some point. I will have to consider this and weigh my options. Besides, reading isn't my issue. My issue is me preferring not to have two separate pointers, one to each type of structure, or to have a single structure with both types of data in it. It looks like I may have to however.

Oh and the single equal sign is a typo on my part. In the code I actually have two equal signs.
I was looking at switching from structures to classes to solve this and it looks as though that may be the best method.

FYI, classes and structs are almost exactly the same thing in C++. The only difference is that the default access specifier for a struct is public, and for a class it's private.

Having said that, the convention is to use a class when you're encapsulating the data and implementation details behind an interface, and a struct when you're just using it to package together some data items (as you would in C).

Now I am not sure that I want to derive the classes from "File" because I will be reading and writing these at some point.

Why would that stop you? The point is that each specialised class can override the read and write methods from the base class, to read and write the appropriate record types. As long as the methods are declared as virtual in the base class, then you can use polymorphism to automagically call the correct method for the correct file type.

That's not the only way of doing it. You know your class design better than we do, of course, so there may be a more sensible class to derive from. But it's the same principle - use inheritance and polymorphism to avoid having tons of "if... else..." statements.
I decided to combine both into one struct and use that struct in a vector. I then only have one set of if/else statements in the loading code. I don't like wasting the 14bytes per record for the numeric data archives, but that isn't much. Now I get to derive a few specialized cases from the base class.

Thanks for the help to everybody!
Topic archived. No new replies allowed.