C5 Memory Management

Clipper Memory Management

Once upon a time ..

PCs has a 640 KB memory limit and Clipper programmers was must struggled with errors such as Memory overflow, Conventional memory exhausted, Stack overflow and so on…

This article mainly focused on this subject.

In our modern era we haven’t such problems, because we have gigabytes of memory installed in our computers. Morever OS’s offers to us immense memory management possibilities transparent to us. Like we have unlimited memory to use.

But nothing can be unlimited …

This article of Roger Donnay has very useful info about efficient ways to using memory; and especially variable handling.

The Art Of Simplicity

A discussion of how to create objects with Clipper using arrays, and ordinary Clipper syntax. Has several good examples.

An Introduction into Object Oriented Programming.

To me, the challenge of programming is in finding a simple clean way to implement a program. Making sure no matter how complex the specs are, the code itself stays small, strait forward, and easy to maintain.

To illustrate how to reduce the complexity of things, lets examine the box drawing routines. Normally to display a nondestructive box on the screen you write something like this:

 old_row:=row()
 old_col:=col()
 old_cursor:=setcursor()
 old_screen:=savescreen(10, 20, 16, 59)
 old_color:=setcolor("w+/n, w+/r")
 @ 10, 20, 16, 59 BOX 'ÚÄ¿³ÙÄÀ³ '

Then when you are done and wish to remove the box, you reverse the procedure:

 restscreen(10,20,16,59,old_screen)
 setcolor(old_color)
 setcursor(old_cursor)
 setpos(old_row,old_col)

This scheme results in using 9 lines of code, 5 memory variables, and requires that the programmer maintain the box coordinates in 3 different places. After going through this procedure a few times I started wondering if there was a better way of doing this.

When I tried to solve this problem, I had several false starts. I created a procedure to display the box that saved all the variables to statics, and the next time it was called it would restore the box. That didn’t work too well since I often wanted more than a single box on the screen.

Then I tried saving the memvars to an array that I used like a stack. But that didn’t work out too well either, since it required that all boxes be removed in the same order that they were created.

Then I decided that all the memvars being used to store the box information belonged in the calling routine, where they had been all along. Despite the fact that this seemed to bring me back to square one I continued with this train of thought.

If I stored all the memvars being used by the box routine in an array, then all the memvars could be stored in a single package, and passed to the calling routine without complications:

aBox := CreateBox(10, 20, 16, 59, "w+/n, w+/r")

And when I no longer needed the box and wished to restore the original screen:

DestroyBox( aBox )

Please compile DEMO1, to see the basic box functions.

CreateBox() and DestroyBox() are used to replace 10 lines of code, and the array aBox was used to replace 5 variables. Putting all the data into the array aBox and handling only the array, makes things much simpler.

Now that we have developed this technique, we could theoretically create a number functions that work together, like CreateBox() and DestroyBox(), and use the data contained in aBox. And in the file BOX.PRG, I have a group of sample functions that do just that:

 CreateBox()
 DestroyBox()
 BoldBox()
 MoveBox()

Another benefit of this technique, is that we can have multiple arrays that each correspond to separate boxes, and use them all at the same time.

For example, we could write a program with a couple of boxes:

 aBox1 := CreateBox( 05,  26,  20,  53,  "w+/n, w+/n" )
 aBox2 := CreateBox( 10,  20,  16,  59,  "w+/b, w+/b" )

To move the second box:

MoveBox( aBox2,  -08,  -18 )

Then we could give the second box a highlight:

BoldBox( aBox2, "w+/b, w+/b" )

Then to remove both boxes:

 DestroyBox(aBox2)
 DestroyBox(aBox1)

This example does some fairly complex things, and it does so, in only six lines of code. To run this example, compile the file DEMO2.PRG.

This programming technique has a name, it is called Object Oriented Programing (OOP).

According to OOP terminology the arrays aBox1 and aBox2 are objects, and the functions CreateBox, MoveBox, BoldBox, and DestroyBox are methods.

Objects are collections of related data, or in dBase terminology, arrays of related memvars. In our example, aBox1 and aBox2 are qualify as objects since they contain related data (the coordinates of the box, the original color, cursor position, cursor status, etc.).

In object oriented programming, several instances of an object can be created, and later destroyed when we are finished with them. In our example, aBox1 and aBox2, constitute two separate instances of box objects.

If you look at the example in DEMO3.PRG, you will see that the program creates an array of four box objects, and four separate instances of the box object are on the screen at once.

