GEOS Programming: 4.3 Using Classes and Objects: Defining Methods

Up: GEOS SDK TechDocs | Up | Prev: 4.2 Non-relocatable Data | Next: 4.4 Declaring Objects
@method, @extern

Methods are the routines executed when an object receives a message. Each class understands a certain set of messages; each of these has a place in the class' method table and corresponds to one method.

Although methods are class-specific, they are not defined between the @class and @endc of class definition. Instead, their declaration line links them to a single class and to a specific message. Goc, Glue, and GEOS build each class' method table automatically; you do not have to create the table yourself.

To define a method, use the @method keyword. This has the following structure:

@method    [<hname>,] <cname>[, <mname>]+;
hname
The handler name, if any. If you wish to use the method as a function, it must have a handler name. If you do not provide a handler name, Goc will create one for you. This name is useful for setting breakpoints when debugging. If you do not provide a name, Goc constructs the name by concatenating the class name (with the -Class suffix, if any, removed) with the message name (with the MSG_- prefix, if any, removed). For example, Goc would call MyClass ' handler for MSG_DO_SOMETHING "MyDO_SOMETHING".
cname
The name of the class to which this method belongs. Each method may belong to only one class.
mname
The name of the message that invokes this method. The plus symbol indicates that one method may be invoked by more than one message as long as they all have the same parameters and return values. At least one message must be specified.
There is a special Goc keyword, _reloc , used in place of a message name when writing a method for MSG_META_RELOC and MSG_META_UNRELOC . A method handling _reloc will handle both MSG_META_RELOC and MSG_META_UNRELOC ; the method code can check the value of its message argument to find out which message is being handled.

Note that the name of the method (the handler name) is optional. Parameters and return values are not put in the method declaration--these are defined with @message as discussed in The GEOS Message System .

If you will wish to call the method as a routine occasionally, your compiler will probably require that you provide a prototype for the routine. If your @message declaration looks like

@message word MSG_MC_DO_SOMETHING(word thing);

and your @method declaration looks like

@method DoSomething, MyClass, MSG_MC_DO_SOMETHING {
/* Code Here */ }

Then your protoype should look like

extern word _pascal DoSomething(optr oself,
					  MyMessages message,
					  word thing);

The name of the type MyMessages is constructed automatically by taking the name of the class, removing the "Class" suffix, and replacing said suffix with "Messages".

Normally, all of a class' methods will be coded in the same code file in which the class is declared with @classdecl . If, however, you find you need to declare a class' methods in a different file, you can use the @extern keyword to indicate a method is defined and/or used in a different object file. Goc will give no error if @extern is used and the method exists nowhere; Glue, however, will give a linking error in such a case. There is no such requirement, however, if you are putting only the class definition (the definitions between @class and @endc ) in a different file. In this case, you can put the class definition in a .goh header file and the method code in the same .goc file as the @classdecl statement; you must @include the .goh file, but you won't need to use the @extern directive (as long as the method code is in the same file as the @classdecl directive).

The format for using @extern is as follows:

/* In the file in which the class is declared with  * @classdecl:
 */
    @extern method <cname>, <mname>+;
/* In the file containing the method code: */
    @extern method <cname>, <mname>+ {
        ...method code goes here...
    }
cname
The name of the class for which the method is defined.
mname
The name of the message which invokes the method. Note that external method definitions, like normal method definitions, can handle more than one message.

Three parameters are passed automatically with messages and do not have to be declared in the @message definition. They are important to know when writing methods, however, because they can greatly simplify your code. These are standard parameters for all classes except ProcessClass and its subclasses below:

pself
A far pointer to the object's instance data. pself points to the master group for the class for which the handler is defined. Note that this pointer may be invalidated by message calls so it is preferable to use the GOC directive, @self , instead. Unlike pself, @self is always valid.
oself
An optr to the object's instance data. It contains the global memory handle and chunk handle of the instance chunk. This can be used for routines that act on the object's instance data.
message
The message number of the message being handled.

As mentioned, ProcessClass is a special type of class. It has no true instance data because it uses the standard PC structure of an application (idata, udata, etc.). It only has one standard parameter to each of its methods: the message that was sent to it. This is because the "instance data" of ProcessClass includes all the global variables of your program. They are accessed automatically, no oself or @self is required.

Code Display 5-14 A Class Definition

/* The class ValClass defines four messages that invoke four different methods. The
 * entire class is shown in this example; it will function properly if coded this
 * way. Note that the methods have the class name in their declaration line
 * and thus do not appear within the class definition. */
@class ValClass, MetaClass;
@instance int value;			/* instance data value: an uninitialized integer */
	/* message declarations 
	 * All four messages will be handled by this class. They return
	 * the types shown and take the parameters defined. */
@message int MSG_VAL_GET_VALUE();
@message void MSG_VAL_SET_VALUE(int newValue);
@message void MSG_VAL_NEGATE_VALUE();
@message Boolean MSG_VAL_IS_VALUE_BIGGER_THAN(int newValue);
@endc
@classdecl ValClass;			/* the class structure must be put in memory */
	/* Method Declarations
	 * Each of the four methods is a single line of code. Note that the
	 * parameters are automatically defined in the message definition and do
	 * not need to be restated in the method definition. The same is true of
	 * the return type. Note also that the class and message names appear in
	 * the @method line. */
@method	ValGetValue, MyClass, MSG_VAL_GET_VALUE {
    return(@self->value);
}
@method	ValSetValue, MyClass, MSG_VAL_SET_VALUE {
    @self->value = newValue;
}
@method	ValNegateVal, MyClass, MSG_VAL_NEGATE_VALUE {
    @self->value *= -1;
}
@method	 ValClass, MSG_VAL_IS_VALUE_BIGGER_THAN {
	/* This handler's name will automatically be created to be
	 * ValVAL_IS_VALUE_BIGGER_THAN. You can use this name as a
	 * C function call from within the same thread. */
    return(@self->value > newValue);
}

You may sometimes wish to call a method with normal C call-and-return conventions, rather than by sending a message. To do so, you will have to declare the method as a routine as well as a method. The declaration should have the following format:

extern <type> _pascal <MethodName>(
	optr					oself,
	<TruncatedClassName>Messages					message,
	<type1>					<arg1>,
	<type2>					<arg2>)
type
This is the type returned by the method. It may be any data type.
MethodName
This is the name of the method. If you will be calling a method as a routine, you must give the method a name when you declare it (see To define a method, use the @method keyword. This has the following structure: ). Use the same name here.
TruncatedClassName
This is the name of the class, without the word "Class". The type of this argument is the truncated class name followed (with no space) by the word "Messages". Thus, for "HelloCounterClass", the truncated class name would be "HelloCounter", and the type of this field would be "HelloCounterMessages".
type n , arg n
Use these fields to declare each of the arguments passed to the message. Be sure to use exactly the same arguments, and in the same order, as in the message declaration.

Code Display 5-15 Declaring a Method As a Routine

@message int MSG_HELLO_COUNTER_RECALCULATE_VALUE( \
				HelloPriority 		priority, \
				word 		randomDatum, \
				char 		aLetter);
extern int _pascal HelloCounterRecalculateValue(
			optr			oself,
			HelloCounterMessages			message,
			HelloPriority			priority,
			word			randomDatum,
			char			aLetter);
@method	HelloCounterRecalculate, HelloCounterClass, \ 
		MSG_HELLO_COUNTER_RECALCULATE_VALUE {
	/* method code goes here... */
}

Up: GEOS SDK TechDocs | Up | Prev: 4.2 Non-relocatable Data | Next: 4.4 Declaring Objects