Question
Arturo Masero · Feb 17, 2022

Help with indirection and variable scope

Hello fellows, I need you wisdom.

In my organization we code in ObjectScript and .int everyday. When I joined nobody knew barely nothing about classes and their use was almost only for storage definition purpose. During my self-learning in caché I discovered the object oriented programming and class developing was possible in caché and started to code in .cls . Being something self-taught, I may have some basic doubts I'd have missed in my documentation readings.

With this scenario in mind, it is common that we use the @ operator to use wildcards for storing same data structures in different named tables, or playing with indexes. And relating to this I have found a problem in classes I don't know how to get rid off.

I'll try to put an example as generic and understandable as I can:

In OS .int we use this:

Indirection()
(evenTotal) 

set TABLES(0)="EVEN"
set TABLES(1)="ODD" 

for i=1:1:100 {
   set table = TABLES((i#2)) set @(table_"("_i_")")=i 
   // Xecute alternative, same issue
   ; set cmd = "set "_table_"("_i_")="_i
   ; x cmd
} 

set evenTotal=0

set i="" for set i=$O(EVEN(i)) Q:i=""
   set evenTotal = evenTotal+EVEN(i)
}
zw evenTotal 
Q

It works and when finished no variables are in memory after execution (besides evenTotal, included in NEW).

But when I do this in .cls:

ClassMethod Indirection()
{
set TABLES(0)="EVEN" 
set TABLES(1)="ODD"

for i=1:1:100 {
   set table = TABLES((i#2)) 
   set @(table_"("_i_")")=i
   
   // Xecute alternative, same issue  
   ; set cmd = "set "_table_"("_i_")="_i  
   ; x cmd 
}

set evenTotal=0
set i="" for set i=$ORDER(EVEN(i)) QUIT:i=""  
   set evenTotal = evenTotal+EVEN(i) 
} 
zwrite evenTotal

}

I get evenTotal zero. Inside the method I can zwrite EVEN and I see the values, but the ORDER goes empty and if I do 'write EVEN(2)' I get UNDEFINED, although I see the value though a zwrite. It's very confusing.

A side effect I get using @ is EVEN and ODD jumps in memory after the method execution. I don't want them in memory.

Playing with the PublicList keyword has not worked for me.

How can I make the 'ObjectScript .int' example work properly in .cls?

Nowadays I try to evade this practice in cls, and some rare times I add the variables to the publicList and try to KILL them before the end of the method. Has worked so far.

Product version: Caché 2018.1
$ZV: Cache for Windows (x86-64) 2018.1.2 (Build 309_5_21269) Fri Nov 26 2021 11:37:39 EST
0
0 344
Discussion (9)1
Log in or sign up to continue

Thanks for the reply. I've tried that too, but if I set the ProcedureBlock to 0, all the variables stay loaded in memory after the method execution, which is worse than having only the ones used inside the indirection.

Is it a dead end? There is no chance to have it working?

I read about $XECUTE accepting parameters. I tried some aproaches without good results. Could it work using that, @Robert Cemper ?

Also, thanks a lot for the link, it's a very interesting post. 

Next variant

ClassMethod Indirection() [  PublicList = (ODD, EVEN) ]


or using %EVEN,%ODD instead of EVEN, ODD

I think that that's not valid as "in theory" he doesn't know the name of variables. I would try this:

ClassMethod Indirection()

{
    set TABLES(0)="EVEN"
    set TABLES(1)="ODD"
    for i=1:1:100
    {
        set table = TABLES((i#2))
        set @table@(i)=
    }

    zw @table
    set evenTotal=0
    set i=""
    for
    {
        set i=$ORDER(@table@(i)) QUIT:i=""  
        set evenTotal evenTotal+@table@(i)
    }
    zwrite evenTotal
}

BINGO !   You are right!
Saludos,

Ok so, to summarize the thread:

Requirements:
1.- Work with variable table names.
2.- Being able to 'see'/'use' them (i.e. the evenTotal count)
3.- Not having the variables loaded in memory after method execution.

In 'ObjectScript+.int' you can have all 3. In '.cls' you can only have 1 and 2, waiving point 3. 


Options in '.cls' from better to worse in my opinion are:

a. The cleanest option I've seen is @Jose Tomas Salvador version. This option still doesn't have the point 3 but you can kill the variables before leaving the method.

b. Before 'Jose Tomas version' my choice was adding the variables to public list, and trying to kill them before leaving the method. Although it breaks the not knowing the names thing.

c. Using percent variables (obscure, and also my colleages don't know well percent vars, so risky too)

d. Set ProcedureBlock=0 (worst choice in my opinion as it makes visible all variables)

Thank you very much for your help as I was a bit anxious with this thing thinking there was a better option and I was too clumsy to find it.

AS you push on "nothing in memory" which is not related at all to INDIRECTION 
I have streamlined your code to not leave any traces in memory.


ClassMethod PPG() {
 set TABLES(0)="EVEN",TABLES(1)="ODD"
 kill ^||Arturo
 for i=1:1:100 set ^||Arturo(TABLES((i#2)),i)=i 
 set evenTotal=0
 set i=""  if $D(^||Arturo("EVEN",0))
 for  set i=$ORDER(^(i),1,val) QUIT:i=""  if $i(evenTotal,val) 
 zwrite evenTotal kill ^||Arturo QUIT}

OLÉ

What about this one:

ClassMethod Indirection() [ ProcedureBlock = 0 ]
{
 (evenTotal)
 set TABLES(0)="EVEN" 
 set TABLES(1)="ODD"
 
 for i=1:1:100 {
   set table = TABLES((i#2)) 
   set @(table_"("_i_")")=i
   
   // Xecute alternative, same issue 
   ; set cmd = "set "_table_"("_i_")="_i 
   ; x cmd 
 }  set evenTotal=0
 set i="" for set i=$ORDER(EVEN(i)) QUIT:i=""  
   set evenTotal = evenTotal+EVEN(i) 
 
 zwrite evenTotal
}