Skip to main content

Defining and Compiling Classes

This chapter describes the basics of defining and compiling classes. It discusses the following topics:

When viewing this book online, use the preface of this book to quickly find other topics.

Introduction to Terminology

The following shows a simple Caché class definition, with some typical elements:

Class Demo.MyClass Extends %RegisteredObject
{

Property Property1 As %String;

Property Property2 As %Numeric;

Method MyMethod() As %String
{
   set returnvalue=..Property1_..Property2
   quit returnvalue
}

}

Note the following points:

This class refers to several system classes provided by Caché. These classes are %RegisteredObjectOpens in a new tab (whose full name is %Library.RegisteredObjectOpens in a new tab), %StringOpens in a new tab (%Library.StringOpens in a new tab), and %NumericOpens in a new tab (%Library.NumericOpens in a new tab). %RegisteredObjectOpens in a new tab is a key class in Caché, because it defines the object interface. It provides the methods you use to create and work with object instances. %StringOpens in a new tab and %NumericOpens in a new tab are data type classes. As a consequence, the corresponding properties hold literal values (rather than other kinds of values).

Kinds of Classes

Caché provides a large set of class definitions that your classes can use in the following general ways:

  • You can use Caché classes as superclasses for your classes.

  • You can use Caché classes as values of properties, values of arguments to methods, values returned by methods, and so on.

  • Some Caché classes simply provide specific APIs. You typically do not use these classes in either of the preceding ways. Instead you write code that calls methods of the API.

The most common choices for superclasses are as follows:

  • %RegisteredObjectOpens in a new tab — This class represents the object interface in its most generic form.

  • %PersistentOpens in a new tab — This class represents a persistent object. In addition to providing the object interface, this class provides methods for saving objects to the database and reading objects from the database.

  • %SerialObjectOpens in a new tab — This class represents an object that can be embedded in (serialized within) another object.

  • Subclasses of any of the preceding classes.

  • None — It is not necessary to specify a superclass when you create a class.

The most common choices for values of properties, values of arguments to methods, values returned by methods, and so on are as follows:

  • Object classes (the classes contained in the previous list)

  • Data type classes

  • Collection classes

  • Stream classes

Later chapters of this book discuss these categories of classes.

Object Classes

The phrase object class refers to any subclass of %RegisteredObjectOpens in a new tab. With an object class, you can create an instance of the class, specify properties of the instance, and invoke methods of the instance. A later chapter describes these tasks (and provides information that applies to all object classes).

The generic term object refers to an instance of an object class.

There are three general categories of object classes:

The following figure shows the inheritance relationship among these three classes. The boxes list some of the methods defined in the classes:

generated description: objclasses

Collection classes and stream classes are object classes with specialized behavior.

Data Type Classes

The phrase data type class refers to any class whose ClassType keyword equals datatype or any subclass of such a class. These classes are not object classes (a data type class cannot define properties, and you cannot create an instance of the class). The purpose of a data type class (more accurately a data type generator class) is to be used as the type of a property of an object class.

Kinds of Class Members

A Caché class definition can include the following items, all known as class members:

  • Parameters — A parameter defines a constant value for use by this class. The value is set at compilation time, in most cases.

  • Methods — Caché supports two types of methods: instance methods and class methods. An instance method is invoked from a specific instance of a class and performs some action related to that instance; this type of method is useful only in object classes. A class method is a method that can be invoked whether or not an instance of its class is in memory; this type of method is called a static method in other languages.

  • Properties — A property contains data for an instance of the class. Properties are useful only in object classes. The following subsection provides more information.

  • Class queries — A class query defines an SQL query that can be used by the class and specifies a class to use as a container for the query. Often (but not necessarily), you define class queries in a persistent class, to perform queries on the stored data for that class. You can, however, define class queries in any class.

  • Other kinds of class members that are relevant only for persistent classes:

    • Storage definitions

    • Indices

    • Foreign keys

    • SQL triggers

  • XData blocks — An XData block is a named unit of data defined within the class, typically for use by a method in the class. These have many possible applications.

  • Projections — A class projection provides a way to extend the behavior of the class compiler.

    The projection mechanism is used by the Java and C++ projections; hence the origin of the term projection.

