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 gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Odd Variables 2

Status
Not open for further replies.

gbcpastor

Programmer
Jun 12, 2010
77
US
I'm working on someone else's code and I'm running across something I've not see before and I hope someone can explain this to me.

I'm seeing variables that have an '&' in the middle of them and I'm not sure what that means.

Ex.
STORE '' to File&cFiles
store 0 to isBMC&cFiles

Can someone please enlighten me?
 
That's still macro substitution, as explained to you in thread184-1772702

Bye, Olaf.
 
This is something called 'macro subsitution'. In a nuthshell, cFiles contains the name of a variable, for example MyVar. File&cFiles is then another variable whose name is FileMyVar. So your first example stores zero to the variable named FileMyVar.

This is a simplified explanation. You can read more in the Help topics, "& Command" and "Macro Susbstition".

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
In this special case cFiles is a preexisting variable holding any string value, not necessarily a name. But that will be taken as part of the variable names addressed via File&cFiles and isBMC&cFiles.

For example:
Code:
LOCAL cFiles
cFiles = SYS(2015) && creates a random file name (That's the intention, but can be "misused" here to create a random suffix part of following variables)

* cFiles is used here as suffix:
STORE '' TO File&cFiles
STORE 0 TO isBMC&cFiles

LIST MEMORY LIKE File*
LIST MEMORY LIKE isBMC*

LIST MEMORY lists the variables really existing. If you run the code again, other variables will be addressed, so instead of & being part of a name the value of cFiles is part of the variables names with File or isBMC prefix.

Bye, Olaf.
 
Thanks so much guys for your help.

From what I can get from this, I can refer to these variables directly by saying something like fileMyVar="testfile.txt"

Correct?
 
No,

you need to know what cFile is to know the put-together names "File"+cFile and "isBMC"+cFile. Those are the names of the variables addressed. Substitution means replacing, simply as the natural english word meaning. cFile is replaced into the source code and that's recompiled. Every time the line is executed. So this is dynamic. It will depend on the runtime value of cFile. You only use macro substitution, if there will be a variation at runtime, otherwise you could simply use static names you already know at design time, when programming. Many different variables starting their names with "File" and "isBMC" can be addressed by this same code, just changing the value of cFile. So, this are not just two variable names, depending on how cFile changes in an iteration, different calls to this function etc, you can have 4,6,10,100,1000 variables. Whatever cFile changes to. Within a single execution during which cFile is having one value, you can use and only use File&cFile to address this variable and isBMC&cFile for the other, but if cFile changes, these expressions address other variables.

It is all explained already. Are you only theoretically working on this, reading/reviewing source code, or do you have Foxpro at hand? Then I suggest you execute this exercise to understand it.

Macro substitution is universally applicable to any part of the code, commands, as I showed already, another example:
Code:
lcOperator = "="
? 1 &lcOperator 1
lcOperator = ">"
? 1 &lcOperator 1
lcOperator = "<"
? 1 &lcOperator 1

What's really executed is
Code:
? 1 = 1
? 1 > 1
? 1 < 1

But since in code cOperator wouldn't be set statically as here, but may come from data or user input, you cannot predict and precompile what will execute later.

If your task is to port this into another language, don't try to port this 1:1, get your head around the overall functionality of the code and program that in whatever other programming language as you'd do it there, you can't redo macro substitution in C# or PHP or Python or Javascript. The nearest you get is with Javascripts and PHPs eval(), which couldn't cover the substitution of the operator, eval is limited to evaluating expressions, which may cover variable names, too, but what language would allow you to dynamically at runtime specify names? By the way Foxpro also has eval, but that won't help you here, too, because no other language would allow declaring variables with names only known at runtime. As I said in the other thread, it might be good to use an array instead, for such situations. in PHP you could use cFile as the name of an array element, as PHP as associative arrays like $myarr['Firstname'], $myarrr['Lastname'] instead of just being able to address array elements with a numeric index $myArr[0], $myarr[1].

Mainly you need to find out how cFile is set and how it varies, which values can occur there. If it's a limited amount of values you know the limited few variable names resulting, if it's random or dynmically changing with endless variations, like SYS(2015) will in my sample code, you have a bigger conversion problem. Unless you don't need to convert but only understand. Then please ask detail question, what you didn't yet understand about the mechanism. It's a mere string replacement + recompile.

Bye, Olaf.
 
Just to add a word about macro substitution in general.

Using macros to form variable names in this way is not unusual, but it is not the most common use of macro substitution. In general, macros allow you to substitute the contents of any variable in any VFP command.

Consider a couple of examples:

[tt]lcTable = "Customer"
USE &lcTable IN 0[/tt]

That's the same as saying [tt]USE Customer IN 0[/tt]. You would usually use this construct if you didn't know that table name at design time. (Another way of achieving that same result is to use a name expression: [tt]USE (lcTable) IN 0[/tt].)

Or:

[tt]lcWhere = "Salary > 5000"
SELECT * FROM Employee WHERE &lcWhere ORDER BY ID[/tt]

That would be usueful if you wanted to build the filter condition dynamically, using information provided by the user.

I know that none of this answers the original question, but I hope it will throw some more light in this useful feature.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Nice read, Tamar. I come across your paragraph on the Execscript() being even better than COMPILE:

But the need for COMPILE at runtime didn’t
last long...In VFP 7, the Fox team added the EXECSCRIPT()
function

This is off topic, but Well, while it's much simpler to run a script via EXECSCRIPT, the COMPILE command still has a very nice feature of writing out compilation errors into an .err file, while Execscript() would simply fail on wrong code, you can at least see whether a prg compiles via COMPILE. That's still no guarantee it runs. That said, I never used it, though I thought it would fit good for writing a code editor/compiler for end users, kind of a FPA - foxpro for applications within a VFP application. Of course a COMPILE command comes handy for giving end users the same experience you can have with compiling PRGs or any single VFP project item instead of a full build only.

Well, gbcpastor, as said this is off topic, but surely a good reference on the normal applications of macro substitution. It doesn't cover the case here, but as already said several times, whenever you encounter & and a name follows, like &cFiles, then cFiles is a variable which substitutes its own value for its own name (overwriting the ampersand character, too, of course), so &something is like a command telling VFP to print the value of something into the code, compile that and finally execute it. The & operation can appear anywhere in code, unlike an expression, command, function keyword, which mainly is separate with a space and more rules on its position in the code line. The & just marks the place in the code line being changed, followed by the variable name with the substitution value, so macro substitution can also be just part of a name or part of a command option, not only be a full name, option or command. You will not know what is excecuted without also looking at how the value of the variable substituted is set, so macro substitution code can only be understood also knowing the code setting the substitution variable, in your case code setting cFiles. The only limitation you have with & directly following must be a variable name, nothing else. And that variable must be a string variable, containing whatever you want to substitute, that's just limited to not contain line breaks. As Tamars article shows, there's EXECSCRIPT for executing multi line code you put into a variable.

Presenting the line [tt]STORE '' TO File&cFiles[/tt] I can only tell you at runtime this will be [tt]STORE '' TO File...[/tt] where ... just is a "fill-in-the-blank", filled in with the value of cFiles. But since you don't show the code setting cFiles, I can't tell you what happens. All I can say: If you debug this, you'll find a variable cFiles existing at this point, otherwise that substitution would fail with an error, so at least with debugging you'll see what cFiles will be and in turn what File&cFiles will be. You need to debug this, unless the code just simply sets cFiles right before that line and it gets obvious, as in Tamars examples, especially the [tt]cWhere[/tt] example. In that example you see how it will become one of several whereclauses, and how this is helping to write less code, only one main sql query varying via the cWhere substitution, instead of needing three full sql queries to put into the CASE statement cases. And this will be different in different situations, nobody would program a macro substitution, if he always only wants to substitute the same value in that position, then you can write normal code without macro substitution.

Bye, Olaf.
 
Macrosubstitution has its place especially to enable modular logic but don't overuse. (The examples you are dealing with did need it since it was concatenating.) Though the speed difference isn't large unless you're doing huge numbers of loops, maybe about 10% or so in my tests, in some cases name expressions can instead be used. They are also described in Tamar's article linked above and are particularly useful/needed when referencing path and file names.

TamarGranor said:
The biggest reason for using name expressions is macros’ inability to handle names that include embedded spaces. While you may choose not to use spaces in file names, you rarely can control the complete path to your application. So addressing files with a macro is an invitation to crash your code.

Both simple (non-concatenated) examples below are valid. In my code I would use '&' only when '()' didn't work.

MyVar = &HoldName && macrosubstitution
MyVar = (HoldName) && name expression
 
Well, you got to be cautious where to use name expressions. If its not clear you mean a name they are not taken as name expression but simply as normal expression, so they just result in the variable name, not in the variable value:

Code:
LOCAL lcName1, lcName2, lcSuffix, lcFullname
lcName1 = "value 1"
lcName2 = "value 2"
CLEAR
FOR lnSuffix = 1 TO 2
    lcSuffix = TRANSFORM(lnSuffix)
    lcFullname = "lcName"+lcSuffix
    ?
    ? "Working solutions:"
    ? "------------------"
    ? "macro",lcName&lcSuffix
    ? "evaluate with partial name", EVALUATE("lcName"+lcSuffix)
    ? "evaluate with full name", EVALUATE(lcFullname)
    ?
    ? "NOT working:"    
    ? "------------"    
    ? "partial name expression",("m.lcName"+lcSuffix) && not taken as name expression!
    ? "full name expression",(lcFullname) && not taken as name expression!
ENDFOR

Where you can use name expressions is, where it's clear they must be names, eg with the STORE command:

Code:
LOCAL lcName1, lcName2, lcSuffix, lcFullname
lcName1 = "value 1"
lcName2 = "value 2"
CLEAR
FOR lnSuffix = 1 TO 2
    lcSuffix = TRANSFORM(lnSuffix)
    lcFullname = "lcName"+lcSuffix
    
    Store "changed value "+lcSuffix TO ("lcName"+lcSuffix)
    ? "changed via partial name:",lcName&lcSuffix
    Store "changed value "+lcSuffix+ " again" TO (lcFullname)
    ? "changed via full name:",lcName&lcSuffix
ENDFOR

The store command works, as the TO clause must specify a name, not a value.

So for simple read access of a variable name expressions are not working. Something like m.(name) also doesn't work. At minimum you have to make clear you want to evaluate the name to get its value. Name expressions are only a solution where they are unambigously meaning a name.

In the end, for the ? command it's totally unimportant, whether you put brackets around something or not:
Code:
? "lcName1"
? ("lcName1")
? (("lcName1"))

Why should they matter? Brackets are allowed in any epxression and only control, which partial expressions are evaluated before others, if you want that in another order as by operator precedence:
Code:
? 1+3*4
? (1+3)*4
So brackets have a double meaning, they can simply be parenthesis or name expressions. Where it's ambiguos VFP doesn't look, whether the result from interpreting the brackets as parenthesis instead of name expression is a name or not. If the language would have a separate notation than normal brackets, eg using the curly brackets, it would be easier to use name expressions universally.

Here's how intelligently VFP has to be about name expressions even in cases they are usable - here for file names, of course:

Code:
MKDIR C:\testdir
CD C:\testdir
STRTOFILE("test","original.txt")
* now let's see what several COPY TO commands do
ON ERROR * && suppress errors
SET SAFETY OFF && also suppress security prompts
COPY FILE original.txt to copy.txt && unambigously copies to copy.txt (in same folder)
COPY FILE "original.txt" to "copy2.txt"
COPY FILE ("original.txt") to ("copy3.txt")
LOCAL lcOldname
lcOldname = "original.txt"
COPY FILE lcOldname to copy4.txt
COPY FILE (lcOldname) to copy5.txt
COPY FILE ("lcOldname") to copy6.txt
Local lcNewname
lcNewname = "copy7.txt"
COPY FILE original.txt to lcNewname 
lcNewname = "copy8.txt"
COPY FILE original.txt to (lcNewname)
lcNewname = "copy9.txt"
COPY FILE original.txt to ("lcNewname")

Here is the resulting directory content (aside of original.txt):
testdir_qztrfb.png


You see the cases it worked. So on the one hand you can STORE "x" TO ("lcNewname") and get "x" as value of lcNewname, but you can't copy to ("lcNewname"). The concept is even not as strictly working in similar manners for variable and file names and the third names you may use it is alias names. I leave the exercise to make a few test cases to anybody caring. At least macro substitution always works as expected.

Bye, Olaf.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top