*.inc file For loop
How to define a For loop in *.inc file?
Comments
To define a For loop in an *.inc file, you can use ObjectScript syntax in the following ways:
Define the loop with numeric increments: You can use a counter variable, specifying the start, increment, and stop values:
FOR var=start:increment:stop { // Code block content }In this syntax:
Iterate through a comma-separated list: You can iterate over predefined values directly:
FOR var="val1","val2","val3" { // Code block content }This executes the code block for each value in the list [2].
Dynamic iteration with delimiters: If you have a string of comma-separated values, you can split and iterate:
SET customString = "1,3,9" FOR index=1:1:$LENGTH(customString, ",") { SET val = $PIECE(customString, ",", index) // Do something with val }The
$LENGTHfunction obtains the count of items in the list, and$PIECEextracts each element [3].Infinite loops with FOR and break conditions: A loop can also work without a definite stop value:
SET x=8 FOR { WRITE "Running loop x=",x,! SET x = x-1 QUIT:x=3 }This continues until a
QUIT,RETURN, orGOTOcommand exits the loop [1][2].*Custom Macros in an .inc file: An INC file can also define new For-like macros to simplify global or variable iteration. For example:
#define ForAll(%in, %gn) SET gn%in=$NAME(%gn) SET %in="" FOR { SET %in=$ORDER(@gn%in@(%in)) QUIT:%in="" }This allows iteration over a global or list defined in the INC file [4].
Sources:
Thank you for the line: #define ForAll(%in, %gn) SET gn%in=$NAME(%gn) SET %in="" FOR { SET %in=$ORDER(@gn%in@(%in)) QUIT:%in="" }
Please, explain what the arguments "%in" and "%gn" mean/define.
I would day that the bot answer is not relevant to what you asked.
there is no directive to do looping in macro definitions, however, you could look at a directive called #execute where you should be able to execute any valid COS code, but there are some limitations:
#execute | ObjectScript Reference | InterSystems IRIS Data Platform 2025.2
I’d specifically like to use a *.inc file to define a for loop—assuming that’s even possible.
Also, is there a way to bypass the AI bot and direct my question exclusively to a human community?
The AI example is misleading anyhow, as it just does a $O loop without any action to be done
*.inc is typically kind of text generation code during compilation.
What is your expectation for using the *.inc ?
%in and %gn are just text replacement parameters:
%gn is used as globalname, %in as first level subscript
I realized that AI-generated answer looked "unreal".
I would like to have *inc file entry to convert an array into a string and string into an array. ObjectScript code to do it is not an issue at all - I just want to have it as a macro in *.inc file. There is a chance I have no enough knowledge regarding *.inc macros because the simple "if" statement doesn't work as well (error on compiling within a class) like: #define TestIf(%arr) if (%arr > 0) { QUIT 5 }. What am I missing? Thank you.
(error on compiling within a class) like:
#define TestIf(%arr) if (%arr > 0) { QUIT 5 } ; no final dot.
#define is an element of ObjectScript
so it has to be embedded in a [Class]Method
and it is only available within that method
It can't be flying free inside a class definition.
If you need your $$$TestIf(...) in more than 1 method, you can deposit
it in some TestIf.INC and include it BEFORE the Class statement !!
Then it is visible to ALL methods.
Attention: You can INCLUDE just 1 single *.INC in a class definition.
If you need more than 1, you have to cascade it with #include in the first *.inc
Include TestIf
Class A.PERSON1 Extends%Library.Persistent
{
Parameter GlobalName = "^.........!";;/// .......ClassMethod michael(param) as%Integer
{
$$$TestIf(param)
.........
quit$$$OK
}
Attention: You can INCLUDE just 1 single *.INC in a class definition.Including Include Files
To include multiple include files at the beginning of a class definition, the syntax is of the form:
Include (MyMacros, YourMacros)
Thanks for the correction.
I had something similar in mind, but didn't find the related doc!
I would like to have *inc file entry to convert an array into a string and string into an array.If the array is one-dimensional, then you can do without cycles altogether. I'll give you a small example below:
#define Array1ToJSONString(%arr,%json)##continue s ##unique(new)=##class(%ZEN.proxyObject).%New()##continue d ##unique(old).%CopyFromArray(.%arr),##continue ##class(%ZEN.Auxiliary.altJSONProvider).%WriteJSONStreamFromObject(.%json,##unique(old))##continue s %json=%json.Read(3641144) #define JSONStringToArray1(%json,%arr)##continue d ##class(%ZEN.Auxiliary.altJSONProvider).%ConvertJSONToObject(%json,,.##unique(new)),##continue ##unique(old).%CopyToArray(.%arr)
Usage:
set a("color")=$listbuild("red","blue")
set a("price")="expensive"
set a("size")=$listbuild("large","small")
$$$Array1ToJSONString(a,jsonStr)
zwrite jsonStr
$$$JSONStringToArray1(jsonStr,b)
zwrite bThank you for the examples. However, replacing a "For" loop isn't quite what I'm looking for.
I'm able to create a simple *.mac routine to achieve the desired functionality, but my issue lies in understanding why the "For" macro fails to compile when used in a .inc file.
What I'm really aiming for is something like Robert's example:
#define MyLoop(%count) set x="" for i=1:1:%count set x=$order(^%SYS("JOURNAL",x),-1)
(Benjamin's example is more complex, but essentially follows the same idea.)
Also, just to note — I'm encountering a similar issue with IF statements in .inc files. Here's a reference to a related discussion:
https://community.intersystems.com/post/failure-compile-inc-simple-if-s…
Thanks again for your help!
Show your code with the for loop that doesn't work.
If the array is multidimensional, then you can't do without loops (the code is without error handling):
#define ArrayToStr(%arr,%str) set ref=$name(%arr),%str="s " for {set ref=$query(@ref,1,val) quit:ref="" set %str=%str_$$FormatName^%qcr(ref,1)_"="_##class(%Utility).FormatString(val)_","} set $extract(%str,*)=""
#define StrToArray(%str) xecute %strUsage:
set a(0)=7
set a(1,"color")="green"
set a(1,"color","green")=""
set a("color",$listbuild($double(3)))=$listbuild("red","blue")
$$$ArrayToStr(a,str)
zwrite str
kill a
$$$StrToArray(str)
zwrite aRe: "If" in *.inc
#define TestIf(%arr) if (%arr > 0) { QUIT 5 } is already included in ...Utility.inc (which contains hundreds of macros successfully used throughout the application).
Include (....Utility) is already declared within a ...FSLogging class.
set a = $$$TestIf(3) is set within a LogArchive classmethod. Expected "a" value is 5
And compilation error: .png)
Where is an issue? Thank you
Now it's clear instead of (%arr > 0) you simply should write (%arr>0)
The ObjectScript compiler is interpreting the blank after %arr as an argument terminator
@Robert Cemper
Same compilation error for all options below:
#define TestIf(%arr) if (%arr>0) {QUIT 5}
#define TestIf(%arr) if (%arr> 0) {QUIT 5}
#define TestIf(%arr) if (%arr>0) { QUIT 5 }
Strange. It should generate IF (3>0) {QUIT 5} and accept it.
Could you pls simplify the case for testing to
#define TestIf(%arr) if%arr>0QUIT5Not yet Halloween, but this looks spooky:
For me, all variants work:
My versions:
InterSystems Studio Client 2024.1.0 Build 262
Server IRIS for Windows (x86-64) 2024.3 (Build 217U)
#define TestIf(%arr) if %arr>0 QUIT 5 - same error, while no "(" in the "Invalid command text in screenshot.
IRIS for Windows (x86-64) 2022.1.2 (Build 574_0_22407U) Wed Apr 5 2023 11:19:54 EDT
InterSystems Studio Client 2022.1.2 Build 574
Server IRIS for Windows (x86-64) 2022.1.2 (Build 574_0_22407U)
So this means that the IF is not applicable in your context.
What commands are before the $$$TestIf(3) and after ?
I think of some WRITE $$$TestIf(3) .....
Or you may try to explicitly write the macro-generated code to see any hidden issue
I'm running a bit out of fantas,y what may go on
I've created separate post for *.inc "If" issue (https://community.intersystems.com/post/failure-compile-inc-simple-if-s…), hoping somebody could bring an idea for resolving the issue.
If no positive resolution I would try to create WRC ticket based on your note that everything works on your environment.
But getting back to the initial issue regarding "For" loop in *.inc file.
Can you post any example of using "For" loop in a macro?
Thank you.
TestFor.inc
#define MyLoop(%count) setx=""for i=1:1:%countsetx=$order(^%SYS("JOURNAL",x),-1) writex,! in Test class:
ClassMethod Mike()
{
$$$MyLoop(7) Write ?5,$get(@$ZR," *** "),!
} Resulting in
PURGED *** PREFIX MAXSIZE 1073741824 LIFESPAN *** LAST 1^C:\InterSystems\IRIS242\mgr\journal\20251029.003 EXPSIZE 0 CURRENT 1^C:\InterSystems\IRIS242\mgr\journal\20251029.003
@Robert Cemper
While the definition, IMO, seems valid, I got the error on attempt to compile class with call in it - see below. But the message does not contain annoying "Invalid function" - complain is regarding missing space in definition.
.png)
Example of @Robert Cemper compiles without errors for me (even on Caché 2018.xxx):
Class dc.a [ Abstract ]
{
ClassMethod Test()
{
#define MyLoop(%count) set x="" for i=1:1:%count set x=$order(^%SYS("JOURNAL",x),-1) write x,!
$$$MyLoop(5)
}
}"Your" definition is NOT in *.inc file!
Upon inserting in *.inc as:
#define MyLoop(%count) set x="" for {i=1:1:%count set x=$order(^%SYS("JOURNAL",x),-1)} QUIT x
And setting in classmethod as:
set x = $$$MyLoop(5)
I got the following compilation error: .png)
You have two mistakes.
- Instead
for {i=1:1:%count set x=should befor i=1:1:%count {set x= - Macros ≠ Function
A working example:
#define MyLoop(%count,%result) set ref="",%result="" for i=1:1:%count set ref=$order(^%SYS("JOURNAL",ref),-1),%result=%result_$listbuild(ref)
$$$MyLoop(5,x)
write $listtostring(x)It works! Thank you - let me try to set "real life" code and test.
Real life" code works as well - thank you very much!
if you have time, please, look into similar issue with "IF" in *inc file https://community.intersystems.com/post/failure-compile-inc-simple-if-s…
Don't pay attention on proposed replacement $SELECT option - I really want to have "clean" IF statement in *inc file.
Thank you.
Here's an example that combines two bitmaps:
#define AndBits(%dst,%src) ##continue
set mbChunk="" ##continue
for { ##continue
set mbChunk=$o(%dst(mbChunk),1,mbBits) ##continue
quit:mbChunk="" ##continue
set mbBits=$bitlogic(mbBits&%src(mbChunk)) ##continue
If $bitfind(mbBits,1) { ##continue
Set %dst(mbChunk)=$bitlogic(mbBits) ##continue
} Else { ##continue
Kill %dst(mbChunk) ##continue
} ##continue
}Using ##continue helps you avoid cramming everything onto a single line
I am pretty familiar with using "##continue" in macro definition - there are a lot for other macros with ##continue in "*inc file.
Upon defining the macro per your example the error is:
.png)
Upon deleting line "set mbChunk="" ##continue" (seems like unnecessary) the error is more definitive (pointing to "for" itself"):
.png)
that first line is required to initialize the mbChunk variable used in the loop, else you'd get an UNDEFINED at runtime.
I'm thinking the way how you're using this macro is slightly off, and this has the same root cause as the other thread you opened. This macro is expected to be used on its own:
// some logic here $$$AndBits(^glo1,^glo2) // more logic here, and note there's at least a space before all these lines
hope this helps
@Benjamin De Boe
You are right about "mbChuck" initiation for runtime, but the discussion is about compiling macro reference in a class which is way prior runtime.
I need output from macro "For" - so I can't just use "$$$AndBits(^glo1,^glo2)" (or "do $$$AndBits(^glo1,^glo2)"). I am testing compilation and upon finding a right syntax, "For" macro will be extended to return value I am looking for (for example: argument could be an array and output will be comma-separated sting of array values). ObectScript code, to achieve it, is not an issue - I would like to have it as macro in *inc file. So, any ideas how to transform your example into compilable entry? Thank you
💡 This question is considered a Key Question. More details here.
.png)