Kinds of Properties

Formally, there are two kinds of properties: attributes and relationships.

Attributes hold values. Attribute properties are usually referred to simply as properties. Depending on the property definition, the value that it holds can be any of the following:

  • A literal value such as "MyString" and 1. Properties that hold literal values are based on data type classes and are also called data type properties. See the chapter “Defining and Using Literal Properties.”

  • A stream. A stream is a Caché object that contains a value that would be too long for a string. See the chapter “Working with Streams.”

  • A collection. Caché provides the ability to define a property as either a list or an array. The list or array items can be literal values or can be objects. See the chapter “Working with Collections.”

  • Some other kind of object. See the chapter “Defining and Using Object-Valued Properties.”

Relationships hold associations between objects. Relationship properties are referred to as relationships. Relationships are supported only in persistent classes. See the chapter “Defining and Using Relationships.”

Defining a Class: The Basics

This section discusses basic class definitions in more detail. It discusses the following topics:

Typically, you use Studio to define classes. You can also define classes programmatically using the Caché class definition classes or via an XML class definition file. If you define an SQL table using SQL DDL statements, the system creates a corresponding class definition.

Choosing a Superclass

When you define a class, one of your earliest design decisions is choosing the class (or classes) which to base your class. If there is only a single superclass, include Extends followed by the superclass name, at the start of the class definition.

Class Demo.MyClass Extends Superclass 
{

//...

}

If there are multiple superclasses, specify them as a comma-separated list, enclosed in parentheses.

Class Demo.MyClass Extends (Superclass1, Superclass2, Superclass3) 
{

//...

}

It is not necessary to specify a superclass when you create a class. It is common to use %RegisteredObjectOpens in a new tab as the superclass even if the class does not represent any kind of object, because doing so gives your class access to many commonly used macros, but you can instead directly include the include files that contain them.

Include Files

When you create a class that does not extend %RegisteredObjectOpens in a new tab or any of its subclasses, you might want to include the following include files:

  • %occStatus.inc, which defines macros to work with %StatusOpens in a new tab values.

  • %occMessages.inc, which defines macros to work with messages.

    For details on the macros defined by these include files, see “Using System-supplied Macros” in Using Caché ObjectScript.

If your class does extend %RegisteredObjectOpens in a new tab or any of its subclasses, these macros are available automatically.

You can also create your own include files and include them in class definitions as needed.

To include an include file at the beginning of a class definition, use syntax of the following form. Note that you must omit the .inc extension of the include file:

Include MyMacros

For example:

Include %occInclude

Class Classname 
{
}

To include multiple include files at the beginning of a class definition, use syntax of the following form:

Include (MyMacros, YourMacros) 

Note that this syntax does not have a leading pound sign (in contrast to the syntax required in a routine). Also, the Include directive is not case-sensitive, so you could use INCLUDE instead, for example. The include file name is case-sensitive.

See also the reference section on #include in Using Caché ObjectScript.

Specifying Class Keywords

In some cases, it is necessary to control details of the code generated by the class compiler. For one example, for a persistent class, you can specify an SQL table name, if you do not want to (or cannot) use the default table name. For another example, you can mark a class as final, so that subclasses of it cannot be created. The class definitions support a specific set of keywords for such purposes. If you need to specify class keywords, include them within square brackets after the superclass, as follows:

Class Demo.MyClass Extends Demo.MySuperclass [ Keyword1, Keyword2, ...]
{

//...

}

For example, the available class keywords include Abstract and Final. For an introduction, see “Compiler Keywords,” later in this chapter. Caché also provides specific keywords for each kind of class member.

Introduction to Defining Class Parameters

A class parameter defines a constant value for all objects of a given class. To add a class parameter to a class definition, add an element like one of the following to the class:

Parameter PARAMNAME as Type;
Parameter PARAMNAME as Type = value;
Parameter PARAMNAME as Type [ Keywords ] = value;

