Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Database

Examining CA-Visual Objects


OCT95: Examining CA-Visual Objects

Rod is a Windows-developer consultant/trainer and principal of Software Perspectives of Richmond Hill, Ontario. He can be reached on CompuServe at 73020,2311 or by phone at 905-771-6675.


CA-VisualObjects(VO)is anapplication-development environment that sports an incremental, native-code compiler, visual painters and editors, and an advanced, repository-based storage system that manages all aspects of a project automatically. VO's underlying language, based on xBase, has extensions that allow optional strong typing and full object orientation.

VO's language, in fact, is the most recent incarnation of the Clipper language, the tool of choice for many hardcore DOS business application developers for over a decade. Clipper was originally designed by Nantucket to be a dBase III compiler producing stand-alone executables. Computer Associates (CA) bought Nantucket in 1992, thereby acquiring Clipper and a research project known as "Aspen," Nantucket's probe into GUI development. Aspen, which evolved into VO, was to be a completely object-oriented, repository-based, cross-platform application-development environment targeting the GUI environment and based on a strongly typed, object-oriented version of the Clipper language.

In this article, I'll examine the VO language, looking at characteristics such as memory management and calling conventions, and finally focusing on its object-oriented features.

Optional Strong Typing

CA-Visual Objects supports optional strong typing. It is hard for any classically trained programmer to do without variable and/or function strong typing. However, all xBase variables and functions are polymorphic in that they can change their types at any time. Consequently, variables and functions are not declared as a certain type in the original dBase language. Moreover, the actual declaration of the variable itself is optional, allowing for dynamically created variables; see Example 1(a). Millions of lines of xBase code use undeclared, dynamically created, polymorphic variables like this. However, this code is dated, and xBase developers now opt for lexically scoped variables using Hungarian notation. In Example 1(b), for instance, nX and cY are still polymorphic, in that their type can change. However, the adoption of Hungarian notation provides a kind of "soft" typing, at least in terms of readability.

VO also supports this kind of code, making it compatible with existing xBase systems. VO is, however, largely geared toward strong typing of its variables and functions, allowing variable references to be resolved by the compiler (generating native code) instead of the slower, run-time symbol table. Strongly typed code also lets the compiler perform standard type checking; see Example 2.

The VO language allows you to mix and match these coding styles. Thus, legacy xBase code can be ported (often without change) to the VO environment, with the option of later being incrementally strongly typed, module by module, when speed is an issue. New code should be strongly typed, but the compiler does not require you to strongly type variables or functions. The compiler also supports an automatic "type-inferencing" switch that infers the type of an untyped variable from its usage in the code and automatically generates native code for it. Thus, the single reference to the untyped variable i in a For loop would be inferred as type WORD or LONG, and the appropriate native machine code would be generated. Since the compiler supports varying degrees of optimization (loop, peep-hole, and the like), the code in Example 2 has the speed/size characteristics as comparable C/C++ code.

Automatic Memory Management

When it comes to memory management, VO inherits high-level base data types from CA-Clipper. Consequently, the allocation and deallocation of memory used by these variables is completely automatic. You declare and use your variables, then return from the function you are in when done. All garbage collection occurs automatically in the background, reclaiming all unreferenced memory. Functions such as new, delete, malloc, and free aren't an issue for regular VO development.

Additionally, dynamic data types (such as STRINGs and ARRAYs) can grow and shrink as necessary; see Example 3. All requests for dynamic memory are handled by VO's own virtual dynamic memory manager.

Calling DLLs with VO

In addition to data types that use dynamic memory, VO supports the traditional data types used to call any DLL function (including the Windows API). BYTE, INT, SHORTINT, LONG, FLOAT, REAL4, REAL8, WORD, DWORD, PTR, PSZ, and C-style STRUCTUREs can all be used to call any DLLed function when the appropriate prototype is provided. (VO also supports statically dimensioned arrays of any of these data types.) This means that you can go directly to the Windows API or third-party DLL for its functionality, although the preferred way is through the layer of abstraction afforded by a class wrapper. For example, Listing One is the VO equivalent of the SDK version of the ubiquitous "Hello World" program.

VO comes with a library of predefined prototypes for the Windows API. You simply specify the "Windows API" library in your application's search path (the equivalent of making it available to the linker), and you can call the API functions using the appropriate manifest constants as if they were any function available to the system. There are no header files of prototypes to manage, as they are precompiled as separate libraries. An import DLL prototype differs only syntactically from the equivalent C/C++ prototype. Example 4 shows typical function prototypes found in the supplied Windows API system library.

