ACE Tutorial 012
Passing classes through ACE_Message_Queue

In the previous tutorial we moved our complex data into the queue by copy()ing it directly into the message block's data area. I hope that most readers got a queasy feeling when I did that. It just isn't a good idea...

A better idea would be to teach the message queue about our data types (or at least a baseclass) so that it can more efficiently handle things:

    // page03.html,v 1.10 2000/03/19 20:09:26 jcej Exp

    #ifndef BLOCK_H
    #define BLOCK_H

    #include "work.h"

    /*
      We derive a Message_Block from ACE_Message_Block and teach it about
      our Unit_Of_Work object.  When our task's svc() method pulls a block
      out of the queue, it can then invoke the virtual methods of the work
      object safely.  In this implementation we've also retained the
      original ACE_Message_Block functionallity so that we can use the
      underlying ACE_Data_Block objects to store data other than our
      Unit_Of_Work.
    */
    class Message_Block : public ACE_Message_Block
    {
    public:
        typedef ACE_Message_Block inherited;

         /*
           Construct our underlying ACE_Message_Block with the requested
           data size and initialize our Unit_Of_Work pointer with the
           given object instance.  Note that this Message_Block instance
           now assumes ownership of the Unit_Of_Work and will delete it
           when the Message_Block is deleted.
         */
        Message_Block( size_t size, Unit_Of_Work * _data )
                : inherited(size), data_(_data)
            {
                ACE_DEBUG ((LM_DEBUG, "(%P|%t) Message_Block ctor 0x%x for 0x%x\n", (void *) this, data_));
            }

            ~Message_Block(void)
            {
                ACE_DEBUG ((LM_DEBUG, "(%P|%t) Message_Block dtor 0x%x for 0x%x\n", (void *) this, data_));
                delete data_;
            }

         /*
           Return the Unit_Of_Work so that the task can invoke methods on
           it.
         */
        Unit_Of_Work * data(void)
            {
                return this->data_;
            }

    protected:
        Unit_Of_Work * data_;

         /*
           Disallow these very dangerous operations.
           If we were to copy a Message_Block object then the data_
           pointer would get copied and we would eventually end up
           deleting the same object multiple times!  That's not good.  By
           preventing the copy, we can avoid this.
         */
        Message_Block &operator= (const Message_Block &);
        Message_Block (const Message_Block &);
    };

    #endif

Ok, this looks pretty good. We just construct our specialized Message_Block instead of the generic ACE_Message_Block and let it carry our data along. When our application is done with the message block and release()es it, we know that our work object will also be taken care of.

Let's now go to main() and see what we had to change there to use this specialization.

[Tutorial Index] [Continue This Tutorial]