(c) Copyright 1996-2003 Foxtalk / Viafox, All rights reserved

Het artikel 'Create Enduring Variables in FoxPro' is gepubliceerd in FoxTalk, februari 1996. De onderstaande, originele, tekst wijkt iets af van het gepubliceerde artikel vanwege kleine aanpassingen in het Engels door een native speaker.

Create Enduring Variables in FoxPro

Copyright Viafox

Here's a creative way to store variables that persist throughout an entire Foxpro or Visual Foxpro session, yet are immune to destruction by commands like CLEAR MEMORY. "Enduring Variables" are great for desktop accessories, add-ons to the development environment, and as a standard interface between 'plug and play' application components.

FoxPro has a wide range of options to store and retrieve information. Various types of variables (PRIVATE, PUBLIC, REGIONAL), memory files, databases, virtual tables and even ordinary text files are at our disposal.

However, if we want to develop a variety of applications that should share information in some regard, each method of information storage/retrieval has a disadvantage. It may be that some applications will CLEAR MEMORY in their cleanup code, thus ruling out the use of memory variables. The location on disk of databases, memory files and ordinary text files may be unknown to some applications. Virtual tables (cursors) may have been closed with CLOSE DATA, e.g. in the cleanup code of an application.

This problem is solvable if all of these applications are developed by the same software house. In-house protocols could instruct the in-house developers to always maintain certain variables or databases. But a protocol won't help if a customer wants to combine applications/tools of various software houses, e.g. in a menu system.

We need variables that will endure during the whole session that FoxPro is active, similar to the System Variables of FoxPro. I developed the function varX() that should meet our need for such 'enduring' variables.

The core of the function is its use of an out-of-use FoxPro System Variable. In FoxPro for DOS the variable _SPELLCHK is used, else _FOXGRAPH. All enduring variables are stored in this System Variable. Thus, the enduring variables are insensitive to commands that clear memory variables or close databases. The varX() function is the interface to this buffer.

It is advised to place a copy of VARX.APP in the FoxPro directory. Thus, it won't be a hassle to tell an application where it can find varX(). Simply add sys(2004) to the FoxPro searchpath prior to the first call. The procedure ADDPATH.PRG (see below) offers a secure way to add sys(2004) to the path.

Some may prefer to possess the original VARX.PRG, in order to be able to customize things or include the code in each project. However, with regard to varX(), Version Control is very important. Compatibility with older versions and copies used by other parties should have the highest priority. That's why the FoxPro community should use an excluded VARX.APP. It is the intention of the author to release updates at a regular base on CompuServe's FoxForum.

It has been noted that varX() uses a System Variable (_SPELLCHK or _FOXGRAPH). Please, don't override these variables ...

Reading Built-in Enduring Variables

The first and second parameter that varX() expects make up the name of an enduring variable. The function will return the value of this variable. Its datatype will be numerical, character, date or logical, dependent on the datatype of the enduring variable.

 The varX() function comes with three built-in enduring variables:

C = varX( 'SYMBOLS_VARX', '' )
C = varX( 'INFO_VARX' )

The 'SYMBOLS_VARX' call returns the Symbols Table that varX() uses. The first 25 columns of the output are reserved for future enhancements. Each symbol occupies an additional 26 columns. Of these 26 columns, columns 1-2 are used for a separator, the next 20 columns for a unique symbol ID and the last 4 columns define the datatype. Asciicodes 13+10 are used as separators. This enables you to distract symbol information with the FoxPro MLINE() function. Note that the second parameter is empty. In that particular case it may be left out, as has been done on the other two calls.

The 'INFO_VARX' call returns some information about varX(). Again, asciicodes 13+10 are used as separators, enabling you to use the FoxPro MLINE() function.

MLINE()   Description
1 Name
2 Version
3 Date of this version
4 Author's ID ( PDV )
5 Copyright Notice
6 Current number of symbols

The 'SERIALID_VARX' call returns a 'Serial ID' based on your Serial Number. You will need this information when declaring your own enduring variables.

How to Create Enduring Variables

It is quite easy to add new enduring variables to the list. Simply call varX() with three or four parameters:

Parameter   Description
1 a new Symbol Name
2 your unique ID
3 a description of the datatype
4 optionally, the initial value

The unique ID should be a character constant that's used by you/your company only, so that your declarations won't ever interfere with the (correct) declarations of other parties. You can obtain a unique ID by calling varX('SERIALID_VARX') in interactive mode.

NOTE!!  You should not ignore or minimize the importance of the unique ID! As a matter of fact, when developing this function, I recognized the necessity to come to terms with the international community of FoxPro Developers. We should take care not to override each others enduring variables. The unique ID is based on the serial number of your copy of FoxPro. It is quite possible to administrate the names of Symbol Names in-house; However, it is totally impossible to do so for the international community.

The reserved length for the Symbol Name plus the unique ID is 20 characters.

The description of the datatype must be one of the following:

Description of Datatype   Explanation
"C"                         Character Expression
"Niij"                     Numerical Value; ii=Length j=Decimals
"D"                         Date
"L"                         Logical
"E"                         Evaluated Character Expression; Maximum of 10 evaluations


Evaluated Character Expression; ii=Maximum number of evaluations

E.g. varX( "tval", "!#0}Sa59V", "N103", 12.345 ) would create a symbol TVAL!#0}Sa59V that's numerical and is initialized to the value 12.345.

There's virtually no limit to the size of character expressions. In one test a 100.000 bytes string was stored as an enduring variable. The routine didn't care at all. Performance (creating/reading/changing) was as fast as one may expect. As a consequence, storing the contents of memo fields should pose no drawbacks.

Dates are stored as a Julian Day Number and returned in the format as set with SET DATE.

The varX() function has another feature. The programmer can create enduring variables that are evaluated when read; Or even they may evoke entire tools and applications! The datatype of such an enduring variable is "E". Stuff between text merge delimiters in the expression will be evaluated. (These delimiters may be changed with the FoxPro command SET TEXTMERGE DELIMITERS). Some examples are given in a separate section.

The datatype parameter is ignored on succeeding calls (except for the datatype 'E'; see the next section). This implies that it is not necessary to initialize a symbol before actual usage. Thus, a call like

#declare varXid "!#0}Sa59V"
if varX( "tval", varXid, "N103" ) > 0

will eventually create the symbol and always return a result.

Reading Your Own Enduring Variables

Reading your own enduring variables is not different from reading built-in variables. Again, the result will be of the declared datatype. E.g.

result = varX( "mystring", varXid, "c" )

The third parameter may be left out if you're certain that the enduring variable was declared in advance.

While, normally, on succeeding calls the datatype parameter is ignored, it is not entirely ignored when the datatype is "E". You may choose to specify "Eii" where ii is a number in the range 0-99. For that particular call, the result will reflect the situation after maximally ii evaluations. E.g. specifying "E0" would return the plain string instead of an evaluated string.

Changing an Enduring Variable

The value associated with a symbol can be changed with the fourth parameter. The result will reflect the new information. E.g.

=varX( "mystring", varXid, "c", "my string" )

The function varX() is meant for advanced users. There's no read-only mode; Even the built-in enduring variable 'SYMBOLS_VARX' may be overridden. Of course, this would corrupt the buffer, so, take care ...

How to use the Datatype "E"

If an enduring variable has datatype "E", stuff between text merge delimiters in the expression will be evaluated. The following (somewhat irrelevant) examples hopefully make this clear:

wait window varX( "getDir", varXid, "E", "Directory <<getdir()>>" )
if varX( "yesno", varXid, "E", "<<_WINDOWS>>" ) = "YES"
number = val( varX( "number", varXid, "E", "<<_TALLY+5>>" ) )
aaa = "<<bbb>>"
bbb = "<<aaa>>"
result = varX( "aaabbb", varXid, "E", "Result is: <<aaa>>" )
wait window varX( "wrong", varXid, "E", "Not possible: <<sys()>>" )

The first example shows how to invoke a function. In this case the FoxPro function GETDIR() is called. In a same vein you could choose to call one of your own applications or tools.

The second example shows how a logical result is handled. Instead of ".T." or ".F.", the merged text will be "YES" or "NO". Embed the FoxPro IIF() function if you want a different text.

The third example should make clear that the output is always of datatype 'Character'. If appropriate, you can use FoxPro functions like VAL() and CTOD() to convert. Also, this example shows that expressions may contain complex calculations. As a matter of fact, varX() will evaluate the expressions with the help of the FoxPro EVAL() function. Thus, eventual restrictions are caused by this function, not by varX().

The fourth example may cause infinite looping. In order to prevent infinite looping, the evaluation routine will stop evaluating expressions after 10 evaluations. This number can be adjusted for the specified enduring variable. E.g., in the upper examples the datatype parameter might have been "E01" or "E1".

The last example is given to show how a wrong call is handled. The result will be:

"Not possible: {{???: sys()}}"

Error Messages

Several error conditions are recognized by varX() and will result in the displayal with WAIT WINDOW NOWAIT of a simple error message. No error routine (either the default FoxPro routine or yours) is triggered and program execution will continue.

One kind of error may go unnoticed in some cases. Data are stored in a variable length format. The ascii codes 17 + 10 are used as separators. If this character sequence is part of a value, the routine will erroneously assume this is the spot where the next value is stored. Consequently, starting at this position the buffer is corrupted. You should take care not to store this sequence of ascii codes.



Add a Path

The following code will conditionally add a path to the FoxPro searchpath.


parameter _px
if atc( ';' + _px + ';', ';' + set( 'path' ) + ';' ) = 0
      set path to ( set( 'path' ) + ';' + _px )
      RETURN .T.

The character type parameter _px is obliged and is supposed to be the path that must be added. The path will not be added if it was already added.

The returned logical value can be used to find out whether or not the path has actually been changed.

To tell your application to also search the FoxPro directory, you might add the following line to the source code:

do addPath with sys( 2004 )