CA-Visual Objects Language Basics

Copyright (C) 1997, Paul Piko



This paper discusses the basic components of the CA-Visual Objects language. It examines the core building blocks that the programmer can combine and manipulate to create CA-Visual Objects applications. Having become familiar with the language basics you will be able to move on to use CA-Visual Objects to its fullest.

This paper assumes a familiarity with basic programming skills and CA-Clipper/Xbase. During the development of CA-Visual Objects, efforts have been made to maintain a high level of compatibility with its predecessor CA-Clipper. However, the new technology and operating environment have necessitated change. In some areas this change is insignificant; in others it will appear foreign to the CA-Clipper developer.

The CA-Visual Objects Integrated Development Environment stores many details about the applications you work on. Gone are the days of juggling multiple PRG files and having to maintain Rmake files. CA-Visual Objects knows about each component you are using and its dependencies on other components. In CA-Visual Objects terms these components are called Entities.



To manage the various parts of an application, CA-Visual Objects introduces the concept of entities. Entities are discrete, logical portions of the application. They are also the smallest unit managed by the repository which is CA-Visual Objects’ application manager. The repository keeps track of the dependencies of one entity upon another and determines which entities need compilation when a change is made.

CA-Visual Objects’ standard entities are determined by special keywords. When CA-Visual Objects recognises one of these words within an application module it will automatically set up a new entity -- the source code editor it will draw a red line across to mark the boundary. The main entity statement keywords are:













You will already be familiar with a number of these entities, such as function and procedure, although there are some modifications to their usage. These, and the other entities, are discussed in more detail in the following pages.

Where to Start()

In CA-Clipper, applications are started by executing the code in the first named module in the link script. In CA-Visual Objects there is no programmer-maintained link script, nor any way or need to define the order code is linked. Because of this, every CA-Visual Objects application must have a starting point explicitly identified. This is done by creating an entity called Start(), which can be a function, a procedure or a method of the App class.

For example:


METHOD start() CLASS app

    TextBox{,"Welcome", "Hello CA-World"}:show()




CA-Visual Objects retains CA-Clipper’s work area based data management system. Fields in data tables can be manipulated in a number of ways:

  • Use the field name

? custname

// prints field custname in

// current work area as long as
// there is no memory variable
// custname


  • Prefix the field name by the alias

? customer->custname

// prints custname in the

// customer work area

  • Prefix the field name with _field

? _field->custname

// prints data field custname in

// the current work area


  • Declare the variable as a field

FIELD custname
? custname

// prints data field custname in

// the current work area

  • Use a data server

oCust := DBServer("customer")

? oCust:custname

// prints the data field custname

// via the server

Some of these techniques require assumptions about the development environment. As usual, the more explicit you can be in specifying your code, the fewer chances you have in experiencing unwanted side effects.


Programmatic variables

The topic of variable scoping in CA-Clipper became an issue with the release of Clipper 5.0. Previously Clipper programmers only had the use of the dynamically scoped Public and Private variables. Clipper 5.0 introduced option of lexically scoping variables through the use of Local and Static variable declarations. CA-Visual Objects carries over the dynamically scoped variables, but there are some minor changes in the use of Static, as discussed later.

When discussing scoping of variables there are two main concerns: the lifetime (how long the variable exists) and its visibility (where you can access the variable). In this section we see how each of the types of variables have different lifetimes and visibility.

Dynamically Scoped Variables

Both Public and Private variables can be created and altered at runtime. Publics can be seen anywhere in the application once they are created. Privates can be seen in the routine that creates them, and in any called routines. These variables are resolved at runtime, not compile time, and so incur a runtime overhead. The compiler is unable to perform some of the checking that is possible with lexically scoped variables, so you lose an opportunity for early capture of some errors. And because of the wide-ranging visibility of the variables, it can quickly become difficult to be sure of their exact value -- there are more possibilities that the variable could be changed in any number of routines where it is visible.

Lexically Scoped Variables

Lexically scoped variables are visible only within the unit of code in which they are declared; that unit of code can be just a function (e.g. Locals) or the whole application. (e.g. Globals).

