Home|Management Portal|Index
Using Caché Objects
Method Generators
« »
   
Server:docs
Instance:CACHE20081
User:UnknownUser
 
-
Go to:
Search:    

A powerful feature of Caché is the ability to define method generators: small programs that are invoked by the class compiler to generate the runtime code for a method.

Method generators are used extensively within the Caché class library. For example, most of the methods of the %Persistent class are implemented as method generators. This makes it possible to give each persistent class customized storage code, instead of less efficient, generic code. Most of the Caché data type class methods are also implemented as method generators. Again, this gives these classes the ability to provide custom implementations that depend on the context in which they are used.
You can use method generators within your own applications. A common usage is to define one or more “utility” superclasses that provide specialized methods for the subclasses that use them. The method generators within these utility classes create special code based on the definition (properties, methods, parameter values, etc.) of the class that uses them. Good examples of this technique are the %Populate and %XML.Adaptor classes provided within the Caché library.
Defining a Method Generator
A method generator is simply a method of a Caché class that has its CodeMode keyword set to “objectgenerator”:
Class MyApp.MyClass Extends %RegisteredObject
{
Method MyMethod() [ CodeMode = objectgenerator ]
    {
        Do %code.WriteLine(" Write """ _ %class.Name _ """")
        Do %code.WriteLine(" Quit")
        Quit $$$OK
    }
}
When the class MyApp.MyClass is compiled, it ends up with a MyMethod method with the following implementation:
 Write "MyApp.MyClass"
 Quit
Note:
The value of CodeMode in the previous example is “objectgenerator”, since this method generator uses the preferred, object-based, method generator mechanism. Prior to version 5 of Caché, there was a different preferred mechanism, in which the value of CodeMode was “generator”. While the older mechanism is preserved for compatibility, new applications should use “objectgenerator”.
How Method Generators Work
The operation of a method generator is straightforward. When you compile a class definition, the class compiler does the following:
  1. It resolves inheritance for the class (builds a list of all inherited members).
  2. It makes a list of all methods specified as method generators (by looking at the CodeMode keyword of each method).
  3. It gathers the code from all method generators, copies it into one or more temporary routines, and compiles them (this makes it possible to execute the method generator code).
  4. It creates a set of transient objects that represent the definition of the class being compiled. These objects are made available to the method generator code.
  5. It executes the code for every method generator.
    If present, the compiler will arrange the order in which it invokes the method generators by looking at the value of the GenerateAfter keyword for each of the methods. This keyword gives you some control in cases where there may be compiler timing dependencies among methods.
  6. It copies the results of each method generator (lines of code plus any changes to other method keywords) into the compiled class structure (used to generate the actual code for the class).
    Note that the original method signature (arguments and return type), as well as any method keyword values, are used for the generated method. If you specify a method generator as having a return type of %Integer, then the actual method will have a return type of %Integer.
  7. It generates the executable code for the class by combining the code generated by the method generators along with the code from all the non-method generator methods.
Method Generator Context
The key to implementing method generators is understanding the context in which method generator code is executed. As described in the previous section, the class compiler invokes the method generator code at the point after it has resolved class inheritance but before it has generated code for the class. When it invokes method generator code, the class compiler creates the following object instances and makes them available to the method generator code:
Objects Available to Method Generators
Object Description
%code An instance of the %Stream.MethodGenerator class. This is a stream into which you write the code for method.
%class An instance of the %Dictionary.ClassDefinition class. It contains the original definition of the class being compiled.
%method An instance of the %Dictionary.MethodDefinition class. It contains the original definition of the method being compiled.
%compiledclass An instance of the %Dictionary.CompiledClass class. It contains the compiled definition of the class being compiled. It contains information about the class after inheritance has been resolved (such as the list of all properties and methods, including those inherited from superclasses).
%compiledmethod An instance of the %Dictionary.CompiledMethod class. It contains the compiled definition of the method being generated.
In addition to these objects, an array variable, %parameter, is provided. This array contains the values of any class parameters indexed by parameter name. For example, %parameter("MYPARM"), contains the value of the MYPARM class parameter for the current class. This variable is provided as an easier alternative to using the list of parameter definitions available via the %class object.
Implementing Method Generators
To implement a method generator, do the following:
  1. Define a method and set its CodeMode keyword to “objectgenerator”.
  2. In the body of the method, write code that will generate the actual method code when the class is compiled. This code will used the %code object to write out the code. It will most likely use the other available objects as inputs to decide what code to generate.
The following is an example of a method generator that creates a method that lists the names of all the properties of the class it belongs to:
ClassMethod ListProperties() [ CodeMode = objectgenerator ]
{
    For i = 1:1:%compiledclass.Properties.Count() {
        Set prop = %compiledclass.Properties.GetAt(i).Name
        Do %code.WriteLine(" Write """ _ prop _ """,!")
    }
    Do %code.WriteLine(" Quit")
    Quit $$$OK
}
This generator will create a method with an implementation similar to:
 Write "Name",!
 Write "SSN",!
 Quit
Note the following about the method generator code:
  1. It uses the WriteLine method of the %code object to write lines of code to a stream containing the actual implementation for the method. (You can also use the Write method to write text without an end-of-line character).
  2. Each line of generated code has a leading space character. This is required because Caché ObjectScript does not allow commands within the first space of a line. This would not be the case if our method generator is creating Basic or Java code.
  3. As the lines of generated code appear within strings, you have to be very careful about escaping quotation mark characters by doubling them up ("").
  4. To find the list of properties for the class, it uses the %compiledclass object. It could use the %class object, but then it would only list properties defined within the class being compiled; it would not list inherited methods.
  5. It returns a status code of $$$OK, indicating that the method generator ran successfully. This return value has nothing to do with the actual implementation of the method.
Method Generators for Other Languages
At this time, method generators are implemented using Caché ObjectScript.
You can, however, generate code for different languages by setting the Language keyword to the desired language. The available choices are “cache”, “basic”, and “java”. (A Java method only appears in the Java class projected for this class definition).
For example, the following is the stub of a method generator that generates Java code for a Java-projected class:
Method JavaStub() As %Integer [ CodeMode = objectgenerator, Language = java ]
 {
    // WriteLine statements with Java code to be generated
 }
Specifying CodeMode within a Method Generator
By default, a method generator will create a “code” method (that is, the CodeMode keyword for the generated method is set to “code”). You can change this using the CodeMode property of the %code object.
For example, the following method generator will generate an ObjectScript expression method:
Method Double(%val As %Integer) As %Integer [ CodeMode = objectgenerator ]
{
    Set %code.CodeMode = "expression"
    Do %code.WriteLine("%val * 2")
}