Keywords represents any parameter keywords. For an introduction to keywords, see “Compiler Keywords,” later in this chapter. For parameter keywords; see “Parameter Keywords” in the Caché Class Definition Reference. These are optional.

Introduction to Defining Properties

An object class can include properties.

To add a property to a class definition, add an element like one of the following to the class:

Property PropName as Classname;
Property PropName as Classname [ Keywords ] ;
Property PropName as Classname(PARAM1=value,PARAM2=value) [ Keywords ] ;
Property PropName as Classname(PARAM1=value,PARAM2=value) ;

PropName is the name of the property, and Classname is an optional class name (if you omit this, the property is assumed to be of type %StringOpens in a new tab).

Keywords represents any property keywords. For an introduction to keywords, see “Compiler Keywords,” later in this chapter. For property keywords; see “Property Keywords” in the Caché Class Definition Reference. These are optional.

Depending on the class used by the property, you might also be able to specify property parameters, as shown in the third and fourth variations.

Notice that the property parameters, if included, are enclosed in parentheses and precede any property keywords. Also notice that the property keywords, if included, are enclosed in square brackets.

Introduction to Defining Methods

You can define two kinds of methods in Caché classes: class methods and instance methods.

To add a class method to a class definition, add an element like the following to the class:

ClassMethod MethodName(arguments) as Classname [ Keywords]
{
//method implementation
}

MethodName is the name of the method and arguments is a comma-separated list of arguments. Classname is an optional class name that represents the type of value (if any) returned by this method. Omit the As Classname part if the method does not return a value.

Keywords represents any method keywords. For an introduction to keywords, see “Compiler Keywords,” later in this chapter. For method keywords, see “Method Keywords” in the Caché Class Definition Reference. These are optional.

To add an instance method, use the same syntax with Method instead of ClassMethod:

Method MethodName(arguments) as Classname [ Keywords]
{
//method implementation
}

Instance methods are relevant only in object classes.

Naming Conventions

Class and class members follow specific naming conventions. These are detailed in this section.

Rules for Class and Class Member Names

This section describes the rules for class and member names, such as maximum length, allowed characters, and so on. A full class name includes its package name, as described in the next section.

Every identifier must be unique within its context (that is, no two classes can have the same name). Caché has the following limits on package, class, and member names:

  • Each package name can have up to 189 unique characters.

  • Each class name can have up to 60 unique characters.

  • Each method and property name can have up to 180 unique characters. See the section “Class Member Names” for more details.

  • The combined length of the name of a property and of any indices on the property should be no longer than 180 characters.

  • The full name of each member (including the unqualified member name, the class name, the package name, and any separators) must be 220 characters or fewer.

  • Each name can include Unicode characters.

Identifiers preserve case: you must exactly match the case of a name; at the same time, two classes cannot have names that differ only in case. For example, the identifiers “id1” and “ID1” are considered identical for purposes of uniqueness.

Identifiers must start with an alphabetic character, though they may contain numeric characters after the first position. Identifiers cannot contain spaces or punctuation characters with the exception of package names which may contain the “.” character. On a Unicode system, identifiers may contain Unicode characters.

Certain identifiers start with the “%” character; this identifies a system item. For example, many of the methods and packages provided with the Caché library start with the “%” character.

Member names can be delimited, which allows them to include characters that are otherwise not permitted. To create a delimited member name, use double quotes for the first and last characters of the name. For example:

Property "My Property" As %String;

For more details on system identifiers, see the appendix “Rules and Guidelines for Identifiers” in the Caché Programming Orientation Guide.

Class Names

Every class has a name that uniquely identifies it. A full class name consists of two parts: a package name and a class name: the class name follows the final “.” character in the name. A class name must be unique within its package; a package name must be unique within a Caché namespace. For details on packages, see the chapter “Packages.”

Because persistent classes are automatically projected as SQL tables, a class definition must specify a table name that is not an SQL reserved word; if the name of a persistent class is an SQL reserved word, then the class definition must also specify a valid, non-reserved word value for its SQLTableName keyword.

Class Member Names

