GEOS Programming: 4.5 Using Classes and Objects: Sending Messages

Up: GEOS SDK TechDocs | Up | Prev: 4.4 Declaring Objects | Next: 4.6 Managing Objects
@send, @call, @callsuper, @record, @dispatch, @dispatchcall, TravelOption, ObjDuplicateMessage(), ObjFreeMessage(), ObjGetMessageInfo()

Often you will have to send messages to objects throughout the system. You can send messages in several ways, but the two most basic and most frequently used involve the keywords @call and @send .

If a message is being sent across threads, you must be aware of synchronization issues. If the message does not cross thread boundaries, the kernel will link the code directly as if it were a function call. (This is an implementation difference only; you do not have to specify anything different in your code.)

The @send keyword causes the kernel to put the specified message into the recipient's event queue. Messages sent with @send may not give return values and may not take pointers to locked memory as arguments. The sender then continues executing without ever knowing whether the message was properly handled or not.

The @call keyword is used when the message being sent must return information to the sender. It is also used when the message must be handled immediately, before the sender is allowed to continue executing. In essence, the sender is "put to sleep" until the message has been processed, at which time the sender is woken up and may continue executing. If the message sent with @call is not handled (passed up the recipient's class tree and still not handled), it will return as if it had been; no error message will be returned.

The formats for @send and @call are similar. Use them like function calls. Their format is given below:

@send [,<flags>]+ \
		<obj>::[{<cast2>}]<msg>(<params>*);
<ret> = @call [,<flags>]+ [{<cast>}] <obj>::\
			[{<cast2>}]<msg>(<params>*);
flags
This is a list of flags separated by the commas. The allowed flags are shown below.
obj
This is the name of the object to which the message will be sent. It can also be an optr variable.
msg
This is the name of the message being sent.
params
This is a list of parameters, built exactly as it would be for a standard C function call.
ret
This is a variable that will hold the return value, if any. Note that this is valid only with @call because @send does not return anything.
cast
If a message name is put here, Goc will automatically cast the return type to whatever type is returned by cast .
cast2
If a message name is put here, Goc will assume that the message is passed the same arguments as message cast2 .

The flags allowed for these keywords are listed below. They are rarely used but are available.

forceQueue
This flag will cause the message to be placed in the recipient's event queue, even if it could have been handled by a direct call. Do not use this flag with @call .
checkDuplicate
This flag makes the kernel check if a message of the same name is already in the recipient's event queue. For this flag to work, forceQueue must also be passed. Events are checked from first (next-to-be-processed) to last. If the message is a duplicate, it will be dropped; however the replace flag, described below, can change this behavior.
checkLastOnly
This flag works with checkDuplicate , causing it to check only the last message in the event queue.
replace
This flag modifies checkDuplicate and checkLastOnly by superseding the duplicate (old) event with the new one. The new event will be put in the duplicate's position in the event queue. If a duplicate is found but the replace flag is not passed, the duplicate will be dropped.
insertAtFront
This puts the message at the front of the recipient's event queue.
canDiscardIfDesperate
This flag indicates that this event may be discarded if the system is running extremely low on handles and requires more space immediately.

The @call command can also be used within an expression in the same way a function call could. For example, the following conditional expression is valid:

if (@call MyObj::MSG_MYOBJ_TEST()) {
    /* conditional code */
}

The result of the message call will be evaluated in the if statement. Note that this may not be done with @send because it returns nothing.

Nested Message Calls

Because of the way Goc processes message calls, it is impossible to nest messages on a single line. For example, this call is illegal:

@send Obj1::MSG_THATS_PASSED_AN_INT(\
		@call Obj2::MSG_THAT_RETURNS_INT());

Any such call will generate a compile-time error. Instead, you should use temporary variables to break this up into several lines, e.g.:

int i;
i = @call Obj2::MSG_THAT_RETURNS_INT();
@send Obj2::MSG_THATS_PASSED_AN_INT(i);

Sending a Message to an Object's Superclass

Often you may wish to send a message directly to an object's superclass to ensure that default behavior is implemented. Use the @callsuper keyword with the following format:

@callsuper <obj>::<class>::<msg>(<pars>*) [<flgs>+];
obj
This is the object to send the message to, as in @call and @send . The object block must already be locked, and must be run by the current thread of execution. (Usually an object uses @callsuper() to send a message to itself.)
class
This is the class whose superclass should receive the message.
msg
This is the message name.
pars
This is the parameter list, same as @call and @send .
flgs
This is the flags list, same as @call and @send .

When used on a line by itself (with no parameters or return values), @callsuper() passes a received message on to the superclass. This is used quite often when a subclass wants to alter existing behavior rather than replace it.

Encapsulating a Message

By encapsulating messages, you can set up events to be sent out at a later time. An encapsulated message can include the message to be sent, the object it should be sent to, and the parameters that should be passed. Using encapsulated messages can sometimes simplify coding.

Messages can be encapsulated with the @record keyword and later dispatched with @dispatch and @dispatchcall . (Though the use of @record does not necessitate a later @dispatch --there are other uses for an encapsulated event.) In addition, when the event is dispatched, you can override the values set in the encapsulated event to change the destination or the message. You can also cast the return value to another type if necessary. The formats of these three keywords are as follows:

<event> = @record <obj>::[{<cast>}]<msg>(<params>*);
event
The handle of the recorded event, of type EventHandle .
obj
The object set to receive the message when it is dispatched. This field may be set to null if the destination is determined when the message is dispatched.
cast
The name of a message; if this is present, the event will have the same argument types as the specified message.
msg
The message set to be sent when the event is dispatched. This field may be set to null if the message is determined when it is dispatched.
params
The parameter list (same as in @call and @send ) that will be sent with the dispatched message.

You may create a copy of a recorded message by means of the ObjDuplicateMessage() routine. To free it, call ObjFreeMessage() . To discover the Message and destination object associated with a recorded event, call ObjGetMessageInfo() .

The @dispatch keyword is used to dispatch an encapsulated event to its destination. This is similar to @send in that it can have no return values. If the event has return values, use @dispatchcall (below).

@dispatch [noFree] \
		<nObj>::[{<cast>}]<nMsg>::<event>;
noFree
This flag indicates that the event's handle should not be freed after the event has been dispatched. This is useful if the same encapsulated event is to be used more than once.
nObj
This is an override destination. If the destination in the encapsulated event is null, then an object must be set here. Any object set will override that in the encapsulated message. If no override object is desired, set this to null .
nMsg
This is an override message. If set, this message will be sent rather than that in the encapsulated event. If no override message is desired, set this to null . Any override will be sent with the same parameters as set in the encapsulated event.
event
This is the handle of the encapsulated event. This may not be a classed event.

The @dispatchcall keyword works exactly like the @dispatch keyword above except that it allows the use of return values. The sender will be "put to sleep" if necessary while the recipient processes the message and will be "woken up" when the message returns.

<ret> = @dispatchcall [noFree] [{<cast>}] <nObj>::\
                                    <nMsg>::<event>;
ret
This is a variable that will contain the return value of the message.
other parameters
All the other parameters are the same as those in @dispatch .

Using Expressions with Messages

All message-sending keywords described in the previous sections-- @call , @send , @record , @dispatch , and @dispatchcall --can take expressions in place of a destination object's name. Additionally, the @dispatch and @dispatchcall keywords can take expressions in place of the message name. However, if an expression is used for the message, you must use a cast type to make sure Goc knows the return and parameter types. Note, however, that casts in this case use curly braces rather than parentheses.

Casting Message Call and Return Types

Goc allows you to cast a message's pass and return values. This is best explained by example:

{
 int swapInt;
 char c;
 c = @call {MSG_1} object:: {MSG_2} MSG_X(swapInt);
}

In this case, MSG_2 takes an integer argument and MSG_1 returns a char. The casts tell Goc how MSG_X will receive parameters and return results. Goc needs the casts in those cases where MSG_X doesn't appear explicitly (perhaps it has been stored as an integer), and thus Goc would not be able to parse the parameters or return values.

When Goc tries to determine proper parameters and returns, it will look to the following sources when available. When trying to figure out parameters, it will look first for MSG_2, then MSG_X, and MSG_1 last. The first one Goc finds will determine the parameters.

@send and @record don't support return values, but on a @call , Goc will figure out return values by looking at MSG_1, MSG_X, and finally MSG_2.

In this case, Goc will pass to fn's method like MSG_CAST_2 but will return values as MSG_CAST_1 does:

Message fn = GetMessageToCall();
c = @call {MSG_CAST_1} myObj:: {MSG_CAST_2} fn(x);

Now we pass to MSG_B like MSG_CAST_2, but return like MSG_B:

 c = @call myObj:: {MSG_CAST_2} MSG_B(swapInt);

Classed Events and Travel Options

MSG_META_SEND_CLASSED_EVENT, TravelOption

You do not always need to specifically include an object as a destination for your message. In many cases, you may be able to generically address your message using a classed event . A classed event consists of a pre-defined TravelOption enumerated type and an EventHandle recorded using the @record Goc construct. The event in this case does not contain a specific object, but rather an object class.

The TravelOption acts as a navigator, determining along what path the message should be delivered. The object class within the classed event acts as a filter, determining if the object sent the message is an object of that class. If the object first encountered along the path dictated by the TravelOption is not a matching class, the classed event is passed to the next object on that path. When the class finally matches, the message will be handled by that object.

The classed event is originally dispathed from the object by sending MSG_META_SEND_CLASSED_EVENT. This message is usually sent by an object to itself. The travel options available depend on the class(es) of the object receiving the message. That is, the object receiving the message must be a sub-class of one of the following:

TO_NULL
Indicates that no object is to receive the message. The default case in this case is to destroy the classed event that is dispatched,
TO_SELF
Indicates that the message should be delivered to itself. If it is not handled there, it will be destroyed.
TO_OBJ_BLOCK_OUTPUT
Each GEOS object block contains a default output stored in the object block header.This TravelOption indicates that the message should be delivered to the object block's output optr.
TO_PROCESS
Indicates that the message should be delivered to the application's ProcessClass object.
TO_GEN_PARENT
Indicates that the message should be delivered the object's generic parent.
TO_FOCUS
Indicates that the message should be delivered along the object's focus hierarchy, following the focus links from child to child.
TO_TARGET
Indicates that the message should be delivered along the object's target hierarchy, following the target links from child to child.
TO_MODEL
Indicates that the message should be delivered along the object's model hierarchy, following the model links from child to child.
TO_APP_FOCUS
Indicates that the message should be delivered along the focus hierarchy, starting at the root GenApplication object and following the focus links from child to child.
TO_APP_TARGET
Indicates that the message should be delivered along the target hierarchy, starting at the root GenApplication object and following the target links from child to child.
TO_APP_MODEL
Indicates that the message should be delivered along the model hierarchy, starting at the root GenApplication object and following the model links from child to child.
TO_SYS_FOCUS
Indicates that the message should be delivered along the focus hierarchy, starting at the root GenSystem object and following the focus links from child to child.
TO_SYS_TARGET
Indicates that the message should be delivered along the target hierarchy, starting at the root GenSystem object and following the target links from child to child.
TO_SYS_MODEL
Indicates that the message should be delivered along the model hierarchy, starting at the root GenSystem object and following the model links from child to child.
TO_VIS_PARENT
Indicates that the message should be delivered to the object's visible parent.
TO_PRINT_CONTROL
Indicates that the object should be delivered to the application object's print control object.

To send a classed event, you must first record the event, specifying the message to send and the object class to handle the message. Then you must send MSG_META_SEND_CLASSED_EVENT--usually to yourself-- passing that handle and a TravelOption .

<event> = @record <objClass>::<msg>
@send self::MSG_META_SEND_CLASSED_EVENT(<event>, 
						<TravelOption>);

Code Display 5-18 Sending a Classed Event

/*
 * First record the Classed event. In this case, we want an object of class 
 * `MyFooClass' to handle MSG_FOO. We will send this classed event along the  
 * target hierarchy. We want to begin the path at the application object, so we 
 * will use the TO_APP_TARGET TravelOption.
 */
event = @record MyFooClass::MSG_FOO;
/*
 * Then dispatch the classed event using MSG_META_SEND_CLASSED_EVENT.
 */
@send self::MSG_META_SEND_CLASSED_EVENT(event, TO_APP_TARGET);

Message Shortcuts

All messages, when received, contain three basic parameters: the message number ( message ), the optr of the recipient ( oself ), and a far pointer to the recipient's locked instance chunk ( pself ). This allows several shortcuts and shorthand formats for use within methods:

@callsuper;

When used in a method as above, the @callsuper keyword passes the received message up to the object's superclass. Use this whenever subclassing a message when the default functionality must be preserved.

 <ret> = @call self::<msg>(<params>*);

Any object can send a message to itself using @call and "self" as the destination. The remainder of the command is the same as a normal @call .

 <ret> = @call process::<msg>(<params>*);

Any object can send a message to its Process object with @call and "process" as the destination. (The Process object is the object of class ProcessClass .) The remainder of the command is the same as a normal @call .

 <ret> = @call application::<msg>(<params>*);

Any object can send a message to its Application object (of class GenApplicationClass ) with @call and "application" as the destination. The remainder of the command is the same as a normal @call .

 <ret> = @call @visParent::<msg>(<params>*);

Any object in a visible tree can use @visParent as the destination of an @call command. The message will be sent to the object's parent in the visible object tree. The remainder of the command is the same as a normal @call .

 <ret> = @call @genParent::<msg>(<params>*);

Any object in a generic tree can use @genParent as the destination of an @call command. The message will be sent to the object's parent in the generic object tree. The remainder of the command is the same as a normal @call .

 @send @visChildren::<msg>(<params>*);

Any composite object in a visible object tree (therefore a subclass of VisCompClass ) can send a message that will be dispatched at once to all of its children. Any message sent with @visChildren as the destination must be dispatched with the @send keyword and therefore can have no return value.

 @send @genChildren::<msg>(<params>*);

Any composite object in a generic object tree (therefore a subclass of GenClass ) can send a message that will be dispatched at once to all of its children. Any message sent with @genChildren as the destination must be dispatched with the @send keyword and therefore can have no return value.

In addition to the above shortcuts, you may also pass the optr of an object using @<obj> , where <obj> represents the name of the object. This syntax gets translated by Goc into (optr)&<obj> ; this is similar to using the ampersand (&) to pass a pointer.


Up: GEOS SDK TechDocs | Up | Prev: 4.4 Declaring Objects | Next: 4.6 Managing Objects