Object Orientation In FoxPro

Editorial Note:

This article was written by Gregory L. Reichert and uploaded to the CompuServe Fox Forum on the 19th January 1995. The text remains unaltered although I have taken the liberty of tidying up the code samples. The casual Web surfer should be warned, there's some pretty complex stuff here.

Malc.


Introduction:

Object Orientation Programming in FoxPro? Yes. The OOPs technique is not an alternate form of programming, but am extension of Structure Programming. Much the same way that structure programming inherits commands and features of unstructure languages like BASIC and Assembler, OOPs should inherit structure programming languages features. The general notion is to enhance the programming style, and still maintain backward compatibility. In the case of implementing OOPs into FoxPro, this should be a rule. The technique I about to describe is simple and requires no external modules. The implementation uses straight forward fox features, and allows greater flexibility then found in many of the other object orientated languages.


Background:

Recently there has been alot of talk about Object Orientation, but until Nantucket announced Clipper 5.0, there has been no evidence of it finding its way to the xBase dialects.

But before we jump into how to construct and using object, let's determine what a object is. The world is full of objects. Everywhere we look, we find objects. These objects are described by their characteristics and behaviors. The same definition applies to software objects. For example, take a book of matches, it can be described to a computer as something of X width, Y height, containing Z items, and etc. This does little to help define what the matches really are, it simple describes the characteristics, but does nothing to describe its usage or behavior. A set of separate routines need to be designed to implement the matches usage. Currently, FoxPro offers little means of binding the routines with the associated data, outside of the routines dependency on the data.


Encapsulation:

This is where objects come into play. They offer a method of Encapsulating, or binding the routines with the associated data. Within a software object are defined the characteristics as a set of data variables. The matches behaviors are defined as Methods, or variables containing the names of routines that manage the data. Like actors on a stage, we simply request from the object to perform one of its services, and the object acts out the predefined operation using the associated data. In respect to the Matches concept, we could have defined some methods like: REMOVE, LIGHT, BURN, PUTOUT to describe to basic activates. These methods would manipulate the data variable REMAIN, the remaining number of matches.

After some common-sense investigating, we discover that the feature of Encapsulation has been a concern of programmers all along. In most applications, the code and data are viewed as related entities; where the code goes, the data follows, and vise-versa. In this case, the needed binding agent is missing. Leaving the responsibility of keeping them together up to the programmer's memory, not the computer's.

Objects definitions resolve this dilemma. As mentioned, they offer a common, but unique, binding agent to maintain the relationship between the code and the data it effects. Objects and their binding agent can be thought of a containers that group data with its related code. Normally, we refer to these containers as Classes. A Class is a logical definition, while an Object is physical, a working representation of the Class. In principle, there is one, and only one, Class definition, but many Objects can be generated from that Class. From this point on, the term Class will refer to the structure of the object, and Object to the run-time representation the class.


Self:

The attributes of a class are bound together by the common agent, called Self. It serves two purposes, first being the binding agent, a unique entity shared by all the attributes of any single class. Second, it acts as the objects identifier, or the Instance of a Class. We address an object's attributes with use of Self. Every class attribute is declared, accessed, and released with respect to the self reference.


Inheritance:

At this point, we have devised a method that enhances reusability, or the ability to port various portions of one application to another. Object orientation doesn't stop there, but includes many other enhancements. Object classes can expanded to incorporate new features without modifying the existing code or data. This concept is called Inheritance. For instance, take our Matches example, matches serve their purpose well, but only to a limited degree. Only twenty attempt can be offered, and a strict environment control need to be observed. We can improve on our situation by purchasing a disposable lighter. The lighter perform all the function of the matches, plus insures greater number of lights, and better durability. In a sense, the lighter inherits all that attributes of the matches, improving on it's weak areas, while maintaining it's strong points and purpose. This is important in designing software object classes.