Every class member (such as a property or method) must have a name that is unique within its class and with a maximum length of 180 characters. Further, a member of a persistent cannot use an SQL reserved word as its identifier. It can define an alias, however, using the SQLName or SQLFieldName keyword of that member (as appropriate).

Important:

InterSystems strongly recommends that you do not give two members the same name. This can have unexpected results.

Inheritance

A Caché class can inherit from already existing classes. If one class inherits from another, the inheriting class is known as a subclass and the class or classes it is derived from are known as superclasses.

The following shows an example class definition that uses two superclasses:

Class User.MySubclass Extends (%Library.Persistent, %Library.Populate)
{
}
Note:

The syntax shown here corresponds to the Super keyword, which is visible in the Studio Inspector and in class definitions exported as XML.

In addition to a class inheriting methods from its superclasses, the properties inherit additional methods from system property behavior classes and, in the case of a data type attribute, from the data type class.

For example, if there is a class defined called Person:

Class MyApp.Person Extends %Library.Persistent
{
Property Name As %String;
Property DOB As %Date;
}

It is simple to derive a new class, Employee, from it:

Class MyApp.Employee Extends Person
{
Property Salary As %Integer;
Property Department As %String;
}

This definition establishes the Employee class as a subclass of the Person class. In addition to its own class parameters, properties, and methods, the Employee class includes all of these elements from the Person class.

Use of Subclasses

You can use a subclass in any place in which you might use its superclass. For example, using the above defined Employee and Person classes, it is possible to open an Employee object and refer to it as a Person:

 Set x = ##class(MyApp.Person).%OpenId(id)
 Write x.Name

We can also access Employee-specific attributes or methods:

 Write x.Salary // displays the Salary property (only available in Employee instances)

Primary Superclass

The leftmost superclass that a subclass extends is known as its primary superclass. A class inherits all the members of its primary superclass, including applicable class keywords, properties, methods, queries, indices, class parameters, and the parameters and keywords of the inherited properties and inherited methods. Except for items marked as Final, the subclass can override (but not delete) the characteristics of its inherited members.

See the next section for more details about multiple inheritance.

Multiple Inheritance

By means of multiple inheritance, a class can inherit its behavior and class type from more than one superclass. To establish multiple inheritance, list multiple superclasses within parentheses. The leftmost superclass is the primary superclass.

For example, if class X inherits from classes A, B, and C, its definition includes:

Class X Extends (A, B, C) 
{
}

The default inheritance order for the class compiler is from left to right, which means that differences in member definitions among superclasses are resolved in favor of the leftmost superclass (in this case, A superseding B and C, and B superseding C.)

Specifically, for class X, the values of the class parameter values, properties, and methods are inherited from class A (the first superclass listed), then from class B, and, finally, from class C. X also inherits any class members from B that A has not defined, and any class members from C that neither A nor B has defined. If class B has a class member with the same name as a member already inherited from A, then X uses the value from A; similarly, if C has a member with the same name as one inherited from either A or B, the order of precedence is A, then B, then C.

Because left-to-right inheritance is the default, there is no need to specify this; hence, the previous example class definition is equivalent to the following:

Class X Extends (A, B, C) [ Inheritance = left ]
{
}

To specify right-to-left inheritance among superclasses, use the Inheritance keyword with a value of right:

Class X Extends (A, B, C) [ Inheritance = right ]
{
}

With right-to-left inheritance, if multiple superclasses have members with the same name, the superclass to the right takes precedence.

Note:

Even with right-to-left inheritance, the leftmost superclass (sometimes known as the first superclass) is still the primary superclass. This means that the subclass inherits only the class keyword values of its leftmost superclass — there is no override for these.

For example, in the case of class X inheriting from classes A, B, and C with right-to-left inheritance, if there is a conflict between a member inherited from class A and one from class B, the member from class B overrides (replaces) the previously inherited member; likewise for the members of class C in relation to those of classes A and B. The class keywords for class X come exclusively from class A. (This is why extending classes A and B — in that order — with left-to-right inheritance is not the same as extending classes B and A — in that order — with right-to-left inheritance; the keywords are inherited from the leftmost superclass in either definition, which makes the two cases different.)