A methods is a special type of function. Methods are functions that are grouped together, and manipulate the same data objects. In the file BOX.PRG, you will see the code for four methods that use the box objects ( CreateBox, DestroyBox, BoldBox, MoveBox )

CreateBox is a special type of method called a constructor, because it creates a box object and initializes it.

DestroyBox is a special type of method called a destructor, because it destroys a box object and frees of the memory that the box object used.

Every time we call a method / function, we pass it the object we want the method to manipulate. In our example, we have two objects, aBox1 and aBox2. To move the first box, we called the method MoveBox() like this:

MoveBox( aBox1, 1, 1 )

And to bold the second box, we called the method BoldBox() like this:

BoldBox( aBox2, "w+/b,w+/n" )

The constructor CreateBox doesn’t need to be passed the object, because the constructor creates the object.

Now that you understand what an object is, you can create additional functions / methods that use the box object. And hopefully go on to create your own objects and methods.

Cynthia Allingham, 1991

/***
*
* BOX.PRG
*
* Written By: Cynthia Allingham 11/01/91
* Purpose: Displays exploding box on the screen
* Returns: Previous screen contents
*/
FUNC CreateBox (nTop, nLeft, nBottom, nRight, box_color)
local save_window:=savescreen(nTop, nLeft, nBottom, nRight)
local save_color:=setcolor(box_color)
local save_cursor:=setcursor()
local save_row:=row()
local save_column:=col()
@ nTop,nLeft,nBottom,nRight BOX 'ÚÄ¿³ÙÄÀ³ '
RETURN {nTop, nLeft, nBottom, nRight, save_window,;
 save_color, save_cursor, save_row, save_column}
/***
* Written By: Cynthia Allingham 11/01/91
* Purpose: destroys the box and restores old settings
*/
FUNC DestroyBox (aList)
restscreen(aList[1],aList[2],aList[3],aList[4],aList[5])
setcolor(aList[6])
setcursor(aList[7])
setpos(aList[8],aList[9])
aList:=nil
return nil
/***
* Written By: Cynthia Allingham 11/01/91
* Purpose: Changes the box border
*/
FUNC BoldBox (aList, cColor)
@ aList[1],aList[2],aList[3],aList[4];
 BOX 'ÛßÛÛÛÜÛÛ' color cColor
return nil
/***
* Written By: Cynthia Allingham 11/01/91
* Purpose: Redimensions the screen
*/
FUNC MoveBox (aList, nVert, nHorz)
local save_window:=savescreen(aList[1],aList[2],aList[3],aList[4])
dispbegin()
restscreen(aList[1],aList[2],aList[3],aList[4],aList[5])
aList[3] += nVert; aList[1]+=nVert
aList[4] += nHorz; aList[2]+=nHorz
aList[5]:=savescreen(aList[1],aList[2],aList[3],aList[4])
restscreen(aList[1],aList[2],aList[3],aList[4],save_window)
dispend()
return nil
* EOF BOX.PRG
/***
*
* DEMO1.PRG
*
* Written By: Cynthia Allingham 11/01/91
* Purpose: Simple program demonstrating the creation and
* destruction of a box object.
*/
local aBox
set procedure to box
@ 00,00,24,79 box 'ÚÄ¿³ÙÄÀ³°'
aBox:=CreateBox(10, 20, 16, 59, "w+/n, w+/r")
@22,19 say padc("Press any key to destroy the box",40)
inkey(10)
DestroyBox(aBox)
* EOF DEMO1.PRG
/***
*
* DEMO2.PRG
*
* Written By: Cynthia Allingham 11/01/91
* Purpose: Demonstates the use of two box objects
*/
local aBox1, aBox2
set procedure to box
@ 00,00,24,79 box 'ÚÄ¿³ÙÄÀ³°'
aBox1:=CreateBox(05, 26, 20, 53, "w+/n, w+/n")
message("Press any key to create a second box")
aBox2:=CreateBox(10, 20, 16, 59, "w+/b, w+/b")
message("Press any key to move the second box")
MoveBox(aBox2, -08, -18)
message ("Press any key to bold the second box")
BoldBox(aBox2, "w+/b, w+/b")
message("Press any key to destroy both boxes")
DestroyBox(aBox2)
DestroyBox(aBox1)
func message(ctext)
@22,19 say padc(ctext,40)
inkey(10)
* EOF DEMO2.PRG
/***
*
* DEMO3.PRG
*
* Written By: Cynthia Allingham 11/01/91
* Purpose: Demonstates the use of four box objects
*/
local aBox[4]
local cnt
set procedure to box
@ 00,00,24,79 box 'ÚÄ¿³ÙÄÀ³°'
aBox[1]:=CreateBox(05, 05, 09, 30, "w+/n, w+/n")
@ 06, 07 say "box #1"
aBox[2]:=CreateBox(18, 03, 22, 14, "w+/b, w+/b")
@ 20, 05 say "box #2"
aBox[3]:=CreateBox(20, 48, 22, 77, "w+/r, w+/r")
@ 21, 50 say "box #3"
aBox[4]:=CreateBox(02, 64, 12, 75, "w+/gr, w+/gr")
@ 03, 66 say "box #4"
for cnt:=1 to 12
 inkey(0.5)
 MoveBox(aBox[1], +1, 0)
 MoveBox(aBox[2], 0,+4)
 MoveBox(aBox[3], -1, 0)
 MoveBox(aBox[4], 0,-4)