The matches still exist, but now we have a lighter. In computers, this translates to an easier and natural method of Version Control. As the application evolves through it's versions, the previous versions remain intact deep inside the current. By instigating the object that defines a previous version, the application performs that version as if the enhancements where never included.


Polymorphism:

The previous version of a class is the parent to the new child class. In fact, a parent class can spawn many variations of child classes. This multiple offspringing is refered to as Polymorphism, meaning Many-Shapes.

Polymorphism can be observed in the Matches/Lighter example, if we first suggest that disposable lighter is a child to a class of flint/steal igniters. Flint/steel class offers the basic concept of any lighter, without defining any one type of lighter. Not only can the disposable lighter be defined from this base class, but we could also derive a ZIPPO-type of lighter and even electical igniter lighters.

In the computer aspect, this can be illustrated by means of the output operations. We start by defining a class that provides a basic text-style of output to the screen. Through an object generated from this class we are able to display information. But display is not the only function in placing the data on screen, the color of the displayed data needs to be considered. A child class is defined to include this new features. Now if the monitor is detected to be monochrome, the color aspect is not need, so the application generate the parent class. But if a the monitor supports color, then the child class needs to be generated.

We can take this example even further, by defining further child classes that describe different types of video displays, and whether it's being display on a text or graphic screen, or even a class for printer and file redirection. To the application, it's just a output device, it is up to the object to determine how to get the information to the device.


On With The Show:

Inheritance and Polymorphism are probably the two greatest assets that OOPS can offer to application development. With a natural version control aspect, and the ability to expand the applications functions and the environments domain, Object Orientation is defiantly a evolutionary advancement to software engineering.

So far, I have not demonstrated any FoxPro code illustrating OOP. I have attempted to illustrate that Object Orientation is not a solution to many problems plagueing the language, but is simply an alternate way of view and defining the problem. Now I will start explain one of possibly many ways that the accepts of OOPS can be implemented in to FoxPro xBase dialect. In a progressive manner, I will redescribe each of the previously aspects in the way that they can be applied to xBase. Unforunately, the method described here requires more process time in order to perform certain operations, not to mention a complete facelift in the way the programmer view the application they are developing. Also a greater number of public memory variables are declared, plus the string space required by the assignments of method variables. The preplanning and predefining of the boundaries and limitations of the application become a rule. The modulization of object orientation will aid in the designing of the project. Also, unlike many other OOPS languages, this methodology does not dictate that programmer use only objects. This method is defined as a language enhancer. Most of the application should remain as structure code, with the object calls weaved in, to perform more generized tasks. Tasks like keyboard and mouse events, screen and window control (which may include predefined data-entry screens), database manipulation, and printers. From these examples, it is obvious that in the term of FoxPro, it is best to use objects only when addressing I/O related operations. Processing part of the application should continue to the designed and wrote in the current structured fashion. The only possibly exception to this rule would be if some blocks of processing code could be generalized into a class; ie. Arrays, or mathimatics.


At The Base:

The first thing we need to do is define a base from which to biuld future classes. For ease, we will call it the Base class. The Base class is the simplist class in the system. All other class are children of the Base class, so that Base provides a common platform of all the classes. The Base class performs no services, except in the creation and destruction of objects. It provides the basic attributes for controlling objects within application's environment.

To define a class in FoxPro, we need a method of parsing a templete of the class into a functioning object at run-time. The simplist way is the use User-defined functions. They offer a means of naming and dynamicly creating objects. The parameter declaration is used to pass any default initializing values to the object being created.

Along with these parameters, a special parameter is declared: the Self reference. The Self reference parameter aids in defining the new object uniquely from any other objects defined. Giving each object a sence of individuality. Normally, when a class procedure is called, Self is passed as an empty character string.

The class attribute are declared as public variable in a special way. These attributes come in two types: data and methods. The syntex to declaration and usage is the important part. The Self reference is concatentated to the front of the attribute, as indicated in example 1.

