Top Up Prev Next Bottom Contents Index Search

7.4 The Heterogeneous Message Interface

The heterogeneous message interface is a mechanism to permit messages of arbitrary type (objects of some derived type of class Message) to be transmitted by blocks. Because these messages may be very large, facilities are provided to permit many references to the same Message; Message objects are "held" in another class called Envelope. As the name suggests, Messages are transferred in Envelopes. When Envelopes are copied, both Envelopes refer to the same Message. A Message will be deleted when the last reference to it disappears; this means that Messages must always be on the heap. So that Messages may be transmitted by portholes, there is a class MessageParticle whose data field is an Envelope. This permits it to hold a Message just like any other Envelope object.

7.4.1 Class Envelope

class Envelope has two constructors. The default constructor constructs an "empty" Envelope (in reality, the envelope is not empty but contains a special "dummy message" - more on this later). There is also a constructor of the form

Envelope(Message& data); 
This constructor creates an Envelope that contains the Message data, which MUST have been allocated with new. Message objects have reference counts; at any time, the reference count equals the number of Envelope objects that contain (refer to) the Message object. When the reference count drops to zero (because of execution of a destructor or assignment operator on an Envelope object), the Message will be deleted. Class Envelope defines an assignment operator, copy constructor, and destructor. The main work of these functions is to manipulate reference counts. When one Envelope is copied to another, both Envelopes refer to the same message.

int empty() const; 
Return TRUE if the Envelope is "empty" (points to the dummy message), FALSE otherwise.

const Message* myData() const; 
Return a pointer to the contained Message. This pointer must not be used to modify the Message object, since other Envelopes may refer to the same message.

Message* writableCopy(); 
This method produces a writable copy of the contained Message, and also zeros the Envelope (sets it to the empty message). If this Envelope is the only Envelope that refers to the message, the return value is simply the contained message. If there are multiple references to the message, the clone method is called on the Message, making a duplicate, and the duplicate is returned. The user is now responsible for memory management of the resulting Message. If it is put into another Envelope, that Envelope will take over the responsibility, deleting the message when there is no more need for it. If it is not put into another Envelope, the user must make sure it is deleted somehow, or else there will be a memory leak.

int typeCheck(const char* type) const; 
This member function asks the question "is the contained Message of class type, or derived from type" ? It is implemented by calling isA on the Message. Either TRUE or FALSE is returned.

const char* typeError(const char* expected) const; 
This member function may be used to format error messages for when one type of Message was expected and another was received. The return value points to a static buffer that is wiped out by subsequent calls.

const char* dataType() const; 
int asInt() const;
double asFloat() const;
Complex asComplex() const;
StringList print() const;
All these methods are "passthrough methods"; the return value is the result of calling the identically named function on the contained Message object.

7.4.2 Class Message

Message objects can be used to carry data between blocks. Unlike Particles, which must all be of the same type on a given connection, connections that pass Message objects may mix message objects of many types on a given connection. The tradeoff is that blocks that receive Message objects must, as a rule, type-check the received objects. The base class for all messages, named Message, contains no data, only a reference count (accordingly, all derived classes have a reference count and a standard interface). The reference count counts how many Envelope objects refer to the same Message object. The constructor for Message creates a reference count that lives on the heap. This means that the reference count is non-const even when the Message object itself is const. The copy constructor for Message ignores its argument and creates a new Message with a new reference count. This is necessary so that no two messages will share the same reference count. The destructor, which is virtual, deletes the reference count. The following Message functions must be overridden appropriately in any derived class:

virtual const char* dataType() const; 
This function returns the type of the Message. The default implementation returns "DUMMY".

virtual Message* clone() const; 
This function produces a duplicate of the object it is called on. The duplicate must be "good enough" so that applications work the same way whether the original Message or one produced by clone() is received. A typical strategy is to define the copy constructor for each derived Message class and write something like

Message* MyMessage::clone() const { return new MyMessage(*this);} 
virtual int isA(const char*) const;
The isA function returns true if given the name of the class or the name of any base class. Exception: the base class function returns FALSE to everything (as it has no data at all). A macro ISA_FUNC is defined to automate the generation of implementations of derived class isA functions; it is the same one as that used for the NamedObj class. The following methods may optionally be redefined.

virtual StringList print() const; 
This method returns a printable representation of the Message. The default implementation returns a message like

Message class <type>: no print method 
where type is the message type as returned by the dataType function.

virtual int asInt() const; 
virtual double asFloat() const;
virtual Complex asComplex() const;
These functions represent conversions of the Message data to an integer, a floating point value, and a complex number, respectively. Usually such conversions do not make sense; accordingly, the default implementations generate an error message (using the protected member function errorConvert) and return a zero of the appropriate type. If a conversion does make sense, they may be overridden by a method that does the appropriate conversion. These methods will be used by the MessageParticle class when an attempt is made to read a MessageParticle in a numeric context. One protected member function is provided:

int errorConvert(const char* cvttype) const; 
This function invokes Error::abortRun with a message of the form

Message class <msgtype>: invalid conversion to cvttype 
where msgtype is the type of the Message, and cvttype is the argument.

7.4.3 Class MessageParticle

MessageParticle is a derived type of Particle whose data field is an Envelope; accordingly, it can transport Message objects. MessageParticle defines no new methods of its own; it only provides behaviors for the virtual functions defined in class Particle. The most important such behaviors are as follows:

void operator << (const Envelope& env); 
This method loads the Message contained in env into the Envelope contained in the MessageParticle. Since the Envelope assignment operator is used, after execution of this method both env and the MessageParticle refer to the message, so its reference count is at least 2.

void getMessage(const Envelope& env); 
This method loads the message contained in the MessageParticle into the Envelope env, and removes the message from the MessageParticle (so that it now contains the dummy message). If env previously contained the only reference to some other Message, that previously contained Message will be deleted.

void accessMessage(const Envelope& env); 
accessMessage is the same as getMessage except that the message is not removed from the MessageParticle. It can be used in situations where the same Particle will be read again. We recommend that getMessage be used where possible, especially for very large message objects, so that they are deleted as soon as possible.

Top Up Prev Next Bottom Contents Index Search
Copyright © 1990-1997, University of California. All rights reserved.