Every user-defined message is derived from class Message. Certain virtual functions defined in that class must be overridden, others may optionally be overridden. Here is an example of a user-defined message type:
#ifndef INTVECDATA_H #define INTVECDATA_H // This is a simple vector message object. It stores // an array of integer values of arbitrary length. // The length is specified by the constructor. #include "kernel/Message.h" class IntVecData : public Message { private: int mLength; void init(int pLength, int* pSrcData) { mLength = pLength; mData = new int[mLength]; for (int i = 0; i < mLength; ++i) mData[i] = *pSrcData++; } public: // the pointer is public for simplicity int* mData; int length() const { return mLength;} // functions for type-checking const char* dataType() const { return "IntVecData"; } // isA responds TRUE if given the name of the class or // of any baseclass. int isA(const char* typ) const { if (strcmp(typ,"IntVecData") == 0) return TRUE; else return Message::isA(typ); } // constructor: makes an uninitialized array IntVecData(int pLength): mLength(pLength) { mData = new int[pLength]; } // constructor: makes an initialized array from a int array IntVecData(int pLength,int* pSrcData) { init(pLength, pSrcData); } // copy constructor IntVecData(const IntVecData& pSrc) { init(pSrc.mLength, pSrc.mData); } // clone: make a duplicate object Message* clone() const { return new IntVecData(*this);} // destructor ~IntVecData() { delete [] mData; } }; #endif//INTVECDATA_H
This message object can contain a vector of integers of arbitrary length. Some functions in the class are arbitrary and the programmer may define them in the way that is most convenient. However, there are some requirements.
The class must redefine the dataType method from class Message. This function returns a string identifying the message type. This string should be identical to the name of the class. In addition, the isA method must be defined. The isA method responds with TRUE (or 1) if given the name of the class or of any base class. Otherwise, it returns FALSE (or 0). This mechanism permits primitives to handle any of a whole group of message types, even for classes that are defined after the primitive is written.
Because of the regular structure of isA function bodies, macros are provided to generate them. The ISA_INLINE macro expands to an inline definition of the function; for example,
ISA_INLINE(IntVecData,Message)
could have been written like the example above, instead of the definition of isA to generate exactly the same code. Alternatively, to put the function body in a .cc file, the programmer can write
int isA(const char*) const;
in the class declaration and put
ISA_FUNC(IntVecData,Message)
in the .cc file (or wherever the methods are defined).
The class must define a copy constructor, unless the default copy constructor, generated by the compiler which does member-wise copying, will do the job. The class must redefine the clone method of class Message. Given that the copy constructor is defined, the form shown in the example, where a new object is created with the new operator and the copy constructor, will suffice.
In addition, the programmer may optionally define type conversions and printing functions if they make sense. If a primitive which produces messages is connected to a primitive which expects integers (or floating values, or complex values), the appropriate type conversion function is called. The base class, Message, defines the virtual conversion functions asInt(), asFloat() and asComplex() and the printing method print(), see the file $MLD/include/kernel/Message.h for their exact types. The base class conversion functions assert a run-time error, and the default print function returns a StringList saying
<type>: no print method
where type is whatever is returned by dataType(). By redefining these methods, the programmer can make it legal to connect a primitive that generates messages to a primitive that expects integer, float, or complex particles, or he can connect to a Printer or XMgraph primitive. For the XMgraph to work, you must define the asFloat function, for Printer to work, you must define the print method.