Page 7 of 7

Re: Macro Best Practices

Posted: Sat Oct 15, 2011 10:10 am
by Bone White
That's what I thought at first Rumble, which is why I edited my post. Unfortunately that edit came between your replies it appears. I'll run some tests:

macro"UDF"

Code: Select all

[h: varX = "UDF"]
macro"Macro Clicked"

Code: Select all

[h: varX = "default"]
[h: runUDF()]
[varX]
Remember, defineFunction is in the format defineFunction(function, macro, ignoreOutput, newScope).

when [h: defineFunction("runUDF","UDF@this",0,0)] ; varX = "UDF"
when [h: defineFunction("runUDF","UDF@this",0,1)] ; varX = "default"
when [h: defineFunction("runUDF","UDF@this",1,0)] ; varX = "UDF"
when [h: defineFunction("runUDF","UDF@this",1,1)] ; varX = "default"

So the controlling variable in the question is indeed newScope. I'm going to edit my original reply back to be correct.

Re: Macro Best Practices

Posted: Sat Oct 15, 2011 1:20 pm
by aliasmask
davout wrote:
Barrodin wrote:I chose to do the whole eval and assign deal because I specifically didn't want it to require me to remember the name of variables -inside- the function when I was writing code that called the function. By passing a variable name and evaluating it inside the function (and vice versa on the return), I can avoid either side having to have "magic names" for the input and output variable.
:shock: Oh my that is major mind shift from other languages.

Thanks.
Yeah, I find it best to avoid "magic names" too. What I also do with macros that share a scope with the parent is "scope" the variable names by putting local.varName or something similar so I know this variable is from there and stays there. Other magic variable that may appear would stand out. Also, even if I can use variables directly from the parent, I usually pass it. But in regards to this thread, we're talking about when it's more economical to reference them indirectly by name.

Code: Select all

...
[H: bigVarObject = {...}]
[H: bigVarName = "bigVarObject"]
[H: am.xx.processBigVarUDF(bigVarName)]
<!-- bigVarObject would be updated by this line --> 

Code: Select all

[H: local.varName = arg(0)]
[H: set("local.varValue",eval(local.varName))]
<!-- do stuff with local.varValue -->
...
<!-- now update original object -->
[H: set("local.varName",local.varValue)] 

Re: Macro Best Practices

Posted: Sat Oct 15, 2011 1:28 pm
by aliasmask
Bone White wrote:That's what I thought at first Rumble, which is why I edited my post. Unfortunately that edit came between your replies it appears. I'll run some tests:

macro"UDF"

Code: Select all

[h: varX = "UDF"]
macro"Macro Clicked"

Code: Select all

[h: varX = "default"]
[h: runUDF()]
[varX]
Remember, defineFunction is in the format defineFunction(function, macro, ignoreOutput, newScope).

when [h: defineFunction("runUDF","UDF@this",0,0)] ; varX = "UDF"
when [h: defineFunction("runUDF","UDF@this",0,1)] ; varX = "default"
when [h: defineFunction("runUDF","UDF@this",1,0)] ; varX = "UDF"
when [h: defineFunction("runUDF","UDF@this",1,1)] ; varX = "default"

So the controlling variable in the question is indeed newScope. I'm going to edit my original reply back to be correct.
Here's a more complicated version of my onCampaignLoad:

Code: Select all

@@ @onCampaignLoad
[H: '<!-- All functions will be given a UDF name excluding tags and special characters -->']
[H: prefix = "am.play."]
[H: this = getMacroLocation()]

[H: '<!-- Macro names ending with .html will allow output. "local" and "sub" also sets newScope to 0. -->']
[H: outputExtensions = json.append("","html","local","out","output","sub","css")]

[H: '<!-- Define functions here with options other than ignoreOutput = 1 and NewScope = 1 -->']
[H: defineFunction(prefix+"jsonToVars","jsonToVars@"+this,1,0)] 
[H: defineFunction(prefix+"varsToJson","varsToJson@"+this,1,0)] 
[H: defineFunction("math.mod","mod@"+this,1)] 
[H: defineFunction("math.div","div@"+this,1)] 
[H: defineFunction("math.isOdd","isOdd@"+this,1)] 

[H: '<!-- List functions names here to exclude from UDF definition -->']
[H: excludeList = json.append("","(new)","mod","div","isOdd")]
[H: tableFunctions = json.difference(getMacros("json"),excludeList)]