Calling Conventions

VO supports the calling conventions STRICT (C-style), PASCAL, Windows CALLBACK, and CLIPPER. The default calling convention is either STRICT or CLIPPER, depending on whether or not you strongly type the parameters in the function declaration.

The first three are the same as their C/Windows counterparts. CLIPPER, however, is a proprietary calling convention required to support CA-Clipper-style functions. This convention allows for flexible parameter passing for traditional, weakly typed CA-Clipper functions. For example, the caller of a function with a CLIPPER calling convention can pass a variable number of parameters or skip parameters altogether (using a NIL value passed as a place holder). This allows for code such as that in Listing Two. Note that declaring a function with the CLIPPER calling convention does not preclude its use of strongly typed variables or the typing of its return value. However, you cannot strongly type its parameters; all parameters passed are by definition weakly typed, polymorphic data types, which VO calls "USUALs" (or "ANYs"). Listing Three, a more efficient version of the ShowMessage() function in Listing Two, uses strong typing where it can, yet maintains flexible parameter passing.

Traditional calling conventions and VO's proprietary CLIPPER calling conventions differ in two ways.

  • VO function calls are resolved at run time via a symbol-table lookup. While slower than a lexically resolved function, this allows flexibility (especially for "data-driven" systems) since the decision of which function to call can be deferred until run time. Even so, symbol-table lookup is done not by function name, but by a 2-byte VO symbol (a special data type that is a direct representation of a Windows atom) representation of the function name. The lookup itself is accomplished by a hashing scheme that determines the code to execute.
  • Parameters passed to a CLIPPER function are not passed on the machine stack but on a separate eval stack. Thus, the prologue code of a CLIPPER function must call another internal run-time function to retrieve the parameters passed rather than obtain them from the stack directly. Unless the function is called repeatedly, this overhead is negligible, and the added flexibility that dynamic late-bound functions add is worth the performance hit.

Creating DLLs

VO also supports the creation of DLLs for distribution. Due to many of its dynamic capabilities, VO supports the creation of VO-only and foreign-hosted (traditional) DLLs. There are no restrictions on the code that can be placed in a VO-only DLL. Anything you can write with VO can be packaged with a click of a mouse into DLL form, callable by any other VO application. VO-only DLLs do not behave like typical Windows DLLs, however. Any application loading a VO-only DLL gets a fresh copy of the DLL's data segment. This means VO-only DLLs behave more like OS/2 DLLs, in that "static" data is not shared among the DLL's clients.

VO's foreign-hosted DLLs, however, are typical Windows DLLs in that the data segment is shared by all DLL users. But as you might expect, there are restrictions on what code can be placed in these types of DLLs. Basically, you cannot export functions with CLIPPER calling conventions. All export functions must be traditional, strongly typed functions with STRICT, PASCAL, or CALLBACK calling conventions because only another VO application can communicate with a DLL export function with the proprietary CLIPPER calling convention.

Creating DLLs with VO is straightforward: There are no .DEF files to define, nor do you explicitly declare functions as export. With VO, everything is exported unless you tell it otherwise by qualifying the function or data declaration as "STATIC," effectively scoping it to its own module. Turning a sample project from an executable into a DLL involves toggling a radio button and specifying the type and name of the DLL you wish to create. The DLL creation process creates the physical DLL file as well as an import library of VO prototypes for the exported declarations in the DLL. This import library can be used by other VO applications to statically link the DLL, if necessary. Alternatively, you can convert these generated prototypes to the equivalent syntax in C/C++, Visual Basic, PowerBuilder, and so on to use a VO foreign-hosted DLL in their respective languages.

Object Orientation

Like C++, VO is a hybrid language in that you can program in it with or without using classes, mixing object-oriented and procedural styles as necessary. However, VO implements classes differently from C++. For instance, class methods are not declared within the class declaration proper. Methods are declared and implemented as separate lexical units much like functions and the VO's repository must associate them with the proper class. (See the accompanying text box entitled "VO's Repository-Based Architecture.") The code in Example 5(a), for instance, is stored as two separate (yet related) "entities" in the system's repository. The build cycle automatically ties the two together when required. The method declaration/implementation need not be in the same library as the class declaration, although this is the norm. This architecture lets you add a method to a class at any time without changing or needing access to the source code for the class declaration. So, for example, you could add the method in Example 5(b) to VO's abstract framework class WINDOW in any module or library of my application; it would then automatically be inherited by all WINDOW's descendant classes.