All lexically scoped variables are resolved at compile time, producing a performance improvement over the dynamically scoped variables. The compiler can perform type checking on lexically scoped variables. The programmer can be more confident that no other modules will "accidentally" change the value of the variables because there is more control over which routines can actually see them.



Locals are defined as they are in CA-Clipper; through either the use of the LOCAL statement, or by specifying the variable name as a parameter for a FUNCTION, PROCEDURE, METHOD or code block. Locals are restricted in both lifetime and visibility to the entity in which they are defined.

The following code snippet shows variable x and y as parameters to function DoSomething. Within the function x and y are local variables. In addition, an extra variable called z is declared as a local variable.


FUNCTION DoSomething(x,y)



STATIC is used as a modifier to declaration statements. A STATIC LOCAL is the same as a LOCAL except that the variable will retain its value for the life of the application. Each time that declares a STATIC LOCAL is called the variable will still have the value from the previous call and not be re-initialised as a LOCAL would be. STATIC LOCAL can be abbreviated to STATIC.



CA-Visual Objects introduces Global variables. Global variables are declared by using the GLOBAL statement and exist as long as the application is executing. Globals are lexically scoped variables and so share their benefits, but have a visibility throughout the application.

Globals can be defined as STATIC GLOBAL -- they too exist for the life of the application, but are restricted in visibility to the module in which they are declared.

A Global variable is a CA-Visual Objects entity, so appear in the browsers separately to the program code:


Strong Typing of Variables

CA-Visual Objects will allow you to specify what data type a lexically scoped variable will be when it is declared. Normally, variables can be changed from one data type to another. For example, you can assign a string to a variable and then on then next line assign a numeric to the same variable. These variables are called dynamic, or polymorphic.

Strongly typed variables, however, cannot change data type, and the compiler can check to ensure that your code does not try to break this rule. So you have yet another safety net available to put in place for your application. In addition to gaining the benefits of lexical scoping, you can use this feature to improve the chances of finding errors while the application is being created, and so reduce the possibility of a user experiencing a runtime error.

Declarative Typing

Strong typing is straight forward to implement, as shown in the following statements. A variable declaration can be followed by the word AS and then the desired datatype.



    LOCAL dBirth AS DATE

    LOCAL cName := "Unknown" AS STRING



If no value is supplied upon declaration, strongly typed variables are initialised to a special value, a NULL version of whatever the data type in question is.


Typing parameters

You can also specify the data type for the variables in the incoming parameter list of a function or procedure:




Typing Function Return Values

The return value from a function can be specified to be a specific data type:



    RETURN Today()-dob



CA-Visual Objects provides constants by use of the DEFINE statement. This should not be confused with the CA-Clipper #define which is a preprocessor directive. The DEFINE statement declares a constant to the CA-Visual Objects compiler and specifies a value for that constant. DEFINEd constants have application-wide lifetime and visibility. This visibility can be restricted to just a module by using the STATIC modifier.

A DEFINE is a CA-Visual Objects entity, so appears separately from the rest of the code.


DEFINE pi := 3.14


Data Types

CA-Visual Objects has many more data types than CA-Clipper. A number of these data types are provided not only to give the programmer more choice, but also to provide compatibility with Windows DLLs. The new numeric and pointer data types should ring a bell with the C programmer. CA-Visual Objects still has the existing data types that make CA-Clipper so easy to use.

Strings, Dates, Logicals

These data types are handled in CA-Visual Objects in the same manner as they are in CA-Clipper.

A literal date can be specified by using the century, followed by a period, then the month followed by a period and finally the day. The following example clarifies this:


? SToD( "19941122" ) == 94.11.22

// .T.

// SToD is String to Date


Symbols can be used to represent strings in your programs. Operations are limited to assignment and simple comparison, but they yield a benefit of efficiency. Comparisons of symbols are faster than comparisons of strings.

Symbols are often referred to as Atoms in the Windows environment.

