Jump to content


Photo

Array[index] doesn't work in label inside a function


  • Please log in to reply
7 replies to this topic

#1 Peter

Peter
  • Members
  • 448 posts

Posted 26 July 2006 - 10:10 PM

I use kind of array, like this: New_Variable:= %Index%String. It works, except when I make it in a label that's inside a function.
Is this normal behaviour, or am I doing something wrong?
TestCode:
NewVar()
   F9::ExitApp
Return
NewVar()
{
   Static TestVar,Index
   Index= Test
   TestVar= {esc}

   VarToMake:= %Index%Var  ; -> content of VarToMake is {esc}
   MsgBox, 0, TestVar= %TestVar%, 1: VarToMake= %VarToMake%
   SetTimer, TimerLabel, 1000
   Return
TimerLabel:
   SetTimer, TimerLabel, off
   VarToMake:= %Index%Var  ; identical expression, but different result (empty)
   MsgBox, 0, TestVar= %TestVar%, 2: VarToMake= %VarToMake%
Return
}
[edit] small correction in ; comment

#2 d-man

d-man
  • Members
  • 285 posts

Posted 27 July 2006 - 12:32 AM

you’ve got to declare the variable global instead of static, eg
NewVar() 
   F9::ExitApp 
Return 
NewVar() 
{ 
   global
   Index= Test 
   TestVar= {esc} 
   VarToMake:= %Index%Var  ; -> content of VarToMakeN is {esc} 
   MsgBox, 0, TestVar= %TestVar%, 1: VarToMake= %VarToMake% 
   SetTimer, TimerLabel, 1000 
return
TimerLabel: 
   SetTimer, TimerLabel, off 
   VarToMake:= %Index%Var  ; identical expression, but different result (empty) 
   MsgBox, 0, TestVar= %TestVar%, 2: VarToMake= %VarToMake% 
Return 
}


#3 Peter

Peter
  • Members
  • 448 posts

Posted 27 July 2006 - 11:18 AM

Yes right, that's one solution but I want to keep it local. The variable TestVar exists local, but under the label, I can't access it with %Index%Var.
This is maybe just a limitation within AHK :? (worth to mention in AHK Help?).
Anyway thanks for your solution :) .

#4 PhiLho

PhiLho
  • Fellows
  • 6850 posts

Posted 27 July 2006 - 12:10 PM

That's because when you exit of the function (Return), the Static or Local variables go out of scope, thus are empty when the timer is fired.
Actually, the labels inside functions are strange things, because they are global: you can't have two labels of same name in different functions. So if you put your label outside of the function, you will get the same result.

Since you declare your variables as static, I suppose you could, as d-man suggered, change them to global variables, with some unique name (I usually prefix with function name).

Mmm, here is another workaround, a bit more elegant, but not the nicest...
NewVar()

Esc::ExitApp

NewVar(bTimerCall=false)
{
	local VarToMake ; Moved above static, to avoid a strange error message
	static TestVar, Index

	If (!bTimerCall)
	{
		Index= Test
		TestVar= {esc}

		VarToMake:= %Index%Var  ; -> content of VarToMake is {esc}
		MsgBox 1 - TestVar= %TestVar%`nVarToMake= %VarToMake%
		SetTimer, TimerLabel, 1000
	}
	Else
	{
		SetTimer, TimerLabel, off
		VarToMake:= %Index%Var
		MsgBox 2 - TestVar= %TestVar%`nVarToMake= %VarToMake%
	}
	Return
}

TimerLabel:
	NewVar(true)
Return
[EDIT] Corrected the problem of local after static.

#5 Peter

Peter
  • Members
  • 448 posts

Posted 27 July 2006 - 01:11 PM

All respect for great PhiLho, but I think you're wrong this time :) .

when you exit of the function (Return), the Static or Local variables go out of scope, thus are empty when the timer is fired

Static variables are not empty in the Timer label (can check with my Testcode in first post). I use this many times to avoid global variables. (I use something similar -and unreliable?- here.)
My browser got stuck so I had to restart my reply. I see you added to your post, but with an error :) , sorry. local VarToMake gives an error, because all variables in a function are local. About labels in functions:

