The topic has been discussed many times, but still there is a question:
For example, I have a list of function names stored in a simple global array funcList
:put $funcList
fName0
function1
mother
Let’s also assume that functions with these names exist (are defined) in the environment and contain executable code.
If the functions are working and have no parameters, I can execute them from a script, like this:
:global funcList;
:foreach i in=$funcList do={[:parse ":global $i; [\$$i]"]; :log info $i}
The question is: Can I declare functions from the funcList array without executing them immediately, so that they become known to the script and can be called later from this script by name, already without declaration, for example:
[$fName]
[$mother]
???
I tried:
:global funcList;
:foreach i in=$funcList do={[:parse ":global $i"]; :log info $i}
[$mother]
But that doesn’t work, probably because function declarations are made from local scope inside the loop and remain unknown to the script, while for example:
:global funcList;
:global mother
:foreach i in=$funcList do={[:parse ":global $i"]; :log info $i}
[$mother]
of course it works.
Is there any way to get around this problem or is it not possible ?
RouterOS script is not a complete programming language, is only a scripting language, and can not be done all like programming language.
I do not know if on other scripting engine you can define later the objects, but with RouterOS is not possible.
For my point of view, I hate library.
For example if on future version something change, if the library fail to load, all stop working.
Instead, if each script is independent, at least are blocked only the scripts that have the problem.
Also not list the function used on top, for me is wrong.
FWIW, I suspect your code doesn't work since the parser isn't loading globals into the stack since it doesn't know about them at "compile time". They way I think about it is each scope gets compiled as a whole e.g. a function, single CLI command, { local-block }, etc. So the ":global x", in the script's text, is how the compiler knows what globals to load into the function's stack. When it goes to run the compiled script, it then does the parse, but by then it's already decided the variable is undeclared. And in the RSC's scheme undeclared variable are treat as just empty string, not error. Thus your issue.
If the desire is to reduce the number of :global's you need to add for external functions, then could keep functions in the array itself. Only one variable there. Just note that $0 is the first argument with function in array: Positional Arguments in Array Function - $0 vs $1?
But array functions get unwieldy with the ($arrfuns->"myfunction") stuff for every call, and $0 vs $1 get confusing quick.
rextended is right, you better off keep things as separate function, if one needs to call another – just declare it explicitly in the other function
Thank you, I know everything you mentioned.
I wanted to declare functions whose names are contained in an array without calling them, and call them later in the same script.
But, since the cycle is the same local area, even global variables declared outside it are not known and they must be explicitly declared again
:global funcList;
:foreach i in=$funcList do={[:parse “:global $i”]}
…
[$myFunc1 par1 par2 par3=“hello”] - not work
:global myFunc1
[$myFunc1 par1 par2 par3=“hello”] - is work
Still, it’s a pity that the Router OS scripting language does not have the ability to declare a variable from a variable as:
:global $Var1
Then it would be easy to compose variable names programmatically. For example:
:global (“$Var1”.“$[/system identity get name]”.“$Var2”)
And, if the variable were an array, then the :global $Array command could declare all variables with names and values. Or if this is a key array, then the keys become names, and the values become the values of the declared global variables. But the developers of Mikrotik do not want this.
They didn’t even implement the first point.
Of course, you can do without all this and use what you have. For scenarios, this is enough.
One WAG would be some recursive closure. e.g. try your :parse trick on array on first call, and then call yourself again a 2nd time, perhaps some way to get them to load?
Dunno if would work, but recursive functions have side-effects. Imagine it gets a new stack on 2nd call, and then those :parse’d globals might be loaded?