Symbols can be created in a variety of ways: using the hash character prior to a string (e.g. #MYSYMBOL), or using one of the functions String2Symbol(), String2Atom(), SysAddAtom(), SysAddAtomUpperA(). The following example shows a variety of symbol usage.





    LOCAL sProcessName AS SYMBOL


    sProcessName := #SALES

    ? sProcessName // SALES


    sProcessName := String2Symbol("Sales")

    ? cProcessName // SALES

    // note change in case


    sProcessName := String2Atom("Sales")

    ? cProcessName // Sales

    // case preserved


When any of these techniques is used, the supplied character string is added to a table called the Atom table. The Atom table is set up whenever your program is run. A value (the symbol) is returned that represents that string in the table. A given string is installed in the table only once. If the supplied string matches any entry already in the table then the symbol of that existing entry is returned.


You now have a wide range of choices for manipulating your numeric data. The decision of which numeric data type to use should be based on: the range of values you need to store, the precision required, and the demands of any functions you may be using requiring numeric arguments. DLLs in particular, and all functions with strongly typed arguments must be passed appropriate values or the compiler, or runtime will complain.

The following table summarises the differences between the numeric data types:

CA-Visual Objects Numeric Data Type

Size (bits)


Significant digits

Corresponding C Data Type

Sample usage



-2,147,483,648 to


int (in 32 bit only)

Large numbers



1.2e-4932 to


long double




-32,768 to


int (in 16 bit only),

Counting, small numbers,
loop control



-2,147,483,648 to


int (in 32 bit only)

Large numbers,



0 to 255


unsigned char

Small numbers,
PC character set



0 to 65,535


unsigned int (in 16 bit),
unsigned short

Larger numbers,



0 to 4,294,967,295


unsigned long,
unsigned int (in 32 bit)

Astronomical distances



3.4e-38 to






1.7e-308 to






Code blocks

Code blocks are pieces of compiled code referenced by a variable. They can be executed on demand through the use of the Eval() function, or by functions that receive code blocks as parameters. Code blocks can be created at compile time, or produced at runtime by macro compilation of a character string.

Code blocks take a list of arguments (none or more) between vertical bars. Following the bars is a list of expressions, separated by commas. Each expression in turn is executed, and the value of the last expression becomes the return value of the code block.

Extra parameters between the vertical bars can be used as variables local to the code block.

You can have more than one reference to a code block by making a copy of the variable that contains the reference.

Visibility of Variables

How it works in CA-Clipper...

Any variables that are in scope when the code block is defined is accessible within the code block. This holds regardless of where the block is executed.

This means locals and statics can be "exported" outside their normal scope through a code block.

And, if a code block is returned from a lower level routine, any local variables it referenced will still be accessible, despite the fact the sub routine finished executing. This is possible because internally CA-Clipper keeps a copy of those locals; they are called detached local variables because they are no longer are "connected" to their origin, but will exist as long as the code block references them.

Any local variables that are referred in a code block will remain accessible

If a code block references anything locally a copy is kept along with the code block

Any Static functions that are in scope when the code block is defined are also valid within the code block. This is true even if the function is not in scope to the current routine - the code block will still be able to execute it.

And how CA-Visual Objects handles it...

CA-Visual Objects will also allow you to reference locals variables inside your code blocks, however there is an important difference from how CA-Clipper handles them. CA-Visual Objects does not have detached local variables. Any reference to a local variable is only valid as long the variable survives in its natural lifetime.

In other words, as long as the variable exists it can be referenced in the code block. So a local variable can be used as long as routine that creates it is somewhere in the call stack. A STATIC local can be used any time after it has been created because it is retained even after the routine finishes. GLOBALs can be used if they are visible because their value is always retained.

Using a variable that no longer exists will result in a runtime error "Illegal Reference SubSystem VO-CODE".

Static functions that are in scope when the code block is defined are also able to be used within the code block. This is true even if the function is not in scope to the current routine - the code block will still be able to execute it.

Other CA-Visual Objects points

Trying to reference self in a code block will yield a compiler error. To use the current object you will need to either assign self to a variable and use the variable in the code block (remembering the points above), or pass the object in as a parameter to the code block.

Code blocks created at runtime are actually objects of the class _CODEBLOCK. This is important to realise, especially if you are strong typing your variables, because compile time code blocks are CODEBLOCK data type. You have to make sure you use the right data type depending on when the code block is created.

You can specify that a method or function can EXPORT LOCAL. This does not change the functionality of the code but speeds the compile. E.g.



Macros and Code Blocks

How CA-Clipper does it...

When a macro is used within a code block there are two points that it can be expanded. The first is when the code block is compiled, and the second is each time the code block is executed. Expansion of the macro at compile time is called early evaluation, while execution time expansion is called late evaluation. Early evaluation can be chosen by just using the & by itself. For example:



c := "5"

bToDo := {|| &c }

? eval(bToDo) // Prints 5


c := "10"

? eval(bToDo) // Still 5

To obtain late evaluation, use a macro expression, the & applied to a parenthesized expression:


c := "5"

bToDo := {|| &(c) }

? eval(bToDo) // Prints 5


c := "10"

? eval(bToDo) // Prints 10

Character strings that contain a representation of a code block can be macro compiled to produce a code block, as we will see in later examples.

And how CA-Visual Objects does it...

CA-Visual Objects will always evaluate the above compile-time macros at execution time, and so the results of the print statements would be 5, 10, 5, 10.

It will, however, handle runtime code blocks in a manner similar to CA-Clipper - macro variables (without the parentheses) will be evaluated early, and macro expressions will be evaluated late. Examine the following example:


// Early evaluation...

c := "5"

cToDo := "{|| &c }"

bToDo := &cToDo

? eval(bToDo) // Prints 5


c := "10"

? eval(bToDo) // Still 5


// Late evaluation...

c := "5"

cToDo := "{|| &(c) }"

bToDo := &cToDo

? eval(bToDo) // Prints 5


c := "10"

? eval(bToDo) // Prints 10

CA-Visual Objects also provides the MCompile() function to compile a string for later execution by MExec(). To achieve results like the previous CA-Clipper example we need code something like:


c := "5"

cCompiled := MCompile(c)

bToDo := {|| MExec(cCompiled) }

? eval(bToDo) // Prints 5


c := "10"

? eval(bToDo) // Still 5



Dynamic Arrays

Arrays in CA-Clipper and CA-Visual Objects are very powerful tools, but you do need to take some care to make sure you are using them properly. Arrays have some special attributes which you have to take into consideration whenever you reference them.

When an array is created, the variable used to access it does not really contain the array. The variable is a "name" that can be used to refer to the array. Think of the array itself as something separate from the variable, an section of memory. The variable contains a value that "points" to the place in memory that the array resides.

For example:


aExample := { 1, 2, 3 }

This example will create an array { 1, 2, 3 } in memory and give it a name "aExample".

When we assign the array to another variable, we are giving the array another "name". E.g.:


aNother := aExample

This code will give our original array an extra name, so that either "aNother" or "aExample" refer to the same array. Note that when I say the same, I do not mean another array with similar values, I mean the one array, at the one point in memory. "aNother" and "aExample" are the one array, not two separate arrays. Both variables will contain the same value, the place in memory that the array occupies.

An array exists as long as there is anything that references it. As soon as there are no references to an array the memory can reclaimed by the internal memory management system.

Any array element can contain a reference to another array, allowing construction of complex structures.

Dimensioned Arrays

As mentioned previously CA-Visual Objects’s dimensioned arrays are arrays whose dimensions are fixed at compile time. This means that when you declare the array you have to supply the number of elements it can contain. Dimensioned arrays are declared using the DIM keyword:


LOCAL DIM aMyArray[10]

This examples creates a dimensioned array of 10 elements. You can also optionally strongly type the array so that it can only contain elements of the specified data type:



Dimensioned arrays are restricted to three dimension only, and cannot be resized.

So why use dimensioned arrays? They are more compact and faster than their dynamic counterparts, and are compatible with C and the Windows API. BUT they are not the same as dynamic arrays and cannot be used in any of the functions expecting arrays as arguments. The UsualType of a dimensioned array is not ARRAY, but the type of the first element.


Pointers are memory addresses. There are two types of pointer in CA-Visual Objects. The PTR data type is a general memory address representing the location of a memory variable or function. A PSZ is a specific pointer to an ascii zero terminated character string. Pointers are mainly used in calling functions from interface external to CA-Visual Objects.



Classes can be used as data types in CA-Visual Objects. In the most general case, a variable can be declared as being some type of object by using the AS OBJECT clause. You can be more specific and actually supply the name of the class the variable must belong to. The class may be any class that is known to the application, even classes you have defined. For example:



// oSomething can be any sort

// of object



// oEmployee is an object of the

// Person class


A structure is a named group of variables. You declare a structure by using the STRUCTURE keyword, followed by the name you want use. The variables making up the structure are then declared using the MEMBER clause, as shown in the next example:





Once declared, a structure can be used as a data type for variables. There are two ways to create structure variables, using either the AS or the IS keywords.


LOCAL struct1 IS holiday

LOCAL struct2 AS


Structures declared using the IS keyword will automatically allocate memory. Structures declared using AS need the programmer to allocate and deallocate the memory (using MemAlloc/MemFree) for the variable before and after using it.

In CA-Visual Objects 2, a structure can be instructed to align each new member at a specified byte boundary.


CA-Visual Objects 2 introduces unions to the language. Unions are like structures, except each member starts at offset zero. Each member can be used as a different way of looking at the same memory location.


The USUAL keyword is used to explicitly specify that the variable is to be polymorphic and not of a particular data type.


String, Date, Logical

These operators perform as they do in CA-Clipper.


The numeric operators of CA-Clipper have retained their unction in CA-Visual Objects. There are, however, additional operators that act on numerics; the Bitwise operators.


The Bitwise operators are >>, <<, _And() and _Or(). They act on INT, SHORTINT, LONGINT, BYTE, WORD and DWORD data type variables.


Assignment operators perform as they do in CA-Clipper. However, in CA-Visual Objects there is an option to enable the use of the single equals sign as an assignment operator. This option by default is not selected, and so the single equals sign acts only as a comparison operator.


Instance variables and methods of an object are accessed via the send operator :. For example:


oPerson:name := "Fred"



Variables within structures are accessed using the dot operator. For example:






LOCAL struct1 AS holiday


struct1.dEvent := today()


Calling Conventions


As discussed earlier, there are choices in how functions and their parameters are declared. The traditional situation, where parameters are untyped and optional is called the CLIPPER calling convention. With these functions you can pass arguments by reference or value. The following are both Clipper calling conventions functions:


FUNCTION Test1(x,y)








Once you start to type the arguments of a function you are forcing STRICT calling convention, which provide speed improvements.







Under CA-Visual Objects 1 all parameters must be supplied. Under CA-Visual Objects 2 parameters can be skipped as long as a default value has been defined in the function declaration:




The default value wil be used in place of the skipped paramater.

Unlike Clipper functions, you cannot use Strict functions in macro expressions. Parameters defined by AS must have arguments passed by value, while those defined by REF must have arguments passed by reference. The following are both Strict calling convention function declarations:



Pascal convention is similar to Strict, except it is compatible with the calling of functions under Windows.



Callback convention is similar to Pascal and identifies a function that will be called by Windows. It includes some special Windows handling before and after the function call. There are many places in the Windows API where you need to provide a function that will be called by Windows itself - this is the convention to use for that type of function.

Classes and Objects

CA-Visual Objects includes an object-oriented language. The entities involved in defining the classes, their behaviours and properties are CLASS, METHOD, ACCESS and ASSIGN.

The programmer can define classes of objects using the CLASS statement.


CLASS Person

If the structure of a new class is based on an existing one, the INHERIT clause to specify the class’s superclass.


CLASS Employee INHERIT Person


Directly after the class statement the any instance variables are declared by using any of the four types of instance variables declarations:

  • INSTANCE: Visible in the class and subclasses but not externally. However, they can be overridden by ACCESS and ASSIGN statements of the same name as the variable.
  • PROTECT: Visible in the class and subclass and cannot be overridden by ACCESS and ASSIGN.
  • HIDDEN: Visible only within the class, not subclasses, and cannot be overridden.
  • EXPORT: Visible outside the class.

The actions an object of a class can perform are described by using the METHOD statement. This is like defining a function that only objects of the class can carry out. When you create a method you also have to specify which class it belongs to:


METHOD Transfer CLASS Employee

An object is created by making an assignment to a variable and specifying the class name followed by {}.


LOCAL oEmp := Employee{}

If a class has a method called INIT then when an object of the class is created that method will automatically be executed.

An object can be instructed to perform one of its methods in the following manner:




The ACCESS and ASSIGN statements define special methods to control the updates and visibility of instance variables. The thing that sets these methods apart from other methods is the way you cause them to be executed. We saw the usual way to invoke a method above. ACCESS and ASSIGN are invoked by using the method name as if it was an instance variable. For example, the following defines an ACCESS method called age. (birthdate is an instance variable of Employee)


ACCESS age CLASS Employee

local n := Today()-birthdate

return n

This method is invoked by using the name as if it was an instance variable:


oEmp := Employee{}

? oEmp:age


Similarly, the ASSIGN method could be defined as follows:



ASSIGN age(nDays) CLASS Employee

birthdate := Today()-nDays

return birthdate


The ASSIGN is then invoked as if age was an instance variable:


oEmp:age := 32*365

Visibility of Methods, Accesses and Assigns

In CA-Visual Objects 2, you can restrict the visibility of Methods, Accesses and Assigns by using the PROTECT and HIDDEN keyword, e.g.:




The same rules of scoping apply here as for instance variables: PROTECT can be called by the class or its subclasses. HIDDEN can be called only by the class itself.

Early Bound Methods

CA-Visual Objects 2 allows early bound methods. These methods are resolved at compile time and provide improved performance. In order to do this, the variable containing the object must be strongly typed inaddtion to the method being strongly typed.


Anything placed into a Textblock entity will be ignored by the compiler. A textblock can be used for documentation purposes. It is also a quick way to "comment out" a routine by preceding the first entry in the entity with the Textblock keyword.


Windows resources such as accelerators, bitmaps, dialogs, icons and menus can be specified to the compiler by using the Resource statement. When you are creating your resource statement you need to follow the rules set out in the SDK because CA-Visual Objects actually passes the definition statements through to the Windows Resource compiler. A sample is shown below:





RESOURCE test DIALOG 9, 25, 223, 130


CAPTION "Dialog Caption"

FONT 8, "MS Sans Serif"


CONTROL "Hello", TEST_TEXT1, "Static", SS_LEFT | WS_CHILD, 65, 32, 45, 12




Reserved Words

CA-Visual Objects is more restrictive than CA-Clipper on the use of reserved words as variables. In CA-Clipper it is possible to use reserved words as program variables and database fields. CA-Visual Objects does not allow any reuse of reserved words.

For example, this means that in CA-Visual Objects you cannot use a program variable called DATE, or a data field called CLASS.

In the case of data fields, you have to do some work to access the data without changing the file structure. The DBFieldInfo function allows you to specify an alternate name for a field, which you can then use in the code in place of the reserved word.


DBFieldInfo(DBS_ALIAS, ;


The reserved words are listed in an appendix to the Programmer’s Guide, Volume III.

CA-Visual Objects 1 Preprocessor

The Preprocessor in CA-Visual Objects 1 is not as extensive as the one supplied with CA-Clipper.

We have already seen the DEFINE statement that supersedes the preprocessor’s #DEFINE.

There is no replacement for the pseudo-function facility of the #DEFINE. You are to use real functions instead. Fortunately CA-Visual Objects producing native code executables, and has very little overhead in function calls.

The #include is replaced by the ability to specify any User Defined Command (UDC) files, and any additional libraries, as part of an application’s properties.

#IFDEF and #IFNDEF are still present and work as they do in CA-Clipper.

UDC files will accept the equivalent to #(x)command, but does not support #(x)translate. You cannot place command definitions anywhere except in UDC files.

The preprocessor is no longer recursive, so some of the fancier thing done in CA-Clipper are not possible in CA-Visual Objects.

The UDC tester provided is a nice interactive tool to try out your new command and see the results without having to actually create a UDC file and install it in an application.

The UDC shown in the following example of the UDC tester is based on the code snippet presented in the section discussing use of reserved words in data files:



CA-Visual Objects 2 Preprocessor

Extending on the features available in version 1, CA-Visual Objects 2 allows you to specify Clipper-style CH files at the application level. Prior to being passed to the internal compiler, your source code is directed through a version of the Clipper preprocessor. Unfortunately, there is no in-built way to view the preprocessor output, although this can be accessed by third party tools.


This paper has shown the different types of components available to the CA-Visual Objects programmer. It has reviewed datatypes, strong typing, variable visibility and lifetime. We also covered writing CA-VO applications from scratch, without the generated framework.