Important:

Before version 2010.1 of Caché, inheritance order was always right-to-left and could not be changed. Classes from an older instance that has upgraded will automatically continue to use right-to-left inheritance due to a class dictionary upgrade. Hence, existing code does not require any changes, even though new classes use left-to-right inheritance by default from 2010.1 onward.

Additional Topics

Also see “%ClassName() and the Most Specific Type Class (MSTC)” in the chapter “Working with Registered Objects.”

Introduction to Compiler Keywords

As shown in “Defining a Class: The Basics,” you can include keywords in a class definition or in the definition of a class member. These keywords, also known as class attributes, generally affect the compiler. This section introduces some common keywords and discusses how Caché presents them.

Example

The following example shows a class definition with some commonly used keywords:

/// This sample persistent class represents a person.
Class MyApp.Person Extends %Persistent [ SqlTableName = MyAppPerson ]
{

/// Define a unique index for the SSN property.
Index SSNKey On SSN [ Unique ];

/// Name of the person.
Property Name As %String [ Required ];

/// Person's Social Security number.
Property SSN As %String(PATTERN = "3N1""-""2N1""-""4N") [ Required ];

}

This example shows the following keywords:

  • For the class definition, the Extends keyword specifies the superclass (or superclasses) from which this class inherits.

    Note that the Extends keyword has a different name when you view the class in other ways; see the next section.

  • For the class definition, the SqlTableName keyword determines the name of the associated table, if the default name is not to be used. This keyword is meaningful only for persistent classes, which are described later in this book.

  • For the index definition, the Unique keyword causes Caché to enforce uniqueness on the property on which the index is based (SSN in this example).

  • For the two properties, the Required keyword causes Caché to require non-null values for the properties.

PATTERN is not a keyword but instead is a property parameter; notice that PATTERN is enclosed in parentheses, rather than square brackets.

Later chapters of this book discuss many additional keywords, but not all of them. Apart from keywords related to storage (which are not generally documented), you can find details on the keywords in the Caché Class Definition Reference. The reference information demonstrates the syntax that applies when you view a class in the usual edit mode.

Presentation of Keywords and Their Values

In many but not all cases, when you specify a keyword for a class definition or for a class member, you add an element of one of the following forms to the class or class member:

  • [ keyword ]

    Specifies the keyword as true.

  • [ Not keyword ]

    Specifies the keyword as false.

  • [ keyword=value ]

    Specifies the keyword as the given value.

In the Studio Inspector, the compiler keywords and their values are presented differently. For example, consider the following class definition:

/// This sample persistent class represents a person.
/// <p>Maintenance note: This class is used by some of the bindings samples.
Class Sample.Person Extends (%Persistent, %Populate, %XML.Adaptor)
{

...

For this class, the Studio Inspector displays the following table of keywords:

generated description: studio inspector

Notice that both Name and Description are keywords. If you edit Description in the Inspector, Studio updates the comments in the class definition, and vice versa. Similarly, there is a keyword named Super, which specifies the superclasses of this class. If you edit that, Studio updates the Extends part of the class definition.

The Studio Inspector has similar behavior when you display a class member. In that case, the Inspector window displays a table of all the member keywords and the values of those keywords for the currently selected member. (For a property, the Inspector window also lists the available property parameters and their current values.)

When you export a class definition to XML, the exported file looks like the following:

<Export generator="Cache" version="25" zv="Cache for Windows (x86-64) 2015.1 (Build 416U)" ts="2014-12-19 15:27:27">
<Class name="Sample.Person">
<Description><![CDATA[
This sample persistent class represents a person.
<p>Maintenance note: This class is used by some of the bindings samples.]]></Description>
<Super>%Persistent,%Populate,%XML.Adaptor</Super>
<TimeChanged>63540,49568.139638</TimeChanged>
<TimeCreated>59269,38836.623</TimeCreated>
 
<Parameter name="EXTENTQUERYSPEC">
<Default>Name,SSN,Home.City,Home.State</Default>
</Parameter>
 
...

Most of the XML elements in this file correspond to the compiler keywords.

When you access a class definition programmatically, the class definition instance contains properties that correspond to the keywords. For information on accessing class definitions programmatically, see the chapter “Using the %Dictionary Classes.”

Creating Class Documentation

Caché provides a web page called the InterSystems Class Reference, which displays automatically generated reference information for the classes provided by InterSystems, as well as for classes you create. Informally, the Class Reference is known as Documatic, because it is generated by the class %CSP.DocumaticOpens in a new tab.

This section introduces the Class Reference and explains how to create your own documentation and how to include HTML markup.

Introduction to the Class Reference

The purpose of the Class Reference is to advertise, to other programmers, which parts of a class can be used, and how to use them. The following shows an example:

generated description: classref ex

This reference information shows the definitions of class members, but not their actual implementations. For example, it shows method signatures but not their internal definitions. It includes links between elements so that you can rapidly follow the logic of the code; in some cases, this is quicker than using Studio. There is also a search option.

Creating Documentation to Include in the Class Reference

To create documentation to include in the Class Reference, create comments within the class definitions — specifically comments that start with ///. If you precede the class declaration with such comments, the comments are shown at the top of the page for the class. If you precede a given class member with such comments, the comments are shown after the generated information for that class member. Once you compile the class, you can view its generated class documentation the next time you open the Class Reference documentation. If you add no Class Reference comments, items that you add to a class or package appear appropriately in the lists of class or package contents, but without any explanatory text.

You can extend any existing Class Reference comments from within Studio, either by editing the Description field for a class in the Studio Inspector window, or by adding specially formatted lines to the class code. The syntax rules for Class Reference comments are strict:

  • The length of the Class Reference comment (all lines combined) must be less than the maximum string length for your system; see “Long String Limit” in the Caché Programming Orientation Guide.

  • All Class Reference comments that describe a class or class member must appear in a consecutive block immediately before the declaration of the item that they describe.

  • Each line in the block of comments must start with three slashes: ///

    Tip:

    Note that, by default, the presentation combines the text of all the /// lines and treats the result as single paragraph. You can insert HTML line breaks (<br>). Or you can use HTML formatting (such as <p> and </p>), as discussed in the subsection.

  • The three slashes must begin at the first (left-most) position in the line.

  • No blank lines are allowed within Class Reference comments.

  • No blank lines are allowed between the last line of the Class Reference comments and the declaration for the item that they describe.

If you add Class Reference comments using the Description field with a Studio wizard or in the Studio Inspector window, Studio handles these details for you (apart from the length restriction). If you add Class Reference comments directly into the code, Studio alerts you to some Class Reference syntax errors: for example, if you insert a blank line between the comments and the declaration, or if you use an insufficient number of slashes at the beginning of a line within a Class Reference text block. However, Studio does not alert you to any other types of bad syntax within Class Reference comments.

Class Reference comments allow plain text, plus any standard HTML element and a small number of specialized elements, as shown in the following code sample:

/// <p>Transforms <i>Star</i> order messages for <i>ChartScript</i>. <br/>
/// Developed Nov 2004 by <b>MT Engineering Team</b>. <br/>
/// See also <class>StarADTtoChartScript</class> and
/// <class>StarMRGtoChartScript</class> </p>
/// <p>Only Orders for these Departments pass: </p>
/// <ul><li>CP</li><li>NS</li><li>URO</li><li>NIV</li></ul>
/// <p>As long as they are one of the following:</p>
/// <ol>
/// <li>New Child Order</li>
/// <li>Child Order Status Change</li>
/// <li>Order Cancellation</li>
/// </ol>
/// <p>Data Transformation sets "T" in MSH 11 for Test environment.</p>
Class MT.dt.StarORMtoChartScript
      Extends Ens.DataTransformDTL [ ProcedureBlock ]

{
  // The data transformation class code goes here.
}

The previous example formats the Class Reference entry for the class as follows:

generated description: documatic html

Using HTML Markup in Class Documentation

You can use HTML tags within the comments in a class. With regard to the allowed HTML elements, adhere to as strict an HTML standard as you can, for example XHTML. This ensures that your comments can be interpreted by any browser. In addition to standard HTML, you can use the following tags: CLASS, METHOD, PROPERTY, PARAMETER, QUERY, and EXAMPLE. (As with standard HTML tags, the names of these tags are not case-sensitive.) The most commonly used tags are described here. See the documentation for %CSP.DocumaticOpens in a new tab for details of the others.

CLASS

Use to tag class names. If the class exists, the contents are displayed as a link to the class' documentation. For example:

/// This uses the <CLASS>Sample.Person</CLASS> class.

EXAMPLE

Use to tag programming examples. This tag affects the appearance of the text. Note that each /// line becomes a separate line in the example (in contrast to the usual case, where the lines are combined into a single paragraph). For example:

/// <EXAMPLE>
/// set o=..%New()
/// set o.MyProperty=42
/// set o.OtherProp="abc"
/// do o.WriteSummary()
/// </EXAMPLE>

METHOD

Use to tag method names. If the method exists, the contents are displayed as a link to the method's documentation. For example:

/// This is identical to the <METHOD>Unique</METHOD> method.

PROPERTY

Use to tag property names. If the property exists, the contents are displayed as a link to the property's documentation. For example:

/// This uses the value of the <PROPERTY>State</PROPERTY> property.

Here is a multi-line description using HTML markup:

/// The <METHOD>Factorial</METHOD> method returns the factorial
/// of the value specified by <VAR>x</VAR>.

Compiling Classes

Caché class definitions are compiled into application routines by the class compiler. Classes cannot be used in an application before they are compiled.

The class compiler differs from the compilers available with other programming languages, such as C++ or Java, in two significant ways: first, the results of compilation are placed into a shared repository (database), not a file system. Second, it automatically provides support for persistent classes.

Specifically, the class compiler does the following:

  1. It generates a list of dependencies — classes that must be compiled first. Depending on the compile options used, any dependencies that have been modified since last being compiled will also be compiled.

  2. It resolves inheritance — it determines which methods, properties, and other class members are inherited from superclasses. It stores this inheritance information into the class dictionary for later reference.

  3. For persistent and serial classes, it determines the storage structure needed to store objects in the database and creates the necessary runtime information needed for the SQL representation of the class.

  4. It executes any method generators defined (or inherited) by the class.

  5. It creates one or more routines that contain the runtime code for the class. The class compiler groups methods according to language (ObjectScript and Basic) and generates separate routines, each containing methods of one language or the other.

    If you specify the Keep Generated Source option with the class compiler, you can view the source for the routines using the View Other Code command (from the View menu) within Studio.

  6. It compiles all of the generated routines into executable code.

  7. It creates a class descriptor. This is a special data structure (stored as a routine) that contains all the runtime dispatch information needed to support a class (names of properties, locations of methods, and so on).

Invoking the Class Compiler

There are several ways to invoke the class compiler:

  • From within Studio using the option in the Build menu.

  • From the Caché command line (in the Terminal) using the Compile() method of the %SYSTEM.OBJOpens in a new tab object:

     Do $System.OBJ.Compile("MyApp.MyClass")

If you use SQL DDL statements to create a table, the class compiler is automatically invoked to compile the persistent class that corresponds to the table.

Class Compiler Notes

Compilation Order

When you compile a class, Caché also recompiles other classes if the class that you are compiling contains information about dependencies. For example, the system compiles any subclasses of the class. On some occasions, you may need to control the order in which the classes are compiled. To do so, use the System, DependsOn, and CompileAfter keywords. For details, see the Caché Class Definition Reference.

To find the classes that the compiler will recompile when you compile a given class, use the $SYSTEM.OBJ.GetDependencies() method. For example:

SAMPLES>d $system.OBJ.GetDependencies("Sample.Address",.included)
 
SAMPLES>zw included
included("SOAP.Demo.LookupCity")=""
included("SOAP.DemoProxy.LookupCity")=""
included("Sample.Address")=""
included("Sample.Customer")=""
included("Sample.Employee")=""
included("Sample.Person")=""
included("Sample.Vendor")=""

The signature of this method is as follows:

classmethod GetDependencies(ByRef class As %String, Output included As %String, qspec As %String) as %Status

Where:

  • class is either a single class name (as in the example), a comma-separated list of class names, or a multidimensional array of class names. (If it is a multidimensional array, be sure to pass this argument by reference.) It can also include wildcards.

  • included is a multidimensional array of the names of the classes that will be compiled when class is compiled.

  • qspec is a string of compiler flags and qualifiers. See the next subsection. If you omit this, the method considers the current compiler flags and qualifiers.

Viewing Class Compiler Flags and Qualifiers

The Compile() method also allows you to supply flags and qualifiers that affect the result. Their position in the argument list is described in the explanation of the Compile() method. To view the applicable flags, execute the command:

 Do $System.OBJ.ShowFlags()

This produces the following output:

    b - Include sub classes.
    c - Compile. Compile the class definition(s) after loading.
    d - Display. This flag is set by default.
    e - Delete extent.
    h - Generate help.
    i - Validate XML export format against schema on Load.
    k - Keep source.  When this flag is set, source code of
        generated routines will be kept.
    l - Lock classes while compiling.  This flag is set by default.
    p - Percent.  Include classes with names of the form %*.
    r - Recursive.  Compile all the classes that are dependency predecessors.
    s - Process system messages or application messages.
    u - Update only.  Skip compilation of classes that are already up-to-date.
    y - Include classes that are related to the current class in the way that
        they either reference to or are referenced by the current class in SQL usage.

These flags are deprecated a, f, g, o, q, v
Default flags for this namespace =dil
You may change the default flags with the SetFlags(flags,system) classmethod.

To view the full list of qualifiers, along with their description, type, and any associated values, execute the command:

 Do $System.OBJ.ShowQualifiers()

Qualifier information displays in a format similar to one of the following:

            Name: /checkschema
    Description: Validate imported XML files against the schema definition.
           Type: logical
           Flag: i
  Default Value: 1

           Name: /checksysutd
    Description: Check system classes for up-to-dateness
           Type: logical
  Default Value: 0

           Name: /checkuptodate
    Description: Skip classes or expanded classes that are up-to-date.
           Type: enum
           Flag: ll
      Enum List: none,all,expandedonly,0,1
  Default Value: expandedonly
  Present Value: all
  Negated Value: none

Compiling Classes that Include Bitmap Indices

When compiling a class that contains a bitmap index, the class compiler generates a bitmap extent index if no bitmap extent index is defined for that class. Special care is required when adding a bitmap index to a class on a production system. For more information, see the section “Generating a Bitmap Extent Index” in the “Defining and Building Indices” chapter of Caché SQL Optimization Guide.

Compiling When There Are Existing Instances of a Class in Memory

If the compiler is called while an instance of the class being compiled is open, there is no error. The already open instance continues to use its existing code. If another instance is opened after compilation, it uses the newly compiled code.

Making Classes Deployed

You might want to make some of your classes deployed before you send them to customers; this process hides the source code.

For any class definitions that contain method definitions that you do not want customers to see, compile the classes and then use $SYSTEM.OBJ.MakeClassDeployed(). For example:

 d $system.OBJ.MakeClassDeployed("MyApp.MyClass")

For an alternative approach, see the article Adding Compiled Code to Customer Databases.

About Deployed Mode

When a class is in deployed mode, its method and trigger definitions have been removed. (Note that if the class is a data type class, its method definitions are retained because they may be needed at runtime by cached queries.)

You can open the class definition in Studio, but it is read-only.

You cannot export or compile a deployed class, but you can compile its subclasses (if they are not deployed).

There is no way to reverse or undo deployment of a class. You can, however, replace the class by importing the definition from a file, if you previously exported it. (This is useful if you accidentally put one of your classes into deployed mode prematurely.)

FeedbackOpens in a new tab