Example 1.a. The Base Class Procedure:

	procedure Base					&& Base Class
	parameter Self

	Self = construct(Self)				&& Generate Self

	if .not. empty(Self)				&& Test for error
		store program() to (Self)		&& assign Class name

		public (Self + 'DONE')			&& Declare Method

		store [DONEBASE] to (Self + 'DONE') 	&& Assign Method
	endif

	return(Self)
Example 1.b. Sample Creation Of An Instance Of Base:
	private Base					&& Instance

	Base = Base('')					&& Object Creation 
	do (evaluate(Base + 'DONE')) with Base		&& Object Destruction
Example 1.a illustrates the complete Base Class procedure, along with a sample construction and destruction operation of a Base Object. Let's take moment and diesect the example to better understand how objects are created. Following the Class Procedure statement is the parameter statement. All class procedure have a parameter statement with at least one parameter, Self. When the class procedure is called, a user suggested self reference, or a null string is passed to Self. The first statement of the procedure is a call to the class's parent constructor, in which returns a self reference value. In the case of the Base example, Base does not have a parent class so a call to Construct is made. The Construct Function's single purpose is to generate a Self Reference, if a valid one wasn't passed to it. The Construct function will be discussed in greater detail shortly, but for the purpose of the explaination, we'll assume a value of 'O00' was recieved by Self. The IF statement at the second line is a spot error check. If Construct was unable to generate a Self Reference, a null string is returned.

The third statement assign, to the variable repersented in Self, the class name. Because the procedure name is the class name we need only assign it the value of Program(). Notice that through Indirect Reference, O00 is assigned the name of the class, not the variable Self. Each decendent class should update this variable with name of the current class.

The forth statement is the public declaration of the method DONE. As explained earlier, Self is tacked on to the front of the method's name to compose a unique variable name, which results in 'O00DONE'. The fifth statement is the assignment of the method procedure name to the method attribute. The final statement of the class is the return of the Self reference used during the construction of the object. Class procedures are declared as a PROCEDURE, but also returns a value like a function. This allows the UDF to be accessed safely as a both a procedure and a function.

These lines of Example 1. generalize the basic requirements for composing objects. Every child object should be comprised of these features (some exception are allowed).

The Example 1.b uses the Base Class procedure to construct an Base object. The Private statement declares a variable to be used as an Instance of the newly created object. The following statement is the call to the Base class procedure (called as a function), returning the Instance. The Instance then used there-after to access the various attributes of the object. The last statement demonstrates this, by using the instance to call the object's DONE method, destroying the object. The method call syntex is comprised of the instance, and the name of the method, combined inside a Indirect Reference parentheses. A copy of the Instance is passed as the object's Self.

Example 2. Base's DONE Method Procedure:

	procedure BaseDone				&& Method Name
	parameter Self					&& Object Instance

	release (Self + 'DONE')				&& Release method
	do Destruct with Self				&& Release Self Reference

	return(Self)
Within the BaseDone Method (Example 2.) we find that it releases the varaibles used to hold the object's attribute. Followed by a call to the Destruct Procedure. The Destruct Procedure release the Instance back into the pool of available Self References. Normally, this call is to the most immediate parent's DONE method, but Base is defined as the root of all Classes and has no parent. The return of Self is erroneous, for any result of the call is discarded. The Return statement is only included to maintain compatibility with all the other methods in the system.


Construct And Destruct Procedures:

The Construct and Destruct procedure define the true base to implimenting OOPs into FoxPro. They provide a automatic way to generate and destroy the self refereneces used to manipulate objects.

