Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

LPARAMETERS and PARAMETERS statements 4

Status
Not open for further replies.

AndrewMozley

Programmer
Oct 15, 2005
621
1
18
GB
I have never been sure of the difference between LPARAMETERS and PARAMETERS.

Have written a short test program to see if I can detect the difference.

Code:
CLEAR
SET TALK OFf
LOCAL X
x = 2
SET STEP ON
lAns = MyFUNC(x)
? "lAns = " + STR(lAns,2)
? "vParm = " + STR(vParm,2)
RETURN

FUNCTION MyFunc
PARAMETERS vParm
*  or LPARAMETERS vParm

   vParm = 3
   RETURN vParm

In each case the results are the same; (the program also gives an error on the last ‘? “vParm = “ . . . ‘ statement)

Would be grateful if anyone cares to provide code which shows the difference between LPARAMETERS and PARAMETERS. I have never learned!

Thanks - Andrew
 
If you use PARAMETERS, the variables specified are PRIVATE. If you use LPARAMETERS, they are LOCAL. Private variables are available to any procedure/function you call from within that method. Local variables are not available ANYWHERE outside that method.



If you want to get the best response to a question, please check out FAQ184-2483 first.
 
Hi Andrew,

From Hacker's Guide to Visual Foxpro

LPARAMETERS creates the parameters as local variables, while PARAMETERS makes them private. We strongly recommend you use LPARAMETERS unless you have a specific reason not to. You can only use one or the other in a given routine. As soon as FoxPro finds a parameter declaration, it rejects any other.

and from the same Hacker's Guide

PRIVATE is a little tricky. Private variables are visible in the routine where you create them and in all routines called by that routine, unless those routines have their own private or local variables with the same names. When the routine that created the variable finishes executing, it's automatically released. (You can, of course, release it yourself first.)

Got that? We didn't think so. Think of it this way: When you create a private variable, you can see it until the routine that created it ends (or you explicitly release it). If that routine calls another routine, it too can see your variable unless it declares a variable of its own with the same name.

means

the scope of LPARAMETERS is limited to the function itself whereas the scope of PARAMETERS is limited to the function plus the function(s) this function calls (top down but NOT up). Hence ? "vParm = " + STR(vParm,2) in your code yields the error.

hth

MarK
 
Amdrew,

The other two have summed it up nicely.

The best advice is to always use LPARAMETERS, unless you have a jolly good reason to do otherwise. In fact, I can't think of any time - from VFP 3.0 onward - that I have use PARAMETERS rather than LPARAMETERS.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Mike said:
I can't think of any time - from VFP 3.0 onward - that I have use PARAMETERS rather than LPARAMETERS.
I've never used PARAMETERS either. Nor do I ever use PRIVATE. I go further and make all my variables LOCAL. If I need them scoped in some fashion, I use THISFORM.AddProperty('PassMe','') or give it some other value or type. That makes the property more or less PUBLIC for that form (only), changeable at any level. I don't have to worry about whether a certain variable name can be used at a particular level.

Some might come up with exceptions. I would guess form properties could be used to work around them. Just my opinion.

Steve
 
Here's a code example that only works with PARAMETERS in the first function called:

Code:
functionABC(123)

function functionABC()
parameters p1

return functionXYZ()

function functionXYZ()
   return p1+1
It is no sensible use case, but functionB can only access p1, if it's received from functionA as a PRIVATE parameter and not as a LOCAL parameter.

So the use case would be a function that is the interface to the outside world using further functions internally, which all work on the initially passed in parameters without needing to pass them on and on and on, as all further functions can access what the entry point function received in the first place.

It could make complex partially recursive functions faster as you only have one time parameter passing in and can work with that on all recursion levels.

That said, recursion usually is a bit of a devil and you better find an iterative or otherwise nonrecursive solution instead. Also, even when using recursion you often will pass on a changed value to the next recursion level and rarely are interested in the values passed in to the entry level of the recursion.

You could find non-recursive scenarios in which splitting up the functionality into several functions (or prgs) makes sense and you would use one of them as entry point to receive private parameters for the whole family of prgs.

Well, and obviously you find no difference of bahavour after the code ran, both local and private parameters or vars are released when the function that created them ends and returns. PRIVATE isn't the same as receiving parameters by ref vs by value, too, so you don't redecalare an outside variable as a private variable, just because you receive parameters into a private parameter variable.

There also is only one reason for this alternative to LPARAMETERS: IF you'd want to guarantee your private function variable isn't overriding an already existing private variable defined outside and previously, you would need to specify PRIVATE varname to state that your code will now claim its own right on that name and won't influence that previously exiting variable. But you can't put a PRIVATE statement before a PARAMETER or LPARAMETER statement, they have to be the first line in the code. So without the existence of PARAMETERS you could not first specify that you want to use this private variable name for yourself and then receive the passed in value into it. With PARAMETERS you can and will do both at the same time.

