1

I am using the following design for sending message between 2 application.

class InternalMessage
{
public:


InternalMessage(unsigned int messageId, unsigned int messageSize, INTERNAL_MESSAGE_TYPE messageType)
               : messageId_(messageId), messageSize_(messageSize), messageType_(messageType) {}
virtual ~InternalMessage() {}


protected:
unsigned int messageId_;
unsigned int messageSize_;
INTERNAL_MESSAGE_TYPE messageType_;
};

And then there are several other messages which use inheritance:

class KeyPressMessage : public InternalMessage
{
public:
KeyPressMessage () : InternalMessage(RADIO_KEY_PRESS_MESSAGE_ID, sizeof(KeyPressMessage ), EVENT_MESSAGE_TYPE),
                         key_(INVALID_KEY) {}

virtual ~KeyPressMessage () {}


}


private:
KEY key_;
};

On recieving message we use base class pointer:

MsgHandler(InternalMessage* )

On sending message we use the derived class sizeof inorder to calculate the number of bytes to send:

sizeof(KeyPressMessage )

It seems that using such design is bad because sizeof derived class is includes virtual table (which can change between 32-bit and 64-bit OS). I would like to ask if there is some better way for ICD implementation of messeges handler and sender/receiever ? Do I need some serialize/de-serialize ?

ransh
  • 1,589
  • 4
  • 30
  • 56

2 Answers2

3

sizeof() may give you different results even on the same OS when using different compilers/build options. When writing code that works over network or with external processes, you DO need serialization/ deserialization code written.

In your example, the situation is even worse: you are writing pointers (VTBL), which has no meaning for another process - it may have different memory layout and functions would be in different addresses.

There are two approaches how serialization is handled:

  • Write and read fields one by one.

    Pretty simple and very portable. Though amount of code is somewhat larger than in other options, it gives benefits for controlling type length and endianess.

  • Use packed POD data structures.

    Well, only primitive types, the compiler is enforced not to align them, so they always take same size (assuming data types are fixed).

To make sure it all works always, you have to:

  • Use only data types with fixed length. No int. Use int32_t and similar from C99.
  • Make sure data marshaling uses same endianess always.
Community
  • 1
  • 1
Valeri Atamaniouk
  • 5,125
  • 2
  • 16
  • 18
  • Hi Valeri, as to the POD , you said "the compiler is enforced not to align them". What do you mean ? Isn't it my responsibility to declare a struct as packed or not packed ? Don't I need to tell the compiler "not to align them" ? Do you mean that is should be prefered to always work unalign ? – ransh Feb 16 '15 at 09:23
  • It is pretty complicated, but technically declaring structure as packed, is almost (note _almost_) the same as declaring each field with alignment of 1 (`__attribute(align(1))` or similar). Working with misaligned data is a possibility, but reduces portability and affects performance. So, as a quick solution - it is the best. For a larger scale system you need to have portable serialization code, that performs byte-based operations. – Valeri Atamaniouk Feb 16 '15 at 09:26
  • Thanks, as to "Write and read fields one by one." do you mean something like that: byte[0] = msg->opcode; byte[2] = msg->key; byte[4] = msg->type; ..... – ransh Feb 16 '15 at 09:48
  • 1
    @ransh Not necessary in that form. You can use binary streams, or another approach which computes offsets and width automatically depending on type. After all you are not writing in C. But, down to lowest level, yes, it would look in a similar way. It is just a matter of making a collection of input/output procedures for basic types, than on top of them ones for more complex and so on. – Valeri Atamaniouk Feb 16 '15 at 10:00
1

In addition, the virtual pointers won't point to the right address in the new process, so the whole thing will go down. This is UB and generally a terrible idea. You need to serialize it properly to some format, not just memcpy around.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • 1
    Hi, Unless the other application uses exactly the same message design as written above, (also running on the same cpu). It already works. The thing is that now we make some changes in both application. The other application will be written in Java, so it's probably time to make it a better design. – ransh Feb 15 '15 at 09:16
  • @ransh: It's not a question of CPU, it's a question of processes and UB. It's a terrible idea. – Puppy Feb 15 '15 at 18:51
  • Thanks, what does UB mean ? – ransh Feb 16 '15 at 09:00