The Envelope class references objects of the Message class or derived classes. Once a message object is placed into an envelope object, the envelope takes over the responsibility for managing its memory: maintaining reference counts and deleting the message when it is no longer needed.
The constructor, which takes a reference to a Message as its argument, copy constructor, assignment operator, and the destructor of Envelope manipulate the reference counts of the referenced Message object. An assignment simply copies a pointer and increments the reference count. When the destructor of an Envelope is called, the reference count of the Message object is decremented. If it becomes zero, the Message object is deleted. Because of this deletion, a Message must never be put inside an Envelope unless it was created with the new operator. Once a Message object is put into an Envelope it doesn’t need to be explicitly deleted. It will ”live” as long as there is at least one Envelope that contains it, and it will then be deleted automatically.
It is possible for an Envelope to be ”empty”. If it is so, the empty method will return TRUE, and the data field will point to a special ”dummy message” of the DUMMY type that has no data in it.
The dataType method of Envelope returns the datatype of the contained Message object. The methods asInt(), asFloat(), asComplex() and print() are also ”passed through”, in a similar way, to the contained object. Two Envelope methods are provided for convenience to make type checking simpler: typeCheck and typeError. A simple example illustrates their use:
The method typeCheck calls isA on the message contents and returns the result, so an error will be reported if the message contents are not IntVecData and are not derived from IntVecData. Since the above code segment is common in primitives, a macro is included in Message.h to generate it. The macro
expands to essentially the same code as above. The typeError method generates an appropriate error message:
Expected message type ’arg’, got ’type’
To access the data, two methods are provided, myData() and writableCopy(). The myData function returns a pointer to the contained Message -derived object. The data pointed to by this pointer must not be modified, since other Envelope objects in the program may also contain it. If the programmer converts its type, he should always make sure that the converted type is a const pointer, see the programming example for UnPackInt. This ensures that the compiler will complain if the programmer is doing anything illegal.
The writableCopy function also returns a pointer to the contained object, but with a difference. If the reference count is one, the envelope is emptied (set to the dummy message) and the contents are returned. If the reference count is greater than one, a clone of the contents is made by calling its clone() function and returned. Again the envelope is set to zero to prevent the making of additional clones later on.
In some cases, a primitive writer will need to keep a received Message object around between executions. The best way to do this is to have the primitive contain a member of type Envelope, and to use this member object to hold the message data between executions. Messages should always be kept in envelopes so that the programmer does not have to worry about memory management.