[H: '<!-- Define UDFs.  Already defined UDFs will not be redefined. -->']
[H, foreach(fn,tableFunctions), code: {
   [H: fName = replace(fn,"<[^>]*?>","")]
   [H: fName = replace(fName,"[^a-zA-Z0-9_.]","")]
   [H: cleanFN = strformat("%{prefix}%{fName}")]
   [H: extension = listGet(cleanFN,listCount(cleanFN,".")-1,".")]
   [H: newScope = 1 - listContains("local,sub",extension)]
   [H: ignoreOutput = 1 - json.contains(outputExtensions,extension)]
   [H: exclude = listContains("x",extension)]
   [H, if(! isFunctionDefined(cleanFN) && ! exclude): defineFunction(cleanFN,strformat("%{fn}@"+this),ignoreOutput,newScope)]
}]

[H: am.play.setupServer()]

!! 
This is really convenient when I want to set the type of UDF options just by naming the macro a certain way.

Re: Macro Best Practices

Posted: Sat Oct 15, 2011 5:18 pm
by davout
aliasmask wrote:What I also do with macros that share a scope with the parent is "scope" the variable names by putting local.varName or something similar so I know this variable is from there and stays there.
How far deep does this "no new scope" go? If you had UDF A call UDF B which calls UDF C, all defined with not having new scope. Could UDF C modify variables in UDF A? If yes, you may have to go beyond local.varName since it's possible other UDFs could have that. I guess you might have to use functionName.varName

For me, coming from Object-Oriented Programming where data encapsulation is held as sacred, having methods have access to variables of the caller warps my fragile little mind and gives me the heebee jeebees :)

Re: Macro Best Practices

Posted: Sat Oct 15, 2011 6:18 pm
by aliasmask
Yes, if you're doing more than one level you'll have to consider your scope variables. I never found the need to do that though.

Re: Macro Best Practices

Posted: Fri Dec 30, 2011 8:43 am
by davout
Aliasmask,

On your onCampaignLoad how does outputExtensions work?

I created two macros both with the following line of code:

Code: Select all

[R: macro.return = 1d20]
One is named: TestAutoDef and the other is TestAutoDef.output

When I call:

Code: Select all

[H: ld_FC_TestAutoDef()]
The above does not produce any output as expected.

When I call:

Code: Select all

[H: ld_FC_TestAutoDef.output()]
The above also does not produce any output, not as expected.

Am I missing something?

Thanks.

BTW ld_FC_ is my prefix.

Re: Macro Best Practices

Posted: Fri Dec 30, 2011 9:29 am
by aliasmask
The extensions I use are for Wiki: defineFunction() purposes. If you call TestAutoDef.output, that is the macro that should have the output.

So, given what you have, change it in the following way.

Code: Select all

[H: macro.return = 1d20]

Code: Select all

[R: ld_FC_TestAutoDef()]

Re: Macro Best Practices

Posted: Fri Dec 30, 2011 11:16 am
by davout
OK, my example was a poor example. But your comment did help me come up with what I was expecting.

Code: Select all

@@ @TestAutoDef
@PROPS@ fontColor=black;autoExecute=true;fontSize=1.00em;sortBy=;color=default;playerEditable=false;applyToSelected=false;group=Init;tooltip=;minWidth=
<html><table><tr><td>[R: 1d20]</td></tr></table></html>

!!
@@ @TestAutoDef.output
@PROPS@ fontColor=black;autoExecute=true;fontSize=1.00em;sortBy=;color=default;playerEditable=false;applyToSelected=false;group=Init;tooltip=;minWidth=
<html><table><tr><td>[R: 1d20]</td></tr></table></html>

!!

Code: Select all

<!-- This produces NO output -->
[R:ld_FC_TestAutoDef()]

<!-- This produces output -->
[R: ld_FC_TestAutoDef.output()]
Thanks

Re: Macro Best Practices

Posted: Sat Apr 18, 2015 8:59 pm
by JimBlue
Veggiesama wrote:And... USE HTML COMMENTS on anything even remotely strange, and don't slack on the description. You'll thank yourself later when you put down the project for a month and try to jump back into it.
While I have yet to write anything for rptools, comments are always a good idea. Never wait too long, you will forget. I guarantee it. If you don't like comments for yourself, other people will want to read them if you share your framework.