How to know the next object to be read is of which class?

I have 3 classes A, B and C (B and C are derived from A). Objects of these 3 classes have different sizes. I have written many objects of these classes into a binary file with random order (Eg: objA -> objC -> objB -> objC1-> objA1...). The hard part is the reading part. Is there any way I can know the next object to be read is of which class, or do I have to know ahead the structure of the file? Thank you.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A
{
public:
  // .......
   int getSize(); // get the size of the object in bytes.
  //.......
}


class B: public A
{
 //.....
 //.....
}


class C: public A
{
 //.....
 //.....
}
Last edited on
What you are trying to do will work, only if A, B and C are Plain Old Data (POD). If of those classes embed complex data types such as std::string, what you are trying to do won't work.

One way to do what you're trying to do is to give A a member that identifies the type of the object. e.g.
1
2
3
4
class A
{
protected:
   char m_type;  // A, B or C 


Each class' constructor will need to set m_type to the appropriate value. If m_type is at offset 0 in the class, you can do an istream::peek() to look at the first character without extracting it from the stream. Once you know the type, you can then construct an object of the appropriate type and invoke its read function.

Last edited on
I don't know if embedding the type information in the class itself is necessary; since the only time knowing the dynamic type seems to matter here is when reading from the file, you could put the type information in the file only and just construct the proper class when reading.

file:
C<binary data>
B<binary data>
...


reader:
1) read one byte and check what it is
2) instantiate proper class
3) read binary data into it
4) repeat

writer:
Each class should have a serialize function that they at least overload to output the real type byte (A, B, etc.) first. Then they can output whatever data they were before.
Thank you. @Zhuge's way is more understandable. However, we must use some extra bytes here.
@AbstractionAnon's way will save some space. However, I am wondering how can we know that the next object to be read is of which class just by peeking the first byte?
Either approach is going to take an extra byte.

Zhuge placed the byte "outside" the struct, while I placed it inside. I think Zhuge's approach is cleaner. My approach requires that the type byte be the first byte of the base class. That may not always be possible especially if at some later point A inherits something else.

Both approaches require the reading or peeking at the type byte. Once the type byte has been determined, it's a simple matter of instantiating the correct object type.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 
  char type; 
  A * a;  // polymorphic pointer

  type = peek / read type byte.
 switch (type)
  {
  case 'A':  a = new A;   // Create an instance of a
                a->Read();    // Call A's read function to read in A
                break;
  case 'B'  a = new B;    // Create an instance of B
               a->Read();    // Call B's read function to read in B
               break;
  case 'C' a = new C;    // Create an instance of C
               a->Read ();  // Call C's read function to read in C
               break;
  }
  //  process a
  delete a;

A's Read() function must be virtual.
Last edited on
Thank you very much. I understood now. Maybe I will use Zhuge's approach.
Topic archived. No new replies allowed.