Hi to all,
this is my first topic in here and as you might see, I'm no native english-speaker, so please - don't rip my (virtual) head off for making the usual mistakes. ;-)
I'm using VFP 6.0 for 2 years now and getting more and more happy about the possibillities macros offer in what I call "runtime coding".
I found the first limits of using macros the time I wanted to add dynamic "for-while", "if-endif"-, "do case-endcase"- structures. Personally, I found that creating temporary prg's and handling the paramaters sometimes isn't worth the saving of a few hundred lines of "duplicate" code.
But, as a wise man said "I know that I know nothing at all" (or so), so my question is: Where are the further limits in macro-use?
I'd even like a discussion about the pro's and con's of macro-use (as I didn't find it in here. *g*).
Pierre "the uncle" Albisser [2002.06.01]
A macro should not be used to substitute for a command. Command ARGUMENTS are fine. The reason is that if the command itself is contained in a macro, the runtime engine will not be able to execute it as it is not compiled yet. It may work in development, but will not work in runtime.
What? The above is totally false. Have you never built a select statement into a variable and then used a macro to execute it? It works fine, even in the runtime! I assume you consider SELECT a command?
cSQL = "SELECT * FROM MyTable WHERE PK = 123"
cSQL = cSQL + " INTO CURSOR CUR_MYTABLE"
Also, you should understand the difference between a macro and an indirect reference to a filename. Indirect references should be used whenever possible.
And finally, EVAL() should be used instead of macros whenever possible. Use of a macro causes the command parser to be invoked which is time-consuming. Ray Kirk
If you are asking whether you can execute uncompiled code with multiple lines and conditionals, the answer is "Yes, using Code Block or as of VFP6 SP3, using the Compile command". The options are summarised in this wiki at Code Block, note that Rick Strahl has reported significant speed differences between the two.
Regarding using macros as substitutes for commands, my apologies. I stand corrected. However, I still believe that it is an unsafe practice and in the case of sql select, I have always used a variation:
lcSQL = ' * from MyTable where lcCondition into cursor temp'
Why unsafe? It just looks differently. The VFP compiler does not compile lines containing macros at all so no matter what they contain (incl. syntax errors). Everything is processed at run-time.
BTW, your example does not need to be executed as a macro. You can simply write:
select * from MyTable where lcCondition into cursor temp
or probably there is one more & missing in the select:
select * from MyTable where &lcCondition into cursor temp
Pierre-- If you aren't already aware of it, the solution to your problem may lie in VFP 7, in the EXECSCRIPT() command. Perhaps it would be easier for you than creating the program and compiling it yourself.
As to using macro commands, having that functionality hidden in my app has saved me several times. One all-purpose form gives me the functionality of the command window from any client without having to run across the plant back to my desk. Granted, I would immediatly remove this if the app was ever to be used outside this plant, but for now, it's quite helpful when you're the only one involved writing AND supporting the app.
As mentioned above, Vfp6Sp3 introduced the Compile command, so all you need there is:
FUNCTION Vfp6Sp3ExecScript( pcScript )
LOCAL lcFile, lResult
lcFile = "jnkRunIt.prg"
DO WHILE File(lcFile) or File(ForceExt(lcFile,'.FXP'))
lcFile = Left(SYS(2015),8)+'.PRG'
DO &lcFile TO lResult
DELETE FILE (lcFile)
DELETE FILE ( ForceExt(lcFile,'.FXP') )
As an example of "unsafe", here is a direct quote from the vfp help file.
"Macro substitution statements that appear in DO WHILE, FOR, and SCAN are evaluated only at the start of the loop and are not reevaluated on subsequent iterations. Any changes to the variable or array element that occur within the loop are not recognized."
In addition, the macro operator can only be applied to MEMORY VARIABLES. Not to FIELDS or PROPERTIES. ie.
&mVar works, but
&table.field does not, and
&Object.Property does not, even if the "Table." or "Object." is not used and not necessary because of having the table alias selected or being in a method of the Object.
This is talking about Case A (below) and not Case B, though, Right?
** Case A
** This loop will ALWAYS print 1 thru 10
** because the "&lcTo" replacement is only done once.
lcTo = '10'
FOR lnI = 1 to &lcTo
lcTo = '7'
** Case B
** This loop will ALWAYS print 1 thru 10
** because the "&lcPrint" replacement is ALWAYS done.
lcPrint = '5'
FOR lnI = 1 to 10
lcPrint = alltrim(str(lnI))
if lnI = &lcPrint
Well, the VFP help is wrong. I just tried this, and B isn't supposed to work like that, according to help. Either that, or help needs to be a *lot* clearer on this. Of course, macro substitution doesn't really matter in the first example, as those only get evaluated once anyway - Macro substitution shouldn't matter. Like this:
** Case C
** This loop will ALWAYS print 1 through 10, becuase the expression only gets evaluated once anyway.
lcTo = 10
FOR lnI = 1 to lcTo
lcTo = 7
** Case D
** Again, this loop prints 1 - 10, because the macro is getting substituted each time
i = 0
DO WHILE i != 10
i = i + 1
cMacro = Str(i)
** Case E
** Finally, I get what help's saying!
** This will result in an infinate loop:
cMacro = ".T."
DO WHILE &cMacro
cMacro = ".F."
** Case F
** Here's what you do to get around this type of thing:
** This will only run once:
cMacro = ".T."
DO WHILE .T.
cMacro = ".f."
Sorry for the delay in answering, had to run around to visit customers. ;)
Well, as for me, I use macros to "shorten" my code, especially as it tends to repeat itself.
As an example: I needed to create a graphical interface with a dynamic number of shapes in different sizes and colors to show the "used time" and "spare time" of a field worker. As the number of operations varies as its individual length, I was not able to use a fix set of shapes.
I had to show the working time of 10 field workers a day in one form. So, I had to do the same thing all the time: getting the data, converting it for use to create the shapes, deleting the old shapes, adding the new ones. No problem for getting and converting the data so far, but as I didn't want to store the position and so on of all shapes, I prefered naming the shapes in a unique way - "worker1_shape5" for example and just stored the amount of shapes "added to the worker".
This repeated for every one of those workers, so I produced macros to add the shapes and alter their width, color and so on, like that:
lcMak = "THISFORM.worker"+lcWorkerNo+"_Shape"+lcShapeNo+".Width = 30"
... and so on.
Spared me a lot of code as I didn't had to repeat all that all the time and made the whole stuff a lot easier to maintain.
The other time, I had to create "dynamic" forms, depending on the data stored in a table, width different number of buttons, grids and so on...
For the example above I can assure you that EVAL() yields a reference to the shape, allowing you to use WITH ... ENDWITH to set properties efficiently and avoid macro substitution. -- John Ryan
Well, might be... I wasn't able to have a look at it, as my boss tends to wait for VFP 8. So I still have to get along with my VFP6SP3. ;)
Thanks to you all for this discussion, so far... :)
See Also: Macro Replacement, EXECSCRIPT
( Topic last updated: 2002.09.12 09:16:11 PM )