This also makes PARAMETERS the only actual declaration of private variables. PRIVATE on its own does not declare a variable, PARAMETERS does, because it will also make the variable names exist and be of value .F. if no parameter is passed in and PCOUNT() is 0 or lower than the number of parameters.

Chriss
 
Steve,

you're completely right. The scope of properties, even if you make them private or protected in the sense of the OOP visibility of them, is a much simpler to understand and visualize concept.

It's also the reason you can really get rid of any use of PRIVATE vars, parameter or not.

Chriss
 
Thank you Chris for confirmation. I've programmed by myself for a long time and I don't have (non-virtual) contact with fellow "VFP'ers" around here (southern Virginia). You "guys" help me a lot on this forum.

As an aside, programming alone lets me use my own variable naming convention, which is pretty loose at times. I realize this could cause maintenance problems for someone else. But I comment profusely and I do the maintenance. Don't get me wrong. That is not really my preference. I don't want to be indispensable to any business (or person), especially at my age. I'm trying to retire 100% but I still have a couple of companies who ask for changes.

Steve
[end of rant]
 
Hi all,

Please don't yet throw away the PRIVATE variables. If e.g. you want to pass a variable to a report (which can't be created in the report) you need to declare it PRIVATE in the calling method. The code below is a sketch of how I use private variables e.o. for a variable report's footer.

Code:
LOCAL ARRAY laGroups[1]
PRIVATE pcPageFooter, piGroups, pdDateTime

USE dbfReport ORDER cdxDagDeel

SELECT COUNT(cDagDeel) FROM dbfReport GROUP BY cDagDeel INTO ARRAY laGroups

piGroups = ALEN(laGroups)
pdDateTime = DATETIME()

DO CASE
[indent]CASE CDOW(Date()) = "Monday"
pcPageFooter = "Printed by Mandy"

CASE CDOW(Date()) = Tuesday"
pcPageFooter = "Printed by Jeff"

OTHERWISE[/indent]

ENDCASE

REPORT FORM Planningtest2 PREVIEW

hth

MarK
 
MarK,

a report can also access any local variables in scope in the code that does the DO report. It's a myth you need private vars for this.



Chriss
 
Hi Chris,

You're right - I tested it a minute ago.

Thank you for the hint

MarK
 
I can't remember the details, but I once needed to use private variables when passing information to a menu (the type that you create with DEFINE BAR, etc.) The menu needed to access a variable (possibly in the SKIP FOR clause) and there was no way for a local variable to be in scope at the time that the menu was displayed.

If I can find the relevant code, I'll report back with more details.

But the main point remains: if is very rare for parameters not to be local to the routine which receives them.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Sometimes private vars are useful for the values of SQL parameters, when you use a SQL passthrough class with a function that wraps up SQLEXEC and error handling in one place and you don't want to use general parameter names like t1 to t27 to be able to pass in the parameters to this method. Instead you can write SQL and are free to choose parameter names, providing you define them as private vars before calling the SQLPassthrough.exec() method.

There's a real usecase. And PARAMETERS would help a tiny bit, if you write a wrapper to all your SQLExecs with a class that has a method per parameterized SQL with PARAMETERS statement. These methods then can call the internally private or protected exec method which then has access to the PARAMETER variables.

So, for example, you write a method for your order passthrough query like GetCustomerOrders():
Code:
PARAMETERS tiCustomerID

This.exec("Select * From Orders Where CustomerID = ?m.tiCustomerID")

And a general exec method like
Code:
LPARAMETERS tcQuery

If SQLEXEC(This.nSQLHandle, m.tcQuery, This.cResultAlias)<0
   This.handleSQLError()
Endif

There could be a nSQLHandle_access method that takes care of the validity of nSQLHandle or connects, if the handle is invalid.

The exec method doesn't need to know anything about the parameters in tcQuery, as the individual methods per query receive the parameters into private variables still available to the general exec method.

And the usage of that then could be:
Code:
goSQL.cResultAlias="CustomerOrdersOfCustomer1"
goSQL.GetCustomerOrders(1)

Many more details can go into this, but it would make good use of PARAMETERS. If you don't like the thought of a plethora of methods - one for each query - then think again, this is a perfectly normal way of encapsulating all your SQL usage into a single place, each one handling its parameters precisely, as it demands them as PARAMETERS. You can even handle how missing parameters should be handled using PCOUNT, EVL to replace non-passed-in parameters with a default value, etc. etc.

Chriss
 
I just found the code where I used a private variable in conjunction with a menu:

Code:
* Opens a shortcut menu
LOCAL lcMenu
PRIVATE poObject

* Store an object reference to the control in a private variable
* for use by the menu
poObject = THIS

IF NOT EMPTY(this.shortcut_menu)
  lcMenu = DEFAULTEXT(this.shortcut_menu, "MPR")
