Developing with SQLWindows : Object-Oriented Programming

Object-Oriented Programming
This chapter shows how to use SQLWindows object-oriented programming features and covers these topics:
About object-oriented programming
Object-oriented programming (OOP) gives you the ability to:
Objects, classes, and inheritance
Object
An object is a software entity that combines:
An object can represent:
An object is an instance of a class. An object has the data and behavior of its class.
Objects that are instances of classes are called user-defined because you specify their behavior in classes.
Class
A class is a blueprint for making objects. In a class, you define a shared data structure and behavior for objects that you later create as instances of the class. You define the same data and behavior for a set of objects without duplicating the code for that data and behavior in each object.
The objects of a class share a common data structure and common behavior, but each object in a class has its own private data values.
Inheritance
Inheritance lets you define new classes in terms of existing classes. A new class that you derive from a base class inherits data and behavior from its base class. As well, a derived class can:
Extend the base class by adding new data and behavior. New data and behavior that you add becomes part of the derived class in addition to the data and behavior inherited from the base class.
Modify the base class' behavior by redefining inherited behavior.
SQLWindows uses the extended or modified data and behavior for the derived class, but the original data and behavior remain valid for the base class.
All of a derived class' data and behavior-new, modified, or inherited-is inherited in turn by its derived classes.
Designing classes and objects
Follow these general steps to design classes and objects:
1.
2.
3.
Writing applications with classes and objects
There are two main steps in writing an application that uses classes and objects:
1.
2.
Class inheritance
You can define classes in terms of other classes. One class inherits behavior from one or more other classes. There are two types of inheritance:
Single inheritance
With single inheritance, you derive a class from another class called the base class:
The word single in single inheritance means that a class has only one direct base class.
Multiple levels of inheritance are allowed. In other words, you can use inheritance to create a class hierarchy:
In the diagram above, class A is a base class of classes B and C:
Class A is a direct base class of class B and an indirect base class of class C
Class C is directly derived from class B and indirectly derived from class A
Multiple inheritance
With multiple inheritance, a class has more than one direct base class. In the diagram below, class D inherits behavior from two direct base classes, B and C:
Types of classes
SQLWindows has three types of classes:
Functional classes
A functional class supplies behavior through its functions. You use functional classes to:
A functional class can be:
If behavior needs to be shared by more than one window type, you can place the behavior in a functional class and then derive window classes from that functional class. The derived classes inherit the common behavior as well as behavior appropriate to their window type.
Window classes
There is a class for each standard window type.
A window class can be:
A base class only of the same window class type. For example, a data field class can be the base class only of another data field class
General window classes
With general window classes, you can create classes with both message actions and functions that you can use with any type of window. A general window class is like a functional class, except that it has a message actions section.
General Window Class: <name>
Description:
Derived From
Class Variables
Instance Variables
Functions
Message Actions
These are the inheritance rules for general window classes:
A general window class cannot be a base class of a functional class
You cannot create an instance of a general window class. You can only use a general window class as a base class of a concrete window class.
Types of objects
SQLWindows has three types of objects:
You use classes and inheritance to create two types of user-defined objects:
Visual objects are user-defined windows
A non-visual object is a user-defined variable (UDV)
User-defined windows
You create a user-defined window as an instance of a window class:
A user-defined window object is like a standard window object because it:
Is associated with a window handle that identifies it
You customize the window's behavior through its class definition.
User-defined variables
You create a user-defined variable (UDV) as an instance of a functional class:
User-defined variables are non-graphic objects. User-defined variables are different from other variables because the only way you can access a user-defined variable is through functions defined in its class. These functions are the user-defined variable's public interface.
You can place a user-defined variable in any variables section in the outline.
A user-defined variable:
Defining classes
You define classes in the class definitions section near the bottom of the global declarations in the outline.
The example below shows six collapsed classes: cls1, clsMDIFrame1, clsDataEntryForm, clsDlgStandard, and clsDatabaseField.
Global Declarations
...
Class Definitions
Functional class: cls1
MDI Window Class: clsMDIFrame1
Form Window Class: clsDataEntryForm
Dialog Box Class: clsDlgStandard
Data Field Class: clsDatabaseField
Later sections explain details about functional classes and window classes.
Defining functional classes
These are the outline items for a functional class:
Functional Class: <class name>
Description: <text>
Derived From
Class Variables
Instance Variables
Functions
The table below describes the outline items:
Class
Variables
Variables shared by all objects of this class.
Instance Variables
Variables replicated and private to each object of this class.
Later sections in this chapter explain class functions, class variables, and instance variables.
Functional classes cannot inherit from window classes, so functional classes do not have a message actions section.
About window classes
This section discusses characteristics that are common to all window classes. Later sections discuss the unique characteristics of classes for the different types of window classes.
Attributes
Attributes that you set are inherited by:
You can redefine inherited window properties in a derived class or in an object you create as an instance of the class.
For multiple inheritance with conflicting attributes, SQLWindows uses the first non-default setting it finds and ignores other non-default settings.
Class Default property
To inherit an attribute from base classes, select the Class Default property. Any other value you specify for an property overrides the value inherited from base classes. If all base classes define Class Default for an property, then SQLWindows sets the property to its internal default.
The attributes Object Title, Icon File, and Picture File Name use an empty string to indicate the Class Default. For example, if the Object Title of a class object contains nothing, it inherits the Object Title of its base classes.
Group boxes and background text class objects inherit their title from an property called Object Title in a class definition.
A class or class object can inherit almost every other attribute. Exceptions are Window Top and Window Left which are explained below.
Window position
You do not specify position information for a window class. You specify position information when you create a window that is an instance of the class.
Window size
For most class types, you can define the Initial Width and Height of objects. This sets the initial size of an object when created on a form window or dialog box using the Controls palette or Coding Assistant.
However, setting the initial width and height does not prevent you from resizing the object with the mouse. Once you resize an object, it no longer inherits the initial width and height from its base class (for example, subsequent changes to the object’s base class initial width and height do not affect the object’s size).
Classes have two additional properties that you can use to control the sizing behavior of objects: Width Editable and Height Editable:
Setting these to Yes before creating an object prevents that object from being sized differently than the class definition
Setting these to Yes after creating an object forces the size of all existing object to the Initial Height or Initial Width defined by the class
Window class definition outline items
All window classes have these common outline items:
The class type is to the left of the colon (for example, Data Field Class). The class name is to the right of the colon (for example, clsDataField1)
A list of class names from which this class is derived. Only list functional classes or window classes of the same window type as the current class. You can select classes in Coding Assistant.
Class
Variables
Variables shared by all objects of this class.
Instance
Variables
Variables replicated and private to each object of this class.
Later sections in this chapter explain class functions, class variables, instance variables, and message actions.
Types of window classes
The window class types are grouped into these categories:
There is separate class for each type of window. The definition for each type of window class is almost the same, but there are a few differences.
Child window classes
You can place objects that you create as instances of these classes in the contents section of top-level windows (dialog boxes, form windows, and table windows).
Outline items
The outline items for child window classes are:
<class type>: <class name>
Description: <text>
Derived From
Class Variables
Instance Variables
Functions
Message Actions
Multiple inheritance with list initialization
If a class for a list box or combo box inherits from more than one base class, SQLWindows uses the first list initialization and ignores the others.
Dialog box classes
A dialog box class has the same outline items as the child window classes. A dialog box class also has a contents section and can have a toolbar.
Form window, top-level table window, and MDI window classes
These classes have two items that child window and dialog box classes do not:
Outline items
These are the outline items for form window, top-level table window, and MDI window classes:
<class type>: <class name>
Description: <text>
Derived From
Menu
Toolbar
Class Variables
Instance Variables
Functions
Message Actions
The table below describes the outline items that are unique to these types of window classes:
Later sections in this chapter explain class functions, class variables, instance variables, and message actions.
Menu
If you do not define a menu in the class, then it inherits the complete menu definition of its base class.
If you define a menu in an application window class, then it completely overrides an inherited menu definition.
With multiple inheritance, if a derived class inherits a menu from more than one direct base class, SQLWindows uses the menu of the first class that defines a menu in the Derived From list and ignores the other menus.
Toolbar
If you do not define a toolbar in the class, then it inherits the complete toolbar definition of its base class.
If you define a toolbar in an application window class, then it completely overrides an inherited toolbar definition.
With multiple inheritance, if a derived class inherits a toolbar from more than one direct base class, you get an error when you compile unless you define a toolbar in the derived class.
Status bar
Use the Attribute Inspector to specify whether the window has a status bar.
The setting that you specify is inherited by derived classes and objects of the class.
You can redefine an inherited status bar setting in a derived class or in an objects of the class.
With multiple inheritance, if a derived class inherits conflicting settings for the status bar from its base classes, you get an error when you compile. If this happens, use the the Attribute Inspector to clarify the ambiguity in the derived class.
Class contents inheritance
With class contents inheritance, you can define window classes with child items that are common across all objects of the class. You can place child objects in the class definitions of:
A child item defined in the contents of a class or in a class’ toolbar is called a class child.
Inheriting class children
When you create an object that is an instance of a class with class children, the object inherits all the class children. The inherited children are called class child instances.
A class child instance inherits all the properties defined by the class child such as color, size, position, and data type. You can override these properties by editing the class child instance. The Attribute Inspector displays all the attributes of a class child instance.
Class child instances also inherit the actions defined by the class child. These are not displayed in the outline, but they execute at runtime. You can add additional actions to a class child instance to supplement the actions defined by the class child.
Multiple base classes
If you derive a class from multiple base classes that contain class children, SQLWindows combines the class children when you create an object that is an instance of the class. Here are examples of what you can do with multiple inheritance:
Define one class with class children in the toolbar and another class with class children in the contents. You then use both classes as base classes to derive a single class that has both sets of class children.
Class children
Add class children to a class definition by:
OR  
A class child can be a standard SQLWindows object or an instance of a class.
You can use the Attribute Inspector to change class child attributes.
Classes that you derive from a parent class inherit the class children. When you edit a class derived from a base class with class children, SQLWindows displays the class children of the base class. You can edit the attributes of inherited class children.
When you add or change a class child, objects that are instances of the parent class are affected immediately. For example, when you add a class child, SQLWindows updates all windows that are instances of the class.
You can define the tab order of class children by selecting Layout, Tab Order. SQLWindows uses this tab order when you create an object that is an instance of the class. However, later changes to the tab order in the class definition do not affect an object that is an instance of the class. You must use the Tab Order command on the object.
You refer to a class child by its name. For example, this statement is valid for a class child called df1:
Set df1 = 'abc'
SQLWindows uses the same rules for default names for class children as for other objects.
Class child instances
SQLWindows displays class child instances the same as other child objects.
The attributes of the object can be inherited from the class child or overridden (including the top and left coordinate of the class child). Most properties have a Class Default setting that uses the properties of the class child. Also, you can supplement or override actions inherited from a class child.
You can set the tab order of an object with class child instances by selecting Layout, Tab Order. By default, the tab order for all class child instances is the order defined in the parent class.
You cannot use the Object Duplicator to copy a class child instance.
A class child instance has a name with this format:
ClassChildName from ClassName: ObjectName
ClassChildName is the name of the class child as declared in the base class (such as df1). ClassName is the name of the class (such as clsForm). ObjectName is the name of the class child instance.
By default, ObjectName is the same as the ClassChildName.
You cannot edit ClassName and ClassChildName. You can edit ObjectName. For example, you can change ObjectName when it is a duplicate symbol.
You refer to a class child instance by its name. For example, this statement is valid for a class child instance called df1:
Set df1 = 'abc'
Example
In this example, the Class Definitions section of the outline has two classes:
A form window class named CForm1 with two class children. The push button (pb1) is a standard SQLWindows object and the data field (df1) is an instance of the class CdfDateTime.
Class Definitions
Data Field Class: CdfDateTime
...
Form Window Class: CForm1
...
Contents
Pushbutton: pb1
...
CdfDateTime: df1
...
When you create an instance of the form window class, it appears in the outline as:
CForm1: frm2
...
Contents
pb1 from CForm1: pb1
...
df1 from CForm1: df1
...
Naming child objects
If you use inheritance with window classes, give each child object in the class its final name immediately after you create it. This applies to both base classes and new child objects that you add to derived classes in the hierarchy.
SQLWindows does not propagate child object name changes to existing derived classes or instances. For example if you already have this hierarchy:
and you add a data field to CDerived, its default name is df1 in both CDerived and frm1. If you rename the data field in CDerived, the change does not propagate down to frm1 where its name if still df1. If you then add a data field to CBase, its default name is df1 in CBase, CDerived, and frm1.
If you rename the data field in CBase, its name does not change in CDerived and frm1. The result is two data fields in frm1 with the same name.
Creating user-defined objects
After you have defined classes, you can create objects that are instances of those classes.
When collapsed, a standard object definition and a user-defined object definition look similar. Each has:
For example, this is a standard object definition:
Data Field: df1
In the example above:
Data Field is the type of standard object
df1 is the name of the standard data field object
For example, this is a user-defined object definition:
ClassDbField: dbField1
In the example above:
ClassDbField is the name of a class. The class name is also called a type.
dbField1 is the name of the user-defined object.
When you define an object, you need to specify the object's name and other properties. The next sections describe the properties that you need to specify when you create an object that is an instance of a class.
Changing an object’s class
You can change an object’s class name by editing the title in the outline. For example, in an application that has the data field classes “clsdf1” and “clsdf2”, you can change “Data Field” to “clsdf1”, “clsdf1” to “Data Field”, and “clsdf1” to “clsdf2”.
Creating user-defined variables
To insert a user-defined variable (an instance of a functional class) in the outline:
or
You can insert a user-defined variable in any variables section of the outline.
User-defined variables are specified completely by their classes. The only properties you specify in the outline are the name of its class and the name of the object.
This outline fragment is for a user-defined variable (buf1) that is an instance of functional class (ClassBuffer):
Form Window: frmMain
...
Window Variables
ClassBuffer: buf1
...
A user-defined variable does not have a “value”. This means that you cannot use a user-defined variable:
The only interface for a user-defined variable is through the functions defined in its class. A later section discusses how to call class functions. You can access variables defined in a user-defined variable with object-qualified references:
If emp.salary > 2000
...
In the SAM_Create action below, a class named ClassBuffer has defined or inherited a function called insert that accepts a number and string as arguments:
Form Window: frmMain
...
Window Variables
ClassBuffer: buf1
...
Message Actions
On SAM_Create
Call buf1.insert( 37, 'Hello' )
Passed by reference
SQLWindows always passes user-defined variables by reference. A called function or top-level window can both read and write the user-defined variable’s instance variables.
Arrays of user-defined variables
You can declare instances of functional classes as arrays. The arrays can be dynamic.
You can use arrays of user-defined variables to create multidimensional arrays. The example below defines a functional class called Employee that contains instance variables for the employee’s name, department, and salary and a function called ChangeSalary:
Functional Class: Employee
...
Instance Variables
String: Name
String: Dept
Number: Salary
...
Functions
Function: ChangeSalary
Parameters
Number: NewSalary
Actions
Set Salary = NewSalary
...
The form window declares an array of Employee objects in its Window Variables section:
Form Window: frm1
...
Window Variables
Employee: emp[*]
...
You access instance variables or functions defined or inherited by a class of a user-defined variable by indexing the array name and qualifying it with a period and the instance variable name. For example:
emp[1].Name reference
emp[2].ChangeSalary( 100000 ) function call
You can also make qualified references and qualified function calls:
hWnd.frm1.emp[1].Name reference
hWnd.frm1.emp[2].ChangeSalary( 100000 ) function call
Assigning UDVs
You can assign one UDV to another using the standard syntax:
Set functionalClassVar2 = functionalClassVar1
functionalClassVar2 and functionalClassVar1 must be the same type, or functionalClassVar2 is of a type from which the type of functionalClassVar1 derives.
Important: Functional class variables are references, not actual values. After you perform an assignment, both variables refer to the same object and changes made through one variable changes the object itself and that change is visible in any other variable that refers to that object.
Example
Assume this class/object hierarchy in the code below:
Functional Class: fClassBase1
...
Functional Class: fClassBase2
...
Functional Class: fClassDerived1
...
Derived From
Class: fClassBase1
Functional Class: fClassDerived2
...
Derived From
Class: fClassBase2
These assignments are always legal:
Set vBase1 = vDerived1
Set vBase2 = vDerived2
These assignments are always illegal:
Set vBase2 = vDerived1
Set vBase1 = vDerived2
This assignment is legal only if vBase1 has most recently been assigned to a value of type vDerived1:
Set vDerived1 = vBase1
If vBase1 is actually a reference to an object of type fClassBase1, the assignment fails at runtime.
Type safe casting
A cast is an action that converts an object from one type or class to another. In SQLWindows, you can safely cast an object to a class higher in its class hierarchy (upcast) or lower in its class hierarchy (downcast).
Consider this class/object hierarchy:
Functional Class: fClassBase1
...
Functional Class: fClassBase2
...
Functional Class: fClassDerived
...
Derived From
Class: fClassBase1
Class: fClassBase2
...
fClassBase1: vBase1
fClassDerived: vDerived1
fClassDerived: vDerived2
Given the hierarchy above, this is an example of a type safe upcast:
Set vBase1 = vDerived1
This is an example of a type safe downcast:
Set vDerived2 = vBase1
Setting UDVs to null
Use the OBJ_Null constant to make a UDV null:
Set functionalClassVar1 = OBJ_Null
If you want to force release of an object safely, assign OBJ_Null to an object variable. This causes the same sequence as going out of scope and causes the referenced object to be freed if there are no other references to it in your application.
Checking for null objects
You can call SalObjIsNull to test an object to see if it is null:
If SalObjIsNull( functionalClassVar1 )
...
The code above is equivalent to:
If functionalClassVar1 = OBJ_Null
...
Dynamic instantiation
When you declare a UDV, SQLWindows creates it at runtime and you can refer to it. However, you can re-initialize it at any time using the new keyword:
Set functionalClassVar = new functionalClassName
The new keyword takes as an argument the type of object being created and returns a reference to an object of that type. functionalClassVar must be a variable of type functionalClassName.
Object destructors
SQLWindows maintains reference counts on UDVs, destroying them when their count drops to zero. If you want to perform some cleanup operation before an object is destroyed, then you must define a destructor for it.
Define a method in the class with the name “ObjectDestructor”. This method must not have a return type and must not have any parameters, local variables, or static variables. Otherwise, the compiler displays an error message.
SQLWindows destroys objects in three different ways:
1.
2.
When an object goes out of scope during execution (such as a local variable going out of scope when a function exits) and as execution terminates (this is relevant only to global objects and the objects to which they refer).
3.
Warning: When objects are destroyed in either of the first two cases, there is a SAL execution environment in which the ObjectDestructor function (if one exists) can be called. However, objects which are “leaked” (that is, there are no application references to them during shutdown) are only detected after the SAL execution environment has terminated, and so it is not possible to run the ObjectDestructor function. The memory used by these leaked objects will be freed, but the destructors are not executed.
Warning: Do not refer to globals in an ObjectDestructor method. The order in which globals are destroyed is unpredictable and a global that you refer to can no longer exist. You can control the order that globals are destroyed somewhat by assigning OBJ_Null to them.
SalObj* functions
You can use these SalObj* functions with UDVs.
Creating user-defined windows
To insert a user-defined window (an instance of a window class) in the outline:
Click the right mouse button, select New, and then choose a user-defined window in the menu
OR  
Select Component and then choose a user-defined window in the menu
OR  
OR  
OR  
Using the Controls palette
For child window types, the Controls palette displays the names of child window classes in a list box. For example, when the data field tool is selected, the Controls palette displays “Standard” for a standard data field and displays the class name for each data field class. Click on one of the names in the list before you place the object. The most-recently selected name is at the top of the list.
“List in Controls palette” attribute
When you set this to No, SQLWindows does not list the class name in the Controls palette. You can set this to No for classes that you only intend use as base classes. This keeps the Controls palette list from getting cluttered with classes that you do not intend to create instances of. This item defaults to Yes.
Components
A user-defined window object definition has the same components as a standard window definition for the same window type. For example:
A user-defined form window and a user-defined data field have message actions sections and you can set their window attributes
Overriding inherited attributes
You can use the Attribute Inspector to override inherited attributes.
Using message actions in classes
This section explains how a window class:
Message actions with single inheritance
The sections below explain how you can use inheritance with message actions in a class that has one direct base class.
Inheriting message actions
A derived class inherits all message actions that its base class defines or inherits. The inherited message actions execute automatically.
Overriding inherited message actions
If you want to change the behavior of an inherited message action in a derived class, you must redefine the message action. A message action in a derived class overrides the corresponding message action that its direct base class defines or inherits. This means that a base class' message action is not executed.
Invoking overridden message actions
Even if you override an inherited message action, you can still invoke it. To invoke the message action in the closest base class that defines a specified message, call SalSendClassMessage in a derived class:
SalSendClassMessage( wMessage, wParam, lParam )
You usually call SalSendClassMessage in a derived class' own definition of the same message action (as shown in the diagram). This is an example of how a derived class “adds behavior” to its base class’ behavior.
Normally, this is all that you need because the closest base class usually invokes its own base class' version of the message action. If you need to, you can invoke a message action in a distant base class with SalSendClassMessageNamed:
SalSendClassMessageNamed( clsName, wMessage, wParam, lParam )
SalSendClassMessageNamed is used more often with multiple inheritance.
SalSendClassMessage and SalSendClassMessageNamed are synchronous (they send the message directly to the receiving object). There are not corresponding asynchronous (post) calls (which send the message to the Windows message queue).
Message actions with multiple inheritance
The sections below explain how you can use inheritance with message actions in a class that has more than one direct base class.
Inheriting message actions
A derived class inherits all message actions that its direct base classes define or inherit.
Overriding inherited message actions
A message action in a derived class overrides the corresponding message action that its direct base classes define or inherit.
If more than one direct base class defines or inherits a message action, you get an error when you compile. You must redefine the message action in the derived class to avoid ambiguity.
If you redefine the conflicting message in the derived class and invoke the message in the base class, you can avoid getting an error when you compile. This technique is described in the next section.
Invoking overridden message actions
If more than one direct base class defines or inherits the same message action, you can select the message action to invoke in the derived class with SalSendClassMessageNamed:
SalSendClassMessageNamed( clsName, wMessage, wParam, lParam )
You usually specify the name of a direct base class and the SalSendClassMessageNamed function finds the closest base class of the specified class that defines the message action. You can also use this function to invoke a message action of a distant base class.
You usually call SalSendClassMessageNamed in a derived class' own definition of the same message action (as shown in the diagram). This is an example of how a derived class “adds behavior” to its base class’ behavior.
SalSendClassMessageNamed is synchronous (it sends the message directly to the receiving object). There is not a corresponding asynchronous (post) call (which sends the message to the Windows message queue).
Using message actions in objects
This section explains how a window object that is an instance of a class:
Inheriting message actions
A user-defined window inherits the message actions that its direct base class defines or inherits:
Overriding inherited message actions
You can override an inherited message action by redefining it in the object:
Invoking overridden message actions
You can invoke a class' message action that has been overridden by calling SalSendClassMessage:
SalSendClassMessage( wMessage, wParam, lParam )
You usually call SalSendClassMessage in an object's own definition of the same message action. This is an example of how an object “adds behavior” to its class’ behavior.
SalSendClassMessage and SalSendClassMessageNamed are synchronous (they send the message directly to the receiving object). There are not corresponding asynchronous (post) calls (which send the message to the Windows message queue).
Instance variables
Instance variables in a class are replicated for each object that you create. Each object has its own private copy of an instance variable.
The diagram below shows three user-defined data fields (df1, df2, and df3) that are instances of the data field class cls_df1. Each instance has its own copy of the instance variable str1.
If you define child objects with an instance variable, each child object has its own copy. For example, if you place a data field with an instance variable in a form and then create multiple instances of the form, each instance of the data field has its own copy of the instance variable.
Inheritance
An instance variable defined in a derived class hides one with the same name defined or inherited by a base class. If you hide an inherited instance variable, you can still access the base class' version by qualifying the name of the instance variable with the name of the base class.
With multiple inheritance, if more than one base class defines or inherits the same instance variable, you get an error when you compile if you refer to the instance variable. Eliminate the ambiguity by qualifying the name of the instance variable with the name of a class.
Using instance variables in classes and objects
You can refer to instance variables in two ways:
Internally for an object that operates on itself. “Operate on itself” means that the object accesses its own copy of instance variables.
Externally for an object that operates on another object.
Internal references
In an object that operates on itself, you can access an instance variable with an unqualified reference.
SQLWindows searches an object’s class for variables before searching the containing object or globals. The search of the object’s class includes the class’s base classes.
The this keyword
Each object which is based on a functional class has access to a reference to itself called the this reference.
There are two situations where you need to use the this reference:
To pass a reference to the current object as a parameter, specify “this”:
Service.add( this )
Normally, when you refer to a variable within a class you do not need to qualify the reference because the variable is in scope. However, if a method defines a variable with the same name as a variable with class scope (such as an instance variable), the class-scope variable is “hidden” by the method-scope variable. A hidden variable can be accessed in the method by preceding its name with the keyword this and the dot operator as in this.x. For example:
Functional Class: fTest
...
Instance Variables
String: str1
String: str2
Functions
Function: f1
...
Parameters
String: str1
String: str2
...
Actions
Set this.str1 = str1
Set this.str2 = str2
...
In the example above, the object’s str1 and str2 variables are set with the values of the method’s str1 and str2 parameters respectively.
External references
An object that operates on another object must identify the other object to access one of its instance variables. You can use:
An object-qualified reference
A fully class-qualified reference
A fully object-qualified reference
Object-qualified reference
If one object contains another object, then the containing object can refer to an instance variable in the contained object (target object).
Qualify the instance variable name with the name of the target object and a period:
Set obj.var = 'test'
An object-qualified reference tells the compiler to “look inside” the named object for the instance variable. The compiler searches for an instance variable with the specified name in the these places and in this order:
1.
2.
3.
You can also use an object-qualified reference to access an instance variable in a containing object. In the diagram on the next page, col1 uses:
An object-qualified reference:
Set tbl1.str4 = 'test4'
This tells the compiler to look in its containing object (tbl1) for the variable defined in the class MyTable.
An unqualified reference:
Set str4 = 'test4'
This sets the variable defined in the containing object (frm1).
An object-qualified reference:
Set frm1.str4 = 'test4'
This sets the variable defined in the containing object (frm1).
Fully class-qualified reference
Use this syntax when the object identified by the handle is an instance of the specified class (cls in the example below):
Set hWnd.cls.str5 = 'test5'
Fully object-qualified reference
In this syntax, you specify a window handle and an object name:
Set hWnd.obj.str6 = 'test6'
At runtime, SQLWindows searches for the instance variable in these places and in this order:
1.
2.
3.
If the window is an instance of a class, and both the window and the class define variables with the given name, then SQLWindows accesses the window variable instead of the class variable.
If the object you specify does not define or inherit the variable you specify, you get an error when you compile.
Class variables
Class variables are:
A class variable is like a global variable, but it is only visible to the defining class, derived classes, and objects of the class.
The diagram below shows three user-defined data fields (df1, df2, and df3) that are instances of the data field class cls_df1. Each instance shares the same copy of the class variable str1.
If you define child objects that have class variables, they all share the same copy. For example, if you place a data field with a class variable in a form and then create multiple instances of the form, all instances of the data field share the same value of the class variable.
Inheritance
A class variable defined in a derived class hides one with the same name defined or inherited by a base class. If you hide an inherited class variable, you can still access the base class version by qualifying the name of the class variable with the name of the base class.
With multiple inheritance, if more than one base class defines or inherits the same class variable, you get an error when you compile if you refer to the class variable. Eliminate the ambiguity by qualifying the name of the class variable with the name of a class.
Using class variables in classes and objects
You can refer to class variables for an object that operates on itself. You access a class variable with an unqualified reference.
Class functions
Functions in classes (along with message actions) determine the behavior of an object of the class. The outline syntax for a class function is the same as an internal function in the global declarations.
You can use class functions in both functional classes and in window classes.
Inheritance
A class function defined in a derived class overrides one with the same name defined or inherited by a base class. If you override an inherited class function, you can still access the base class version by qualifying the name of the class function with the name of the base class.
With multiple inheritance, if more than one base class defines or inherits the same class function, you get an error when you compile if you refer to the class function. Eliminate the ambiguity by qualifying the name of the class function with the name of a class.
Using class functions in classes and objects
You can call class functions in two ways:
Internally for an object that operates on itself. “Operate on itself” means that the called function accesses the calling object's own copy of variables.
Externally for an object that operates on another object.
Internal references
In an object that operates on itself, you can use:
An unqualified reference
A class-qualified reference
Unqualified reference
Only use an unqualified reference in:
Use the same syntax that you use to call an internal function:
Call classFunction( parms )
Class-qualified reference
Use a class-qualified reference:
When you normally use an unqualified reference, but you need to eliminate ambiguity. You get an error when you compile if more than one direct base class defines or inherits the same function.
Qualify the function name with the name of a class and a period:
Call className.classFunction( parms )
Late-bound unqualified reference
A late-bound reference calls the function defined by the class of the current object or defined by the object itself, instead of the function defined in the class that calls the function.
Important: You can only make a late-bound unqualified reference in a class that defines or inherits the named function.
Warning: Late binding adds flexibility, but it is significantly slower, so only use it when necessary.
Qualify the function name with two periods:
Call ..classFunction( parms )
In the example below, ClassA defines the functions F1 and Print. The F1 function calls Print.
If the current object's class is ClassB (derived from ClassA) and it makes the unqualified reference F1(), then the F1 function defined in ClassA executes because ClassB inherits F1 from ClassA. F1 calls ClassB's Print function and not ClassA's because F1 uses a late-bound reference, ..Print():
Form Window Class: ClassA
...
Functions
Function: F1
...
Actions
...
! Print the current object. If a derived class
! or the current object defines a function
! called Print, call it instead of the one
! defined in this class.
Call ..Print()
...
Function: Print
...
Actions
! Print ClassA's contents.
...
 
