2

I'm trying to implement deserialization where the mapping to field/member is only known at runtime (its complicated). Anyway what I'm trying to do is something like the following:

Class A
{
  public:
    int a;    // index 0
    float b;  // index 1
    char c;   // index 2
} 

Then I have two arrays, one with the index of the field and the other with something that indicates the type. I then want to iterate over the arrays and write to the fields from a byte stream.

Sorry for the crappy description but I just don't know how to implement it in code. Any ideas would be appreciated thanks!

user1867045
  • 85
  • 1
  • 4
  • I was thinking about having another array of void pointers to the fields and doing a memcpy. instead of holding the type just hold the length of the field in bytes. not sure if its the best way tho... – user1867045 Jan 24 '13 at 05:43
  • avoid memcpy at all costs - thats a dangerous function, you can easily make your whole OS die if you overwrite one single byte because of a programming mistake – specializt Jan 24 '13 at 05:49
  • Using `memcpy` is almost never a good idea in C++, except for the basic types like `int`, `char`, `float` and similar. – Some programmer dude Jan 24 '13 at 05:50

4 Answers4

1

There are a lot of issues and decisions needed. At the simplest, you could keep an offset into A per field, you can switch on type and set through a pointer to the field. For example - assuming there's a int16_t encoding field numbers in the input stream, making no effort to use static_cast<> etc. where it's a little nicer to do so, and assuming a 0 field number input terminator...

A a;
char* pa = (char*)&a;
char* p_v = (char*)&input_buffer;

...
while ((field_num = *(int16_t)p_v) && (p_v += sizeof(int16_t)))
    switch (type[field_num])
    {
      case Int32:
        *(int32_t*)(p_a + offset[field_num]) = *(int32_t*)(p_v);
        p_v += sizeof(int32_t);
        break;
      ...
    }

You may want to consider using e.g. ntohl() etc. to handle endianness conversions.

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
1

I can't think of a language construct that will be able to give your a field address given an index at runtime. If you could have the "type" array to actually include field sizes you would have been able to do something like:

istream &in = <get it somehow>;
size_t *field_size = <get it somehow>;
size_t num_of_fields = <get it somehow>;
A a;

char *ptr = reinterpret_cast<char *>(&a);
for (int i = 0; i < num_of_fields; i++)
{
    in.read(ptr, field_size[i]);
    ptr += field_size[i];
}

Note that this will be true if your class is simple and doesn't have any virtual function members (or inheritcs from such a class). If that is the case, you would do better to include a dummy member for getting to the byte offset where fields start within the class:

class A
{
    int __dummy;   /* must be the first data member in the class */

    ...
    <rest of your class definition here>
};

and now change the initialization of ptr as follows:

ptr = reinterpret_cast<char *>(&a) + offsetof(A, __dummy);

Another implicit assumption for this code is that machine byte-order is the same for both the machine running this code and the machine from which the serialized data is received. If not, then you will need to convert the byte ordering of the data read from the stream. This conversion is of course type dependent but you could have another array of conversion functions per field.

smichak
  • 4,716
  • 3
  • 35
  • 47
1

Yes you can, the there are two things you need to look out for when doing it though.

First of all make sure you start writing from (const char*)&A.a because all compilers append stuff that doesn't really concern you at the start of an object (visualc puts the vtable there for instance) and you won't be writing what you think you are if you start from the address of the object.

Second you might want to do a #pragma pack(1) before declaring any class that needs to be written to disk because the compilers usually align class members to make DMA transfers more efficient and you might end up having problems with this as well.

On the dynamic part of it, if making one class definition for each field combination you want to have is acceptable, then it's ok to do it like this, otherwise you'd be better off including a hash table in your class and serializing/deserializing its' contents by writing key-value pairs to the file

Radu Chivu
  • 1,057
  • 7
  • 17
-1

Let the compiler do it:

Write an operator>> function.

gbronner
  • 1,907
  • 24
  • 39
  • That's it? That's the complete answer? – jogojapan Jan 24 '13 at 05:49
  • using operator-functions is a sure sign if faulty code ... and using the shift-op for deserialization is ... dumb. Sorry but thats just experience talking out of me. – specializt Jan 24 '13 at 05:51
  • Will the compiler know what type at runtime? This is the tricky bit for me. If I put pointers into and array they would have to be void* right as multi type arrays are not supported? Are you suggesting using the operator>> function instead of memcpy only or something else? – user1867045 Jan 24 '13 at 05:59
  • Specializt: I have a problem where the field mapping can only be given at runtime. Do you have a better solution? – user1867045 Jan 24 '13 at 06:02
  • well since i got to be the victim of trolls you will be forced to re-invent the wheel and create your own serialization/deserialization - which is by design error-prone and will take a lot of time. I was pointing out the correct answer (which is : use libraries for that) but nevermind, i take it this whole website is filled with trolls, people who spread opinions as facts and people who dont even speak english hence i shall leave. I suspected this site to be - yet another - pool of trolls ... and i was right. – specializt Jan 24 '13 at 09:01