Question
· Oct 1

Execute a routine with argument by name - Without using Xecute!

Hello everyone, 
so lets say i have the following:

Hello
Q
say(arg)
    w arg
 Q


and i am trying to execute it by its name such as:


s a = "say^hello"

*execute the name inside a*
 

now I know for a fact that if say were written without any argument then do @a would work!

but I can I pass an argument in this case?
I don't want to use Xecute because I am in need of really high performance code.

 

Product version: IRIS 2019.1
Discussion (10)3
Log in or sign up to continue

I assume, you have a routine 'hello' like this

hello ; this is my hello-test
	 quit
	 
say(arg)
     write arg,!
     quit
     
add(x,y) Public
{
  quit x + y
}

and some variable, set as follows

set rou="hello"
set say="say", add="add"
set a1=5,a2=10, arg="Hello World"
set sayall="say^hello(arg)"
set addall="$$add^hello(a1,a2)"

then you can do things like

do @sayall            ---> Hello World   // command indirection
do @say^@(rou)(arg)   ---> Hello World   // name-indirection
do @say^hello(arg)    ---> Hello World   // name-indirection
do say^@(rou)(arg)    ---> Hello World   // name-indirection
do say^hello(arg)     ---> Hello World

write @addall               ---> 15      // command indirection
write $$@add^@(rou)(a1,a2)  ---> 15      // name-indirection
write $$@add^hello(a1,a2)   ---> 15      // name-indirection
write $$add^@(rou)(a1,a2)   ---> 15      // name-indirection
write $$add^hello(a1,a2)    ---> 15

// Caution, you can't do
write @addall + 3  // with "+ 3", the write command is turned into an
                   // expression, and in turn, the indirection is now
                   // a name-indirection. That gives you a SYNTAX error
                   
// but you can do
write $$@add^@(rou)(a1,a2) + 3 --> 18

Hey after further testing I encountered a problem.

When trying to run a routine from within a method using the terminal, it would execute the argument in our terminal scope and not in that classMethod context, For example:

ClassMethod test()
{
    s arg = "asd"
    s routine = "say^hello(arg)"
}


then in terminal: 

s arg = "ddd"
d ##class(something).test()


output:

ddd

and NOT asd as you would expect...
any help on this topic?

Indirection has its rule:  Indirection works with PUBLIC variables,
i.e. the variable, you address, mustbe a public variable, in your case the <arg> variable.
This is due to compatibility with old applications,
developed before the introduction of the block structure.
  
You have two options

instead of using argument indirection (what you currently do),
use name indirection for label and routinname, see method Test1()

@lab and @(rou)  because label- and routine-names are not variables.

If you want to keep argument indirection, just tell your class, that certain variables were PUBLIC
see method Test2()

In your example, you got a wrong result because, by chance the variable <arg> was defined in the terminal session with the same value as in methode code, see method Test3()


ClassMethod Test1()
{
    s arg="argument-1"
    s lab="say", rou="hello"
    d @lab^@(rou)(arg)
}

ClassMethod Test2() [ PublicList = arg ]
{
	s arg = "argument-2"
	s routine = "say^hello(arg)"
	d @routine
}

ClassMethod Test3()
{
	s routine = "say^hello(arg)"
	d @routine
}

Now some tests in a terminal

kill   // we kill all local variables
do ##class(your.class).Test1() ---> argument-1

kill
do ##class(your.class).Test2() ---> argument-2

kill
do ##class(your.class).Test3() ---> <UNDEF> *arg ==> missing arg

kill
set arg="surprise"
do ##class(your.class).Test3() ---> surprise

// you can prove things the other way too

set arg="my-value"  // variables, defined in a terminal session are public
do ##class(your.class).Test1() ---> argument-1
write arg --> my-value  // arg wasn't overwritten!

do ##class(your.class).Test2() ---> argument-2
write arg --> argument-2  // arg IS OVERWRITTEN

Method Test3() shows, indirection works with public variables

I have a comment on the sentence "I don't want to use Xecute because I am in need of really high performance code."

I have always thought of Xecute and @ (indirection) as similar in performance characteristics. But I don't know what the current reality is. Is it possible that omer was told that Xecute is slow and indirection wasn't mentioned?

Here are some quotes from the docs. I don't see anything here that indicates Xecute is slower or faster than indirection. Maybe someone else on this thread has more detailed knowledge on the difference if any.

  • The execution time for code called within XECUTE can be slower than the execution time for the same code encountered in the body of a routine. This is because InterSystems IRIS compiles source code that is specified with the XECUTE command or that is contained in a referenced global variable each time it processes the XECUTE.
  • You should use indirection only in those cases where it offers a clear advantage. Indirection can have an impact on performance because InterSystems IRIS performs the required evaluation at runtime, rather than during the compile phase.
  • You can always duplicate the effect of indirection by other means, such as by using the XECUTE command.

For me, if I was faced with 2 possible solutions and was concerned about performance, I would simply test both solutions within a loop and time how long it takes to do n number of loops.

Something like this

Class Performance.Test [ Abstract ]
{ ClassMethod SolutionA() As %Status
{
 
    set x=9
    quit $$$OK
} ClassMethod SolutionB() As %Integer
{
 
    set x=9
    quit 1
} ClassMethod TestSolutions()
{
 
    write "SolutionA Start : "_$zdatetime($ztimestamp,3,1,3),!
    for i=1:1:90000000 {
        set ret=..SolutionA()
    }
    write "SolutionA End : "_$zdatetime($ztimestamp,3,1,3),!
    
    write "SolutionB Start : "_$zdatetime($ztimestamp,3,1,3),!
    for i=1:1:90000000 {
        set ret=..SolutionB()
    }
    write "SolutionB End : "_$zdatetime($ztimestamp,3,1,3),!
} }

I don't see any difference between SolutinA and SolutionB.

First, $$$OK is converted at COMPILE time into 1 and second, your test code  neither has an execute nor an indirection - or do I miss something? But you can try the below code for timing difference

ClassMethod XTime()
{
	f i=1:1:3 d ..exc()
	w !
	f i=1:1:3 d ..ind()
}

ClassMethod exc()
{
	s arg="s x=1234_(1/2)"
	while $zh["." {} s t=$zh f i=1:1:1E6 { x arg } w $zh-t,!
}

ClassMethod ind()
{
	s arg="x=1234_(1/2)"
	while $zh["." {} s t=$zh f i=1:1:1E6 { s @arg } w $zh-t,!
}