Form Window Class: ClassB
Derived From
Class: ClassA
...
Functions
Function: Print
...
Actions
! Print ClassB's contents.
...
External references
An object that operates on another object must identify the other object to call one of its functions. You can use:
A fully object-qualified reference
An object-qualified reference
A fully class-qualified reference
Fully object-qualified reference
If you know the object’s name, use this syntax:
Call hWnd.obj.func( )
Use this syntax when the calling object does not contain the target object.
The compiler searches for a function with the specified name in the these places and in this order:
1.
2.
3.
You do not need to know whether the object defines the function or inherits it. However, within a class you do not usually know the object name, so you can use a fully class-qualified reference (explained later).
Early-bound object-qualified reference
If one object contains another object, then the containing object can call a function of the contained object (target object).
Qualify the function name with the name of the target object and a period:
Call obj.Function( parms )
An object-qualified reference tells the compiler to “look inside” the named object for the function. The compiler searches for a function with the specified name in the these places and in this order:
1.
2.
3.
You can also use an object-qualified reference to call a function in a containing object. In the diagram on the next page, col1 uses:
Call tbl1.F()
This tells the compiler to look in its containing object (tbl1) for the function defined in the class MyTable.
Call F()
This calls the function defined in the containing object (Form1).
Call Form1.F()
This calls the function defined in the containing object (Form1).
Early-bound fully class-qualified reference
Use this syntax when the object identified by the handle is:
An instance of the specified class (cls in the example below)
The specified class defines or inherits the function:
Call hWnd.cls.function( parms )
This reference is more efficient than the late-bound references in the next sections.
Late-bound fully class-qualified reference
Use this syntax when the object identified by the handle is one of the following:
An instance of the specified class (cls in the example below)
For example:
Call hWnd.cls..function( parms )
At runtime, SQLWindows searches for the function in this order:
1.
2.
The class (cls2) of the object identified by hWnd, assuming cls2 is derived from cls
3.
4.
This is the least-efficient reference and is significantly slower than the other types of references.
Efficiency of external references for class functions
The diagram below shows the relative efficiency of external references for class functions:
References in container objects
An unqualified reference resolves to an instance variable or function inherited from the object’s class. For example, the reference to str1 below resolves to clsDf1.str1:
Data Field Class: clsDf1
Instance Variables
String: str1
...
Form Window: frmMain
Contents
clsDf1: df1
Actions
On SAM_Click
If str1 = 'abc' ! Refers to clsDf1.str1
...
Window Variables
String: str1
...
MyValue system variable
Use this system variable to get or set the value of an object in a window class when an object name is not available. Standard window objects usually do this by referring to their own object names. For example:
Set df1 = strValue
For SAL statements in a class, an object name is not available so you can use MyValue instead of an object name. For example:
Data Field Class: ClassStringField
...
Functions
SetValue
...
Parameters
String: strValue
Actions
Set MyValue = strValue
...
SalObjIsValidClassName
Call SalObjIsValidClassName to determine if the specified class name exists in the outline.
SqlVarSetup
Before GUPTA performs a SQL execute or fetch operation, it compiles the bind and into variables which is looking up the symbols and generating the code that gets the values (for bind variables) or that fetches the values (for an into variable). By default, GUPTA compiles:
You can change this default behavior by calling SqlVarSetup which saves the current execution context. When you execute or fetch later, GUPTA uses that execution context to resolve references to bind variables and into variables. This means that you can use bind and into variables in a different context than where you call Sql* functions. You must call SqlPrepare for the Sql Handle before you call SqlVarSetup.
Use this function to write:
This function does not affect the lifetime of the bind and into variables and does not guarantee that the variables will exist when you execute or fetch. You must ensure that the variables are still valid when you use them.
Developing with SQLWindows

GUPTA Technologies