To instantiate an object of a class, VO uses the object-instantiation operators "{}". To invoke a method or access an instance variable of an object, VO uses the send operator ":" as in Example 5(c).

VO classes do not have constructors in the traditional sense since the system automatically allocates dynamic memory for each new object. However, class designers can implement an Init() method to automatically call object initialization whenever an object of the class is instantiated. Parameters can be passed to the Init() method by including them between the {} operators; see Example 5(c). Additionally, default values can be specified in the instance-variable declarations for the class as compile-time constants; see Example 5(a).

VO supports a hybrid message-binding model: Instance variables are usually early bound, whereas methods are always late bound since they all use the CLIPPER calling convention. This combination provides speed and flexibility since the code within a method can run at native code speed, yet the decision as to which method to call can be deferred until run time.

VO methods are implemented the same way as CLIPPER functions. Moreover, a further optimization in the VO method-invocation implementation ensures that once a method has been looked up, its code is not executed by a traditional stack-based function call. Instead, it jumps directly to the code's entry point with a jmp instruction, executes the code, and then jumps directly to the original point of method invocation (not the launch point in the message-lookup routine). This avoids the stack set up and clean up of a typical function call. The result is fast, late-bound execution of all methods. (On a Pentium 90, I can issue one million method invocations on an object in just over two seconds.)

Inheritance in VO is clean and consistent, especially compared with C++. To call a method, you must send a message to an object, whether you are inside a class (in one of its methods) or outside. Notice in Listing Four the consistency of the method and function calling syntax regardless of where it takes place. Outside the class (in the Start function), the method is invoked by issuing the send operator against the object variable and the function is called directly. Inside a class method, a method invocation is issued by sending a message, this time to self (a method's equivalent to C++'s this), or super if you want to call up the chain of inheritance, and the function-calling syntax remains unchanged. Compare this with equivalent C++ code, which uses a different calling syntax for both functions and messages, depending on whether or not the call takes place inside a method.

As with C++, classes declared in VO are effectively user-defined types. Therefore you can use a declared class as a legitimate type declaration for your object variables. In Example 6, for instance, the first two variable declarations are essentially equivalent. They both define a variable and assign it a SomeClass object. Both variables are of a polymorphic (xBase) type, so their type can be changed to any other data type by simply assigning a new value at any time. The compiler cannot perform any type checking for these variables since the variable type can change at any moment. Any erroneous usage of these variables will only be found at run time via the run-time type-checking code necessarily generated for such variables. The only real difference between the two declarations is that the second explicitly documents this intention.

The AS OBJECT declaration tightens up the rules and allows the compiler to generate more-efficient code. It also provides compile-time type checking to ensure that only variables of type object are ever assigned to this variable. There's still plenty of opportunity to misuse this variable, however, since the compiler will allow any object (of any class) to be assigned to this variable.

The fourth variable declaration is more restrictive still: Any descendant of the ParentOfSomeClass base class can be assigned to the variable, but no other object type. The most restrictive of all is the last declaration, which allows only objects of type SomeClass (or its descendants) to be legal assignment values to this variable.

VO's typing flexibility treats all methods as the equivalent of C++ virtual methods in that the correct method implementation is chosen by the actual type of the object (not the declared type) while still allowing the flexibility of a more relaxed declaration. This eliminates any need to cast pointers to base classes into pointers to subclasses to ensure the correct implementation of an object is invoked. For example, VO's compiler allows the code in Example 7(a).