Example 3. The Construct and Destruct Procedures:

	procedure Construct
	parameter Self
	private x_

	x_ = 0

	do while (x_ <100 ) .and. (tpye(Self)="C" ) x_="x_" + 1 Self="ObjRef(" x_ ) enddo if (x_="100)" wait "Error Out of Object References" window nowait return('') endif public (Self) store program() to (Self) return(Self) function ObjRef
	parmaerer x_

	return("O"+ padl(ltrim(str(x_ ) ), 2, "0") )


	procedure Destruct
	parameter Self

	if .not. empty(Self)				&& If reference exist
		release (Self)				&& release reference
	endif

	return(Self)
The Construct procedure searchs the pool of objects, and returns the first available Self Reference found. The name of a prospective reference is generated by the function ObjRef. A return of a null string from the construct procedure indicates an error has occur in the attempt. The example limits the number of auto-generated instances to 100, but the only real limitation is the size of the string segment, and the number variables that can be generated by the system.

Next, the found Self reference is a declared publicly, and is assigned the name of the constructor as default. This declaration signifies to the future Construct calls that this reference is in uses by an object. Finally, the choosen reference is returned to be used as the instance to the object.


Children:

Now that some of the basic have been laid out, let's proceed to construct some simple classes. For purpose of explination, I will pass over the preplaning stage of OOP developement for now, and proceed as if it has already been considered.

Example 4. Class Heritage:

	Base
	 |-->	Location
		 |-->	At
		 |-->	Box
			 |-->	Window
Example 5.a. Class Definitions:
	procedure Location
	parameter Self, R, C

	Self = Base(Self)
	store program() to (Self)

	public (Self + [ROW]), (Self + [COL])
	public (Self + [DONE]), (Self + [Show]), (Self + [Move])

	store [Loc_Done] to (Self + [Done])
	store iif(type('R') = "N", R, 0) to (Self + [Row])
	store iif(type('C') = "N", C, 0) to (Self + [Col])
	store [Loc_Show] to (Self + [Show])
	store [Loc_Move] to (Self + [Move])

	return(Self)
Example 5.b. Location Method Declarations:
	procedure At
	parameter Self, R, C, Content

	Self = Location(Self, R, C)
	store program() to (self)

	public (Self.[Content])
	public (Self.[Done]), (Self + [Show])

	store [At_done] to (Self + [Done])
	store Content to (Self + [Content])
	store [At_show] to (Self + [Show])

	return(self)


	procedure Box
	parameter Self, T, L, B, R, Style

	Self = Location(Self, T, L)
	store program() to (self)

	public (Self + [Bottom]), (Self + [Right]), (Self + [Style])
	public (Self + [Done]), (Self + [Show]), (Self + [Move])

	store [Box_Done]  to (Self + [Done])
	store iif(type("B") # "N", evaluate(Self + [ROW]) + 1, B) to (Self + [Bottom])
	store iif(type("R") # "N", evaluate(Self + [COL]) + 1, R) to (Self + [Right])
	store iif(type("Style") # "C", "", iif( inlist(Style, "DOUBLE", "PANEL"), Style, "")) to (Self + [Style])
	store [Box_Show]  to (self + [Show])
	store [Box_Move]  to (self + [Move])

	return(self)


	procedure Window
	parameter Self, T, L, B, R, Style

	Self = Box(Self, T, L, B, R)
	store program() to (Self)
          
	public (Self + [Name])
	public (Self + [Done]), (Self + [Show]), (Self + [Hide]), (Self + [Move]), (Self + [Size])
          
	store [Win_Done]  to (Self + [Done])
	store Self to (Self + [Name])
	store [Win_show] to (Self + [Show])
	store [Win_hide] to (Self + [Hide])
	store [Win_move] to (Self + [Move])
	store [Win_Size] to (Self + [Size])
	store iif(type("Style") = "C", Style, "") to (Self + [Style]), Style

	define window (self) from T, L to B, R &Style


	return(Self)

Default Parameter Arguments:

The idea of default parameter augments is not unique to the OOPs. It can be used in standard structured programming as well. The concept is testing the various parameters passed to the function to insure that they are of the purpore type and value. In the case of Default Parameter values, if no value was passed for a specific parameter, a valid value is then substituted.

Example 6. Default Parameter Auguments:

	procedure Box
	parameter Self, Top, Left, Bottom, Right, Style

	top = iif(type("Top") # "N", 0, Top)
	left = iif(type("Left") # "N", 0, Left)
	bottom = iif(type("Bottom") # "N", Top + 1, Bottom)
	right = iif(type("Right") # "N", left + 1, Right)
	style = iif(type("Style") # "C", "",;
		iif(InList(upper(style), "DOUBLE", "PANEL", ""), style, ""))

Overloading:

The concept of Overloading function attributes is impossible in the FoxPro language, because the function's parameter are declared as untyped. Overloading occurs when functions of the same name are declared within the scope of a single class, but contain a parameter of different types. The only complimentary way to approuch this concept is to test for the various types with in the body of the function itself. The ability of declaring multiple functions of the same name is omitted, and the different parameter types would be noted in the comments provided with the attribute declaration.

Example 7. Overloading By Imbedded Type Testing:

	procedure Func
	parameter Aelf, A
	private _pcnt, _type

	_pcnt = parameters()
	_type = type("A")

	do case
If none passed, declare as character.
		case  _pcnt=1
			A = ""

		case _type="C"

		case _type="N"

		case .....
	endcase

	.....

	return(A)
An alternate method of Overloading can be achieved by way of Inheritance. Each child Method is declared in a Virtual manner, and handles a single type. If the child Method determines that the parameter is not the type required for the Method then the previous parent declaration of the Method is called, with use of the Obj.DO method.

This method of Overloading requires more overhead of code and processing time, and should be used sparingly.


Example Object Program:

Rethinkng Object Construction

Added functions
Object System variables (ObjSys and ObjEnd)
Obj: Instance of the Object Class
ObjDepth: Maximum number of possible instance
ObjKey: 'O'
UnitName: Temporary used by Preform (DO)
Construct and Destruct: revised
ObjectO: Object class attribute definition
ObjDone: Object class destructor
Declare: Defines class attributes
Free: Release class attributes
Get and Put: Retrieve and store values to attributes
Perform: Access parent methods
ObjLoad and ObjSave: Manipulating Persistent Objects

Generating the Object-Orientation System Virtual Methods
VML attribute
When to declare Virtual and when to declare Static

Friendship
Multiple Inheritance
Persistant Objects to files or Memo fields

Idea to controlling object creation

Freeing unneed attributes during creation
The Source Code:

	procedure ObjSys
	parameter Self, Depth_, Key_

	set udfparms to reference
   
	public Obj, ObjDepth, ObjKey, UnitName

	ObjDepth = iif( type("Depth_") = "N", Min(Max(Depth_, 1), 3),  2 )
	ObjKey   = iif( type("Key_") = "C", left(KEY_, 1), "O")
	UnitName = iif(type("UnitName") # "C", sys(16), UnitName)
	Obj = ObjectO(iif(type("Self") # "C", "", Self))

	return(Obj)


	procedure ObjEnd
	do (evaluate(Obj + [Done])) with (obj)

	release Obj, ObjDepth, ObjKey, UnitName

	return(0)
Initialize new Object Reference
	procedure Construct
	parameter Self
If no Self is being implied search for first unused reference
	if empty(Self)
		private x_

		for x_ = 0 to (10^ObjDepth) - 1
			if type((ObjKey + padl(ltrim(str(x_)), ObjDepth, '0'))) = "U"
				exit
			endif
		endfor
Create reference
		Self = ObjKey + padl(ltrim(str(x_)), ObjDepth, '0')
Check for overflow
		if type(Self) # "U"
			? "Error - Out of Object Handles"
			cancel
		endif
	   endif
Initialize Object Reference
	public (Self)

	return(Self)
Destroy existing Object Reference
	procedure destruct
	parameter Self

	if (! empty(Self)) .and. (type(Self) # "U")
		release (Self)

		Self = [*]
	endif

	return(Self)


	procedure ObjectO
	parameter Self

	= Construct(Self)

	store program() to (Self)

	do declare with Self, [Define],[Declare], program()
	do declare with Self, [Constr],[Construct], program()
	do declare with Self, [Destr], [Destruct], program()
	do declare with Self, [Free], [Free], program()
	do declare with Self, [Get], [Get], program()
	do declare with Self, [Put], [Put], program()
	do declare with Self, [Do], [Perform], program()
	do declare with Self, [Save], [ObjSave], program()
	do declare with Self, [Load], [ObjLoad], program()
	do declare with Self, [Done], [ObjDone], program()

	return(Self)


	procedure declare
	parameter Self, Var, Value, Class, Dir, Proc

	private pcnt, tvalue, Sx
	pcnt = parameters()

	if empty(var)
		return(.f.)
	else
		var = upper(alltrim(var))
	endif

	tvalue= iif(type("Value") # "C", [*], Value)

	class = iif(pcnt >= 4, upper(alltrim(class)), [])

	if (pcnt >= 5)
		dir = upper(alltrim(dir))
		dir = iif(' '$dir, substr(dir, rat(' ', dir) + 1), dir)
		dir = iif('\'$dir, left(dir, rat('\', dir)), dir)
	else
		dir   = []
	endif

	if (pcnt >= 6)
		proc  = upper(alltrim(proc))
		proc  = iif(' '$proc, substr(proc, rat(' ' ,proc) + 1), dir)
	else
		proc  = []
	endif

	if empty(proc) .and. (!empty(dir)) .and. (!('\'$value))
		value = dir + value
	endif

	public (Self + Var)
	Var = iif("("$Var, left(Var, at('(', Var) - 1), Var)
	Var = iif("["$Var, left(Var, at('[', Var) - 1), Var)
	store Value to (Self + Var)
Declare VML if need to.
	if type(Self + "VML") = "U"
		Sx = iif(type("OBJ") # "C", Self, Obj)
		do (evaluate(Sx + "define")) with Self, [VML], []
	endif

	store  proc + chr(251) + dir + chr(252) + cass + chr(253) + var + chr(254) + tvalue + chr(13) + chr(10)  + evaluate(Self + "VML") to (Self + "VML")

	return(.t.)


	procedure Free
	parameter Self, Var, Class

	private Pnt, X Temp, VML
	Pcnt = parameters()

	if empty(Var) .or. empty(self)
		return(.f.)
	endif

	X = atcline(iif(pcnt = 3, chr(252) + alltrim(class), '') + chr(253) + alltrim(var) + chr(254), VML)

	if X > 0
		temp = mline(evaluate(Self+ "VML"), X)
		store stuff(evaluate(Self + "VML"), at(temp, evaluate(Self + "VML")), len(temp) + 2,[]) to (Self + "VML") 
       
		X = atcline(chr(253) + alltrim(var) + chr(254), evaluate(Self + "VML"))

		if X > 0
			temp = mline(evaluate(Self + "VML"), X)
			store substr(temp, at(chr(254), temp) + 1) to (Self + Var)
		else
			if ((chr(251) + chr(252) + chr(253) + [VML] + chr(254) + chr(13) + chr(10)) = evaluate(Self + "VML"))
				do (evaluate(Obj + "Free")) with Self, [VML]
			endif

			release (Self+var)
		endif

		return(.t.)
	endif

	return(.f.)


	procedure Get
	parameter Self, Var, Value, Class

	if type("Class") = "C"
		private X

		X = mline(evaluate(Self + "VML"), atcline(chr(252) + alltrim(Class) + chr(253) + alltrim(Var) + chr(254), evaluate(Self + "VML")))
		Value = substr(X, at(chr(254), X) + 1)
	else
		Value = evaluate(self + alltrim(Var))
	endif

	return(Value)


	procedure Put
	parameter Self, Var, Value, Class

	if type("Class") = "C"
		private Token, X

		Token = chr(252) + alltrim(Class) + chr(253) + alltrim(Var) + chr(254)
		X = atc(Token, VML)

		if X > 0
			store stuff(VML, X + len(Token), len(mline(VML, atline(Token, VML))) - len(Token), alltrim(upper(Value))) to (Self + "VML")

			return(.T.)
		endif
	else
		store Value to (Self+Var)

		return(.T.)
	endif

	return(.F.)


	procedure Perform
	parameter Self, Var, Class, P0, P1, P2, P3, P4, P5, P6

	private Pcnt, Tpara, X, Temp, Tproc, Tdir, Tvar, _oldproc
	pcnt = PARAMETERS()

	if empty(self) .or. empty(var)
		return(.f.)
	endif

	Tpara = substr(", P0, P1, P2, P3, P4, P5, P6", 1, (pcnt - 3) * 3)
	Var   = upper(alltrim(var))
 	Class = iif(type("Class") = "C", Class, [])

	Temp = evaluate(Self + "VML")
	X = atcline(iif((Pcnt > 2) .and. (! empty(Class)), chr(252) + alltrinm(Class), "") + chr(253) + var + chr(254), Temp)
	Temp  = mline(Temp, X)
	Tproc = left(Temp, at(chr(251), Temp) - 1)
	Tdir  = substr(Temp, at(chr(251), Temp) + 1)
	Tdir  = left(Tdir, at(chr(252), Tdir) - 1)

	Tvar = iif( ! empty(Class), substr(Temp, at(chr(254), Temp) + 1), (Self + Var))

	if empty(Tvar)
		return(.f.)
	endif

	if ('\'$tvar) .or. (':'$tvar)
		do (Tvar) with Self &Tpara


		return(.t.)
	endif

	if ! empty(Tdir)
		if empty(Ttproc)
			Tvar = Tdir + Tvar
			do (Tvar) with self &tpara


			return(.t.)
		endif

		if .not. (('\'$tproc) .or. (':'$tproc))
			Tproc = Tdir + Tproc
		endif
	else
		if empty(Tproc)
			do (Tvar) with Self &Tpara


			return(.t.)
		endif	
	endif

	if .not. (empty(UnitName) .or. empty(tProc))
		_OldProc = UnitName
		UnitName = Tproc

		if (_OldProc # Tproc) 
			do (Tvar) in (UnitName) with Self &Tpara

		else
			do (Tvar) with Self &Tpara

		endif

		UnitName = _OldProc

		return(.t.)
	endif

	return(.f.)
   

	procedure ObjLoad
	parameter Self, From_, Asmemo
	private l0para, l0memo

	l0para = parameters()
	l0meno = iif(Asmemo .and. (! empty(alias())) .and. (type(To_) = "M"), [memo], [])
	restore from &l0memo &From_ additive
	do Publics with Self, evaluate(Self + "VML")
	restore from &l0memo &From_ additive

	return(Self)


	procedure Publics
	parameter Self, p0VML
	private X, Temp, A

	release (Self)
	public (Self)

	for X = 1 to memline(p0VML)
		Temp = mline(p0VML, X)
		a = at(chr(253), temp) + 1
		temp = substr(temp, a, (at(chr(254), temp) - a))
		release (Self + Temp)
		public (Self + Temp)
	endfor

	return


	procedure ObjSave
	parameter Self, To_, Asmemo
	private l0Self, l0memo

	l0meno = iif(asmemo .and. (! empty(alias())) .and. (type(To_) = "M"), [memo], [])

	save to &l0memo &to_ all like &Self.*

	return(Self)


	procedure ObjDone
	parameter Self

	do Free with Self, [Constr]
	do Free with Self, [Destr]
	do Free with Self, [Define]
	do Free with Self, [Get]
	do Free with Self, [Put]
	do Free with Self, [Do]
	do Free with Self, [Save]
	do Free with Self, [Load]
	do Free with Self, [Done]
	do Free with Self, [Free]

	= Destruct(Self)
	
	return(Self)

Bibliography:

OOPs Foxpro - Gregory L. Reichert
FOXtalk Oct '90 - Pinnicle Publishing.
TURBO PASCAL 5.5 : OOP Guide - Borland