Logo

Hacker’s Guide to Visual FoxPro
An irreverent look at how Visual FoxPro really works. Tells you the inside scoop on every command, function, property, event and method of Visual FoxPro.

Function, Procedure, Return, EndFunc, EndProc

FUNCTION and PROCEDURE indicate that what follows is a subroutine. When used in a DEFINE CLASS command, these two commands indicate the code that follows is a method. RETURN indicates that control should return from a subroutine and may include a value to pass back to the calling program. ENDPROC and ENDFUNC are optional, signaling the end of their respective procedures and functions, but are weaker placeholders than RETURN.

Usage

FUNCTION RoutineName [ ( LocalParameterList) ]
PROCEDURE RoutineName [ ( LocalParameterList ) ]

RoutineName is the name of the subroutine. It follows the usual naming rules for FoxPro (which are laid out in Help).

The syntax is a bit different when used as a part of the DEFINE CLASS command. Please see the DEFINE CLASS topic in the Reference section.

Although the help for both FUNCTION and PROCEDURE fails to mention it, you can specify parameters for a routine by putting them in the header line as shown above. Parameters specified this way are local, which is our preferred scope anyway.

On the other hand, the help does explain clearly where Visual FoxPro looks for a routine when you call one and which one gets used, if there are several with the same name.

It doesn’t matter whether you call a routine a function or a procedure in the header line. They’re identical and, in fact, often you don’t need either one. If a routine lives by itself in a PRG, it’ll run faster without either a FUNCTION or a PROCEDURE declaration. (If you really feel the need to put one there, comment it out.) See “Faster than a Speeding Bullet” in “Franz and Other Lists” for more on this subject.

As for functions and procedures being identical, of course they’re not. But it’s not what you call a routine that determines whether it’s a function or a procedure, it’s how you call it. When you use the DO RoutineName syntax, the routine is called as a procedure. When you use the RoutineName() version, it’s called as a function. The one key difference between the two is that procedures can change the value of passed variables (“by reference”) where functions usually only receive their value (“by value”). We say “usually” because, like most things in FoxPro, you can change this if you want. Read up on the SET UDFPARMS command and “Just Some Routine Inquiries, Ma’am” in “Xbase Xplained” for more details.

We recommend you use FUNCTION or PROCEDURE to indicate how you expect the routine to be called.

Usage

RETURN [ eReturnValue ] [ TO MASTER ] [ TO FunctionName ]
ENDFUNC
ENDPROC

RETURN has two purposes in life. First, it indicates the end of execution of a routine and can be read there as “return control to whoever called this routine.” In this context, RETURN can appear either at the end of a routine or within a routine. You can omit RETURN at the end of a routine if you don’t need to return a value, and either the file ends there or the next line begins another function or procedure declaration, but we think you should put it there anyway to clearly indicate your intent.

RETURN’s second purpose is to hand a value back to the caller. Given the dual nature of subroutines as both functions and procedures, returning a value could be very hairy. In fact, this part of FoxPro is quite well designed. If a routine has a return value and you call it as a procedure, the return value is ignominiously dumped in a bit bucket somewhere along the line, never used. On the other hand, if a routine doesn’t have a return value and you call it as a function anyway, it returns .T. Finally, if a routine doesn’t even have a RETURN statement and is called as a function, it also returns .T.

Visual FoxPro 7 includes a little enhancement that allows arrays to be returned from a procedure or function. There are some constraints on returning an array. First, the array must be in scope after the function ends. You can’t use a variable defined as LOCAL or PRIVATE within the function to hold the array, and since PUBLICs are not good programming practice, the best practice is to store the array to a property of an object that is still in scope at the end of the routine. When you return the array property, be sure to prefix it with “@” as in: RETURN @oMyObject.Array. Because you’re returning an array from a method in an object that is already exposed within that object, it’s not exactly something you’re going to use in your average desktop application. This feature was added to return arrays from COM servers.

The TO clauses are a little more tricky, and we generally avoid them because they can violate good structured programming principles. RETURN TO MASTER blasts through the entire program stack, returning control to the topmost program. Any other code waiting to execute in the intervening routines is ignored. This has the potential of leaving your application in an unstable condition, and we typically use this only in dire situations, such as deep in an error handler.

RETURN TO FunctionName lets you do something almost as dangerous—leapfrog over the calling routines to a particular place in the calling stack. If you design your application exactly right, you may get away with this, but if you’re expecting cleanup code to fire in the routines skipped over to restore the environment, you could be out of luck.

Be aware that RETURN TO expects a literal procedure name—you'll need to use &lcProcName to macro-expand a variable procedure name. For an object method, you need to use only the method name and not the object designation—RETURN TO ReadEvents works, but RETURN TO oApp.ReadEvents bombs. Far worse, RETURN TO anywhere or RETURN TO &lcProcName will perform a RETURN TO MASTER if "anywhere" hasn't been declared or if the lcProcName isn't in the calling stack. We'd expect a syntax error instead.

We generally agree it’s best to have one and only one RETURN in each routine. Definitely, you should put RETURN at the end of the routine, whether you need it or not. It’s also better to do all your calculations and store the return value in a variable, then have a single RETURN RetVal at the end of the routine. This avoids those situations where you add some cleanup code onto the final RETURN, but it doesn’t fire for those other RETURN statements with your code. However, we’ve compromised this principle in the case of “short-circuiting” a routine. In many routines in this book, we test parameters and bail out if those tests fail. Once all the tests are passed, however, we have a single RETURN at the end of the real work. Our second example, the MakeName routine below, shows the compromise. If any parameter is no good, we give up and return the empty string. Once all the tests are passed, though, there’s only one RETURN.

ENDFUNC and ENDPROC are weak cousins of RETURN. They were introduced into the language with the DEFINE CLASS command, but all three can be used interchangeably. ENDFUNC and ENDPROC, however, can’t return values, nor do they support the TO clause.

Example

PROCEDURE CleanUp
* Clean up after a program dies.
SET SYSMENU TO DEFAULT
CLOSE ALL
CLEAR ALL
ON KEY

RETURN

FUNCTION MakeName(cFirst, cMiddle, cLast)
* Combine a first name, middle name and last name to
* get one nicely formatted name. cMiddle may be omitted.
* Sample call: ?MakeName("Tamar","E.","Granor")
*              ?MakeName("Ted",,"Roche")
LOCAL cFullName

IF TYPE("cFirst")<>"C" ;
   OR (TYPE("cMiddle")<>"C" AND TYPE("cMiddle")<>"L") ;
   OR TYPE("cLast")<>"C"
   RETURN ""
ENDIF

IF EMPTY(cMiddle)
   cFullName=TRIM(cFirst)+" "+TRIM(cLast)
ELSE
   cFullName=TRIM(cFirst)+" "+TRIM(cMiddle)+" "+TRIM(cLast)
ENDIF

RETURN cFullName

See Also

Define Class, LParameters, Parameters, Set UDFParms