A function may contain subroutines for timers, GUI g-labels, menu items, and similar features. This is generally done to encapsulate them in a separate file for use with #Include, which prevents them from interfering with the script's auto-execute section. However, such subroutines should use only static and global variables (not locals) if their function is ever called normally rather than simply serving as a container for subroutines. This is because a subroutine thread that interrupts a function-call thread (or vice versa) would be able to change the values of local variables seen by the interrupted thread. Furthermore, any time a function returns to its caller, all of its local variables are made blank to free their memory.

Actually, the labels inside functions are strange things, because they are global: you can't have two labels of same name in different functions.

You're right, I also use for labels some prefix derived from the function name.

#6 PhiLho

PhiLho
  • Fellows
  • 6850 posts

Posted 27 July 2006 - 01:57 PM

All respect for great PhiLho, but I think you're wrong this time :) .

Hey, great men make errors sometime, even more when doing stuff in haste. ;-)

Static variables are not empty in the Timer label (can check with my Testcode in first post). I use this many times to avoid global variables.

Ouch, somehow I missed this info, my message boxes are small with my theme, so the title is truncated.
And indeed, since variables are static, there is no reason why they should go out.

I see you added to your post, but with an error :) , sorry. local VarToMake gives an error, because all variables in a function are local.

I added this as an afterthought, because I take the habit to always declare local my local variables, even if it is not necessary. Thus, I don't have to think twice to be sure to reach global variables.
I don't understand this message:

---------------------------
__Test.ahk
---------------------------
Error at line 50.

Line Text: local VarToMake
Error: Local variables do not need to be declared in this function.

The program will exit.
---------------------------
OK
---------------------------

I re-read the Functions page of the the manual and I don't see such restriction. It shows that only because the other variables are static, ie. if I declare them local, it is OK...
Aha, it works if I declare the local variable first! It is nearly a bug, at least, an obscure feature...

Within a function, any dynamic variable reference such as Array%i% will always resolve to a local variable [...]If neither exists and the usage requires the variable to be created, it will be created as a local variable.

No simple mean to create it as static variable.
It can be a bug, where AutoHotkey look in the lists of local and global variables, but not in the list of static variables?
Uhu, the following code doesn't work either, so either I am missing something, or you really have put the finger on a bug.
NewVar()
F9::ExitApp

NewVar()
{
	local VarToMake, VarTest
	static TestVar, Index

	If TestVar =	; First init of static vars
	{
		Index= Test
		TestVar= {esc}
	}

	VarToMake:= %Index%Var  ; -> content of VarToMake is {esc}
	MsgBox TestVar= %TestVar%`nIndex=%Index%`n1: VarToMake= %VarToMake%
	SetTimer, TimerLabel, 1000
	Return

TimerLabel:
	SetTimer, TimerLabel, off
	VarTest := TestVar
	VarToMake := Var%Index%
	MsgBox TestVar= %TestVar%`nVarTest= %VarTest%`nIndex=%Index%`n2: VarToMake= %VarToMake%
Return
}


#7 Peter

Peter
  • Members
  • 448 posts

Posted 27 July 2006 - 03:43 PM

Within a function, any dynamic variable reference such as Array%i% will always resolve to a local variable [...]If neither exists and the usage requires the variable to be created, it will be created as a local variable.

No simple mean to create it as static variable.
It can be a bug, where AutoHotkey look in the lists of local and global variables, but not in the list of static variables?

I checked furthermore, it's only when jumping to the label that the empty VarToMake variable occurs, not when coming back to the function in a normal way.
And what AHK's manual means with Array%i% will always resolve to a local variable, is probably like, it resolves to a only local accessible variable, either local or static.
Anyway, if no solution here, I maybe go for a bug report.

#8 PhiLho

PhiLho
  • Fellows
  • 6850 posts

Posted 27 July 2006 - 03:52 PM

I checked furthermore, it's only when jumping to the label that the empty VarToMake variable occurs, not when coming back to the function in a normal way.

Yes, that's what I show with my workaround...
You are right, in my workaround, the array syntax accesses to the static variable without problem.