At first glance the assignment of a Dog{} object to a variable typed as Animal might look like a type conflict. However, the compiler correctly reasons that, semantically speaking, Dog is "a-kind-of" Animal, and can respond to the same messages as an Animal object. The compiler is smart enough, however, not to allow the reverse situation, and will generate a compiler error for code such as that in Example 7(b), which could result in an undefined situation. This flexible object-typing capability (along with each class's compiler-determined flat-message table) eliminates the equivalent of C++'s internal virtual pointer tables, yet still allows fast, type-safe, intuitive, and syntactically clean code.

C++ developers traditionally use Get/Set methods to access or assign to an object's protected or hidden member. These methods detract from the informational value of the class-user's code because a method-only interface cannot clearly indicate when and where the "state" of the object is being queried nor set as clearly as a mixed-instance variable/method interface.

VO's ACCESS/ASSIGN methods maintain the distinction between instance variable and method. Moreover, this distinction can be maintained without sacrificing the class's encapsulation. ACCESS/ASSIGNs provide safe access to a class's instance variables without resorting to presenting the class user with a method-only interface to the class.

So What's Missing?

VO is not without its shortcomings. The language is full featured, but some C++ features currently have no VO equivalents; typed pointers, for instance (VO's pointer data type PTR is always a C/C++ void equivalent). C++ supports function-pointer dereferencing, while VO only allows you to pass the pointer off to C/C++ for dereferencing. C++ supports scoped methods, but all VO methods are public. Nor does VO support multiple inheritance, unions, default arguments, variable-length parameter passing for the C-style calling conventions, compiler support for pure abstract classes (currently all classes can be instantiated in VO), const declarations, and compiler pragmas.

But VO's greatest problems reside outside of the language: The environment itself shows immaturity. The IDE, while functional, is not up to the standards of more mature development environments, and the class-framework library (a layer of VO classes interfacing with CommonView) can be awkward. In addition, some of the tools you get with the environment don't measure up. For example, VO's Window painter does not natively support VBXs (although third-party tools allow VO to call any VBX and enable the VO Window painter to visually position the controls on a form and generate the appropriate object-oriented code). The tools also lack multiprogrammer support--the current version of VO only supports single-user, local repositories. While you can import/export your applications, this isn't a substitution for the ability of more than one programmer to share a single repository of code (project).

Conclusion

CA-Visual Objects is an alternative to C++ for large-scale development under Windows. It boasts fast, native-code executables and an impressive language well-suited to building both application- and system-level software. VO has become my language of choice for general-purpose Windows development because its power/productivity ratio is better than other languages I've worked with.

VO's Repository-Based Architecture

CA-Visual Objects addresses the complexities of GUI development via an active repository known internally as "Adam." Adam represents a mass-storage system that holds as independent pieces of data every aspect of all your current projects--source code; object (compiled) code; menu, window, and icon resources; database definitions; library functions and class prototypes; define constants; and project settings.

The repository stores all the information necessary to describe the relationships between the various components of your project. This "meta" data is not required or used in your final application, but is necessary to automatically manage the project in a way similar to the traditional make utility. In this respect, VO offers the same kind of project management as other development environments, letting you work with various components of a project without dealing with the underlying files and dependency rules. But since VO does not manage physical files the way make does, its project-management capabilities exceed those of other systems, providing faster build times.

Perhaps the single greatest difference between VO and other approaches is that VO application components are not file based. With make utilities, your dependencies are formed at the file level based on time and date stamps. In VO, dependencies' granularity goes down to the "entity" level. In fact, VO has no concept of a source file--everything is measured in lexical entities, much smaller pieces of code that can stand alone as separate application components. Since relationship information is tracked at the entity level rather than at the source-file level, rebuilds are faster, leading to fast, "incremental" compiles.

VO's source-code editor is linked to its parser such that source code is not only color coded according to syntax, but also visually divided into separate entities. Language constructs stored in the repository as separate entities (segregated by the editor) include functions, procedures (functions without return values), class declarations, methods, globals, and defines (run-time constants). This means that if an application contains a define statement (such as DEFINEx :=10) on which three functions are dependent, the compiler will automatically flag the functions to be rebuilt if the define statement is changed. The module can have dozens of entities, but only the four would be flagged to be recompiled.

These relationships are not retained just within a given project. For example, assume the define statement is stored in a separate project of type library, and the library is included in another project's search path. Since both projects are stored in the same repository, the repository can track relationships across the projects; then, if the define in the library is ever modified, the three functions that reference the define in the dependent application are flagged for recompile. This fast, incremental compile--coupled with VO's ability to test all applications from within the IDE itself--gives the VO environment an edit/build/test cycle on par performancewise with interpretive environments.

Since the VO repository is "all seeing," it can provide some useful functionality. For example, since all code is stored in the repository, if you type a function name into the source-code editor and click the mouse to choose "expand prototype," VO will provide a template of the parameters of the function--complete with type information. This is great for those who can't remember the number, type, or order of parameters to functions.

The repository also provides browsing. Since the repository catalogs and categorizes all the different types of entities it stores, tools can be built to browse the contents of the repository on a per project basis or even across the entire repository. VO comes with two such browsers: the general-purpose Entity browser, which displays all of the entities grouped alphabetically within entity type; and the Class browser, which displays in a hierarchical fashion all classes and their associated methods and instance variables, within the context of their class-tree hierarchy.

--R.d.S.

For More Information

CA-Visual Objects

Computer Associates International

1 Computer Associates Plaza

Second Floor

Islandia, NY 11788-7000

516-225-5224

<a name="0186_007e"><B>Example 1:</B> (a) Declaration of variables is optional, allowing for dynamically created polymorphic variables; (b) nX and cY are local but still polymorphic.<a name="0186_007e"><pre>
(a)
FUNCTION SomeUDF
   x := 4          // dynamically create and initialize x
   ? x            // print x
   x := "Hello"          // x can change type at anytime
   ? x                    // print x
RETURN NIL

(b)
FUNCTION SomeUDF
   LOCAL nX, cY           // lexical scoped variables
   nX := 4
   ? nX
   cY := "Hello"
   ? cY
RETURN NIL
Example 2: Strongly type VO code
FUNCTION SomeUDF AS VOID
   LOCAL wX AS WORD
   LOCAL cY := "Hello" AS STRING
   wX := 4
   ? wX
   ? cY
RETURN
Example 3: Dynamic data types.
FUNCTION ComputeSquares( wStart, wLimit AS WORD ) AS ARRAY
  LOCAL aSquares := {}  AS ARRAY  // empty array - we will grow it as necessary
  LOCAL i AS WORD
  // Loop to "grow" the array
  FOR i := wStart UPTO wLimit
     AADD( aSquares, i*i )       // add an element to the array
  NEXT i
  ? STR( i-1 ) + " squares have been computed!"  // '+' concatenates strings
  WAIT
RETURN aSquares   // array's memory will NOT be automatically
                 // collected here (the normal case) since a
                 // reference to it is being passed out to the caller
Example 4: Typical function prototypes in the VO-supplied Windows API system library.
_DLL FUNCTION UpdateWindow( hWnd AS WORD ) AS VOID PASCAL:USER.124
_DLL FUNCTION UpdateWindow( hWnd AS WORD ) AS VOID PASCAL:USER.UPDATEWINDOW
Example 5: (a) Class declarations and methods are stored as two separate "entities" in repository; (b) adding a method to VO's abstract framework class WINDOW; (c) invoking a method.
(a)
CLASS SomeClass
   EXPORT  wIVar1 := 0 AS Word           // EXPORT  same as C++ public
   PROTECT cIVar2 := "Hello" AS STRING   // PROTECT same as C++ protect
   HIDDEN  lIVar3 := TRUE AS LOGIC       // HIDDEN  same as C++ private

METHOD SomeMethod CLASS SomeClass // Each method declares the class it belongs to
   ? wIVar1, cIVar2, lIVar3

(b)
METHOD NewWindowExtensionMethod CLASS Window
  // Do your extension stuff here

(c)
oWin := ShellWindow{ oOwner }    // Instantiate a descendant of WINDOW
oWin:NewWindowExtensionMethod()  // Invoke our newly added method
Example 6: The first two variable declarations are equivalent.
CLASS SomeClass INHERIT ParentOfSomeClass
FUNCTION Start
   LOCAL oObj1 := SomeClass{}
   LOCAL oObj2 := SomeClass{} AS USUAL
   LOCAL oObj3 := SomeClass{} AS OBJECT
   LOCAL oObj4 := SomeClass{}AS ParentOfSomeClass
   LOCAL oObj5 := SomeClass{}  AS SomeClass
Example 7: (a) VO's compiler will allow this code; (b) this code will generate a compiler error.
(a)CLASS Animal
CLASS Mammal INHERIT Animal
CLASS Dog    INHERIT Mammal

FUNCTION Start
   LOCAL oRexx AS Animal
   oRexx := Dog{}
   ... 

(b)FUNCTION Start
   LOCAL oRexx AS Dog
   oRexx := Animal{}
   ...

Listing One

FUNCTION Start
  LOCAL wc      IS _WINWNDCLASS
  LOCAL msg     IS _WINMSG
  LOCAL l       AS LOGIC
  LOCAL hwnd        AS WORD
    
  // Create a Windows window 'class'
  wc.style      := _OR(CS_VREDRAW, CS_HREDRAW)
  wc.lpfnWndProc    := @MainWndProc()  // pointers to functions 
  wc.cbClsExtra     := 0
  wc.cbWndExtra     := 0
  wc.hInstance      := _GetInst()
  wc.hIcon      := LoadIcon( _GetInst(),  PSZ(IDI_APPLICATION) )
  wc.hCursor        := LoadCursor( 0, PSZ(IDC_ARROW) )
  wc.hbrBackground  := GetStockObject(WHITE_BRUSH)
  wc.lpszMenuName   := PSZ("")
  wc.lpszClassName  := String2Psz( "HelloWorldClass" )
  // Register class with Windows
  IF !RegisterClass(@wc)
    RETURN FALSE
  ENDIF
  // Construct an instance of the window class just created
  hwnd := CreateWindow(;
    String2Psz( "HelloWorldClass" ),;
    String2Psz( "Hello World!" ),;
    WS_OVERLAPPEDWINDOW ,;
    100, 100, 500, 500,;
    0, 0, _GetInst(),;
    _MAKEPTR(0,0))
  // Bail out if something went wrong
  IF hwnd = 0
    RETURN FALSE
  ENDIF
  // Show and paint the window
  ShowWindow(hwnd, SW_SHOWNORMAL)
  UpdateWindow(hwnd)
  // Begin looping for messages
  WHILE GetMessage(@msg, 0, 0, 0)
    TranslateMessage(@msg)
    DispatchMessage(@msg)
  ENDDO
  // We are done so unregister class and exit
  UnregisterClass (String2Psz( "HelloWorldClass" ), _GetInst())
FUNCTION MainWndProc (hwnd AS WORD, message AS SHORTINT,;
            wParam AS WORD, lParam As LONG) AS LONG CALLBACK
  // Minimal Window Procedure Posts a Quit message on
  // receiving the WM_DESTORY message
  IF message = WM_DESTROY
    PostQuitMessage(0)
    RETURN 0L
  ENDIF
  // All other messages are defaulted to Windows base behavior
  RETURN DefWindowProc(hwnd, message, wParam, lParam)

Listing Two

ShowMessage()
ShowMessage(,,"A message at the default location")
ShowMessage(100,100)  
ShowMessage(,400)
...
FUNCTION ShowMessage( nXCoord, nYCoord, cMessage ) CLIPPER
  // Default parameters not passed to intelligent values
  IF IsNil( nXCoord )
     nXCoord := 0
  ENDIF
  IF IsNil( nYCoord )
     nYCoord := 620
  ENDIF
  IF IsNil( cMessage )
     cMessage := "One Moment Please..."
  ENDIF
  // Code to display message goes here...
  ...

Listing Three

FUNCTION ShowMessage( nXCoord, nYCoord, cMessage ) AS VOID CLIPPER
  LOCAL wXCoord, wYCoord AS WORD
  LOCAL cMsg AS STRING
  // Default parameters not passed to intelligent values
  // Stored in strongly typed variables
  IF IsNil( nXCoord )
     wXCoord := 0
  ELSE
     wXCoord := nXCoord 
  ENDIF
  IF IsNil( nYCoord )
     wYCoord := 620
  ELSE
     wYCoord := nYCoord
  ENDIF
  IF IsNil( cMessage )
     cMsg := "One Moment Please..."
  ELSE
     cMsg := cMessage
  ENDIF
  // Code to display message using strongly typed variables goes here...
  ...

Listing Four

CLASS BaseClass
METHOD SomeMethod CLASS BaseClass
  ...
CLASS DerivedClass INHERIT BaseClass
// Override SomeMethod of BaseClass
METHOD SomeMethod CLASS DerivedClass
   ...
   self:SomeOtherMethod()  // methods always require a message send
   ...
   SomeFunction()  // consistent function calling syntax 
   ...
   super:SomeMethod() // call base class method again with a message send
FUNCTION Start
  LOCAL oObj := DerivedClass{} AS DerivedClass
  oObj:SomeMethod()   // methods always require a message send 
  SomeOtherFunction() // consistent function calling syntax 


Copyright © 1995, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.