ENDIF

DO &lcMenu

Ths code is in a custom method of a generic text box control. It lets the user open a short-cut menu (typically containing Copy, Cut, Paste, etc) when right-clicking on the control. The name of the menu file (an MPR file) is held in a custom property of the control (shortcut_menu).

The private variable holds an object reference to the control. This allows the menu to access the various properties and methods of the control. This is used, for example, to determine if the control is currently read-only or disabled.

To be honest, I'm not sure if a private variable is necessary here. I wrote the code over 12 years ago. I presumably thought at the time that it was the right thing to do.

Mike


__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Thank you for these helpful replies.

I can see the advantage of LPARAMETERS - although I don’t believe that I have come across a case where the value of parameters in a function have been corrupted by a lower level function altering the value of the PRIVATE variables defined by PARAMETERS. But I suppose it could happen.

One can equally use the option of putting parameters in brackets in the FUNCTION statement itself :

Code:
FUNCTION MyFunc(vParam1, vParam2)

Do other people use that?


 
Andrew,

defining functions like that with bracketed parameters, makes them LPARAMETERS, too.
Yes, it's usable. When you compile for COM interop this allows to specify the COM types, too. So it's useful. The parameters of the brackets are shown by IntelliSense that way, too.

The point about PARAMETERS isn't to enable altering the parameter values, you mostly only read them in the function code, even if you do so on the entry level, nothing goes back to the caller that way, even if the caller had defined private vars of the same name as PARAMETERS, PARAMETERS will make them new private variables not overriding those of the caller:

Code:
PRIVATE all like p*
pcParam = 'callervalue'
FunctionXYZ('passed-in value')
? pcParam
Function FunctionXYZ()
   PARAMETERS pcParam
   ? pcParam
   pcParam = 'changed value'
Endfunc

So PARAMETER does not say "use private variables of these names or declare them, if they don't exist yet, it always declares new private vars and hides the ones that already existed to be unhidden again, when the code ends. So both PRIVATE and PARAMETERS have no upwards effect, upwards meaning towards the caller. The effect of PRIVATE and PARAMETER always is downwards only.

So indeed, if you want a function to make use of private vars the caller sets, then you should actually NOT use PARAMETERS and NOT PRIVATE, too. You just introduce a hard to debug problem, if you depend on those private vars to be set up by the caller. If your first usage is a read of them, you depend on them existing. If your first use is setting them to some value they could be used in the same manner as parameters passed in by reference, but you'd need to document that to make it understandable,

I'd prefer explicit use of passing by reference into LPARAMETERS for that. It doesn't tell by the mere code setting an LPARAMETER to a new value, that it goes back to the caller, that's only visible in the caller code that makes a call using @var as a parameter and then reads a response in that var. The good thing is that you see this in the caller code, where it matters. It's still not a thing you can use to enforce getting returned values, as the function code is never forced to write result values into parameter variables. It's always a convention by both caller and called code to make use of private vars or by reference parameters.

Chriss
 
Andrew said:
I don’t believe that I have come across a case where the value of parameters in a function have been corrupted by a lower level function altering the value of the PRIVATE variables defined by PARAMETERS

The issue here is that the function might call a lower-level function which has been imported into the project, perhaps part of a generic function library or a framework, or perhaps from another application. In those cases, there might well be a clash of names.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Mike is right.

And most of the time you get into trouble with name clashes, is with very common variable names like i,j,k for LOOPs.
If you forget to declare them LOCAL, then your counters can become influenced calling something else.

It actually takes the error of both you and the external code programmer to get a clash, if the norm is to declare everything local.

With PARAMETERS you create private vars intentionally. And in the sense of protecting yourself against name clashes of code you use from somewhere else and even by yourself, you make them local instead. That's the reasoning for locals everywhere. Then you only loosen that intentionally, if at all.

Chriss
 
Andrew,

as it all goes around the issue of damaging code functionality, if you ask because you want to reassure yourself it's okay to leave in PARAMETERS instead of LPARAMETERS in all your legacy code, it takes a single pass with Code references to replace PARAMETERS with LPARAMETERS everywhere in whole project or folders. Just do it and be done with it.

Chriss
 
Chris said:
it takes a single pass with Code references to replace PARAMETERS with LPARAMETERS everywhere in whole project or folders. Just do it and be done with it.

Hang on a minute. Using Code References in that way will replace every instance of the word PARAMETERS, which is not quite the same as every instance of the PARAMETERS statement. So it will replace the PARAMETERS() function with LPARAMETERS(), which would then trigger an error. It would also replace any instance of the word inside a comment (which won't do any harm to the code, but which might look a bit funny).

So, yes, replace all PARAMETERS with LPARAMETERS, but do it carefully, ideally by choosing "Confirm replacements" in the Replace dialogue (but think about how tedious that could be).

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top