next
inkey(10)
for cnt:=1 to 4
 DestroyBox(aBox[cnt])
next
* EOF DEMO3.PRG

General Terms

ASCII :

An acronym for the American Standard Code for Information Interchange, the agreed upon standard for representing characters (alphabetic, symbolic, etc.) in the memory of the computer.

Binary :

The numerical base upon which computer programs are modeled. Also referred to as base 2, it is a mathematical way of representing the electrical circuitry upon which computers operate. A 0 represents an off state, and a 1 indicates an on state. A combination of eight ons and offs are used to express bytes of program or data in memory.

See Also: Byte

Buffer :

A temporary data storage location in memory. As an example, a disk input-output buffer is an area of memory that stores data read from the disk to temporary locations while processing it.

Byte :

Eight bits of data, the smallest unit of information stored in the computer’s memory. As an example, one byte is required to represent one ASCII character.

Character :

A letter, digit, punctuation mark, or special symbol stored and processed by the computer.

See Also: ASCII String

Delimiter :

A character or other symbol that marks a boundary.

Destination :

The work area, file, or device to which data is sent.

Device :

Either an actual physical component of the computer system such as printer or a DOS handle that refers to it (e.g., PRN:), or a logical device that behaves and is addressed the same way as a physical device (e.g., a print spooler).

Disk :

A magnetic storage medium designed for long-term storage. Disks come in two varieties: hard disks (fast but fixed) or floppy disks (slow but removable). A disk can be partitioned into multiple volumes, each containing a tree-structured directory system that holds files accessible by programs.

See Also: Directory, File, Volume

Dynamic :

Used generically to refer to data or algorithms that change with time. Often used specifically to describe algorithms that automatically adjust to prevailing conditions.

Formfeed :

A special character (CHR(12)) that by convention causes most printers to move the printhead to the top of the next page.

See Also: Hard Carriage Return Linefeed

Hard Carriage Return :

An explicit carriage return character at the end of a line in a text file, as opposed to a soft carriage return that might be inserted into text by a program designed to handle word wrapping. A hard carriage return character is generated by the expression (CHR(13)) where a soft carriage return character is generated with the expression (CHR(141)).

Hexadecimal :

A representation of a value in base 16 rather than decimal which is base 10. Hexadecimal values are easily converted to and from binary (base 2) which is the form of data the computer actually uses. Hexadecimal values are represented by digits zero through nine and A through F for values between 10 and 15.

Extended Character Set :

The character set built into the ROM of the PC. This character set is a superset of ASCII, containing additional special characters (such as a line drawing character set) that may be used to enhance your program screens.

Linefeed :

A special character (CHR(10)) that by convention causes the cursor or printhead to move to the next line or to terminate a line in a text file. It is usually used in combination with a hard carriage return.

See Also: Formfeed, Hard Carriage Return

Port :

A designation for the hardware that allows the processor to communicate with peripheral devices.

Query :

A query is also a general term used when you want to interrogate a setting or an exported instance variable for its current value.

Queue :

A data structure of variable length where elements are added to one end and retrieved from the other. A queue is often described as first in, first out.

See Also: Print Spooler, Stack

Soft Carriage Return :

A carriage return that is introduced into text usually in order to implement some sort of wrap operation, as opposed to a Hard Carriage Return that was specifically entered into the text when it was created.

Stack :

A data structure of variable length whose elements are added and retrieved from the same end. A stack is often described as first in, last out.

See Also: Queue