Main Page | Modules | Class Hierarchy | Compound List | File List | Compound Members | File Members | Related Pages

Examples and caveats on using messages

The Message class is structured to allow you to manipulate message data in a variety of different ways, but there are two general styles which can be used.

One style minimizes the amount of data which is copied from place to place, at the expense of ``extra'' code that you have to generate. For example, say we have a message that we want to fill with the following structure,

        struct TestStruct {
          int a;
          int b;
          bool c;
        };

The most efficient way, from a performance perspective, to set up and send this message is,

        TestStruct* ts = (TestStruct*) msg->getData();
        ts->a = 1;
        ts->b = 2;
        ts->c = false;
        msg->setSize(sizeof(TestStruct);

Similarly, if we want to unpack a TestStruct instance from a message, we could do,

        TestStruct* ts = (TestStruct*) msg->getData();
        if (msg->getSize() < sizeof(testStruct))
          return false;
        a = ts->a;
        b = ts->b;
        c = ts->c;

As a convenience, if you do not mind that there is an extra copying of the data, you can use the convenience templated methods Message::getStruct and Message::setStruct. For example, to copy the message data to a destination structure, you could simply do,

        TestStruct ts;
        if (!msg->getStruct<TestStruct>(&ts))
          return false;

And to copy a source structure to the message data you can do,

        TestStruct ts;
        if (!msg->setStruct<TestStruct>(&ts))
          return false;

This style is much more ``compact'' than dealing with the message data directly, but that compactness does come at the price of an extra message copy.

Remember that messages are ultimately just a vector of bytes. There are several things to watch out for when directly using C++ structures to set and get message data.

First, you must be aware that different compilers can ``align'' structure elements differently. For example, some compilers will pad a one byte type such as a unsigned char out to four bytes when preceding a four byte integer. Others will not. So a structure which has the elements ``unsigned char'' followed by an ``int'' is much more dangerous than the structure which has the elements ``int'' followed by an ``unsigned char''.

Even more importantly, remember that even under the best of circumstances, only ``flat'' structures can be dealt with properly by the message convenience functions. A flat structure cannot be a class, and cannot contain pointers of any kind. For example, this is a flat structure,

       struct FlatStruct {
         int a, b;
         float c[24];
       }

But, this is not a flat structure,

        struct UnflatStruct {
          int a, b;
          float *c;
        }

If you have a structure that is not flat that you want to send, you will have to manage the packing and unpacking yourself. The simplest example is the sending and receiving of a variable length string.

On the sending side, one way to package a string ( char* s) is this,

        int len = strlen(s);
        if (len > msg->getMaxSize())  // truncate strings which are too large
          len = msg->getMaxSize()-1;
        memcpy(msg->getData(), s, len);
        msg->getData()[len] = '\0';
        msg->setSize(len+1);

On the receiving side, you simply unpack the string by doing,

        char* s = (char*) msg->getData();

Packaging more complicated structures, such as multiple strings, or structures containing variable length vectors of number, has to be done ``by hand,'', i.e., you must assemble the data vector component by component. On the plus side, assembling the data vector by hand this way alleviates the problem of structure alignment differences between compilers.


RHexLib Reference Documentation