AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Scripting.Dictionary Object as Associative Array
Goto page Previous  1, 2, 3, 4  Next
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
Sean



Joined: 12 Feb 2007
Posts: 1141

PostPosted: Fri Apr 27, 2007 5:06 pm    Post subject: Reply with quote

Chris wrote:
Sean, to the extent you agree with Laszlo's changes, maybe you'd consider applying them to the topmost post.

I was thinking about rewriting the Enumerate() function, so I may incorporate the NextKey() function.
Back to top
View user's profile Send private message
Sean



Joined: 12 Feb 2007
Posts: 1141

PostPosted: Fri Apr 27, 2007 5:22 pm    Post subject: Reply with quote

Laszlo wrote:
AllocString() does more than SysAllocString() in CoHelper.ahk (fill memory). The AllocString name was meant to avoid conflicts with the original. It does allocate a string now, so foom might accept this name for a different functionality.

Sorry, I didn't pay a careful attention.
So, you separated the function to create a variant whose element is BString.
Hmm, would you consider again about the name? IMHO, the name better contain the implication that it's owned by the system.
Back to top
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 3877
Location: Pittsburgh

PostPosted: Fri Apr 27, 2007 7:13 pm    Post subject: Reply with quote

Sean wrote:
the name better contain the implication that it's owned by the system.
How about SysAllocStringB()?
Back to top
View user's profile Send private message Visit poster's website
Sean



Joined: 12 Feb 2007
Posts: 1141

PostPosted: Fri Apr 27, 2007 10:58 pm    Post subject: Reply with quote

Laszlo wrote:
How about SysAllocStringB()?

I was thinking about AllocBString, as I'm not aware of the usage of BString outside COM, but I may be wrong. Personally, anything would be fine if it contains either Sys or B.
Back to top
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 3877
Location: Pittsburgh

PostPosted: Fri Apr 27, 2007 11:29 pm    Post subject: Reply with quote

Changed to AllocBString, for popular demand Smile
Back to top
View user's profile Send private message Visit poster's website
Sean



Joined: 12 Feb 2007
Posts: 1141

PostPosted: Sat Apr 28, 2007 1:00 am    Post subject: Reply with quote

Laszlo wrote:
Changed to AllocBString, for popular demand Smile

Laughing

Thanks for the change.
Back to top
View user's profile Send private message
toralf



Joined: 31 Jan 2005
Posts: 3842
Location: Bremen, Germany

PostPosted: Fri May 11, 2007 9:47 pm    Post subject: Reply with quote

I managed to go through Laszlos code. That version is much easier to diggest. It doesn't seem to be too complicated. The only thing I do not like is that I have to provide the dictonary handle to each function. Is there a different way to avoid this then making it global?
Of cause this handle helps to create different namespaces, but I could also name the vars as a combination of namespace and varname.

Another thing to use it as a module, would be if there is a simple init function, so that it is easier to start with. And if needed also an exit function.
_________________
Ciao
toralf
Back to top
View user's profile Send private message Send e-mail Visit poster's website
toralf



Joined: 31 Jan 2005
Posts: 3842
Location: Bremen, Germany

PostPosted: Fri May 11, 2007 9:52 pm    Post subject: Reply with quote

I was surprised to see the add() function of AHK be overloaded, untill I relalized that AHK doesn't have an add() function. It is just used as an example for functions in the manual. :)
But it would have been a cool feature. >)
_________________
Ciao
toralf
Back to top
View user's profile Send private message Send e-mail Visit poster's website
Laszlo



Joined: 14 Feb 2005
Posts: 3877
Location: Pittsburgh

PostPosted: Fri May 11, 2007 10:07 pm    Post subject: Reply with quote

toralf wrote:
I have to provide the dictionary handle to each function. Is there a different way to avoid this then making it global?
Of cause this handle helps to create different namespaces, but I could also name the vars as a combination of namespace and varname.
If you only use one dictionary, you can make its handle global. You can also pre-process your script, to insert the name of the appropriate handle to the dictionary functions, but until AHK will have true block structure, you have to do this manually.
toralf wrote:
a simple init function, so that it is easier to start with. And if needed also an exit function.
In the beginning you only need
Code:
DllCall("ole32\CoInitialize", UInt,0)
and for each dictionary
Code:
pdic := CreateDictionary()
I cannot imagine a simpler initialization. Maybe, the function CreateDictionary could call CoInitialize at the first invocation, so you don't need even that single line. Similarly
Code:
DestroyDictionary(pdic)
DllCall("ole32\CoUninitialize")
are the only things you need at exit.
Back to top
View user's profile Send private message Visit poster's website
toralf



Joined: 31 Jan 2005
Posts: 3842
Location: Bremen, Germany

PostPosted: Fri May 11, 2007 10:14 pm    Post subject: Reply with quote

Will the dictonary destroy itself when the script exists, or is it mandatory to call the two commands?
_________________
Ciao
toralf
Back to top
View user's profile Send private message Send e-mail Visit poster's website
Laszlo



Joined: 14 Feb 2005
Posts: 3877
Location: Pittsburgh

PostPosted: Fri May 11, 2007 10:49 pm    Post subject: Reply with quote

I think it leaks, but try it in a loop, to see if the free memory is reduced
Back to top
View user's profile Send private message Visit poster's website
toralf



Joined: 31 Jan 2005
Posts: 3842
Location: Bremen, Germany

PostPosted: Fri May 11, 2007 10:59 pm    Post subject: Reply with quote

Maybe like this (as a module):
Code:
Dic()

Dic_Add("Test1", "This is a Test1")
Dic_Add("Test2", "This is a Test2")
Dic_Add("Test1", "This is a Test3")       ; no effect, already exists
Dic_Add("Test3", "This is a Test9")
Dic_Add("Test0", "")

Dic_Set("Result1", "This is a Result1")   ; create and set
Dic_Set("Result2", "This is a Result2")
Dic_Set("Result1", "This is a Result3")   ; overwrite

Dic_Rename("Result1", "Result3")
Dic_Remove("Test3")

MsgBox, % "Hash of 'Test9': " . Dic_HashVal("Test9")
MsgBox, % "Count: " . Dic_Count()

MsgBox % "Test1:"  . Dic_Get("Test1")
MsgBox % "test2:"  . Dic_Get("test2")     ; empty

Loop {
  k := Dic_NextKey(penum)
  IfEqual penum,, Break                    ; k (Itm) can be empty
  MsgBox % "[1/"  . A_Index . "]" . k . ":" . Dic_Get(k)
}
Loop % Dic_Count() {
  k := Dic_NextKey(penum)
  MsgBox % "[2a/" . A_Index . "]" . k . ":" . Dic_Get(k)
  k := Dic_NextKey(penu1)
  MsgBox % "[2b/" . A_Index . "]" . k . ":" . Dic_Get(k)
}

Dic("Exit")
Return


;###############################################################################
;###  Scripting.Dictionary COM object Modul                                  ###
;###############################################################################
Dic(Action=""){
    static initialized, pdic
    If (Action = "pdic")               ;return pdic
        Return pdic
    Else If Action {                   ;exit - clean up
        Dic_DestroyDictionary(pdic)
        DllCall("ole32\CoUninitialize")
    }Else{                             ;create a dictionary
        If !initialized {
            initialized := True
            DllCall("ole32\CoInitialize", UInt,0)
          }
        pdic := Dic_CreateDictionary()
      }
    Return 1
  }

Dic_Add(sKey, sItm) {  ; If key exists: no effect (<--> Set)
   pdic := Dic("pdic")
   Dic_AllocBString(pKey, var1, sKey)
   Dic_AllocBString(pItm, var2, sItm)
   DllCall(Dic_VTable(pdic,10), UInt,pdic, UInt,&var1, UInt,&var2)
   DllCall("oleaut32\SysFreeString", UInt,pKey)
   DllCall("oleaut32\SysFreeString", UInt,pItm)
}

Dic_Set(sKey, sItm) {  ; If key exists, update the item, Else create a new entry
   pdic := Dic("pdic")
   Dic_AllocBString(pKey, var1, sKey)
   Dic_AllocBString(pItm, var2, sItm)
   DllCall(Dic_VTable(pdic,8), UInt,pdic, UInt,&var1, UInt,&var2)  ; 8 (Set0 -> 7)
   DllCall("oleaut32\SysFreeString", UInt,pKey)
   DllCall("oleaut32\SysFreeString", UInt,pItm)
}

Dic_Get(sKey) {  ; empty if not exists
   pdic := Dic("pdic")
   Dic_AllocBString(pKey, var1, sKey)
   DllCall(Dic_VTable(pdic,12), UInt,pdic, UInt,&var1, IntP,bExist)
   If bExist {     ; to avoid creating an unwanted new entry
      VarSetCapacity(var2, 16, 0)
      DllCall(Dic_VTable(pdic,9), UInt,pdic, UInt,&var1, UInt,&var2)
      pItm := *(&var2+8) | *(&var2+9) << 8 | *(&var2+10) << 16 | *(&var2+11) << 24
      Dic_Unicode2Ansi(pItm, sItm)
      DllCall("oleaut32\SysFreeString", UInt,pItm)
   }
   DllCall("oleaut32\SysFreeString", UInt,pKey)
   Return sItm
}

Dic_NextKey(ByRef penum) {                         ; penum = "": create new list
   pdic := Dic("pdic")
   If penum =                                          ; not static: allow multiple independent lists
      DllCall(Dic_VTable(pdic,20), UInt,pdic, UIntP,penum) ; create key-list in penum
   VarSetCapacity(var, 16, 0)
   If DllCall(Dic_VTable(penum,3), UInt,penum, UInt,1, UInt,&var, UInt,0) {
      DllCall(Dic_VTable(penum,2), UInt,penum)             ; END: destroy key-list
      penum =                                          ; signal end of list
      Return                                           ; empty
   }
   pKey := Dic_UInt@(&var + 8)
   Dic_Unicode2Ansi(pKey, sKey)
   DllCall("oleaut32\SysFreeString", UInt,pKey)
   Return skey
}

Dic_Count() {      ; #entries
   pdic := Dic("pdic")
   DllCall(Dic_VTable(pdic,11), UInt,pdic, IntP,nCount)
   Return nCount
}

Dic_Exists(sKey) {
   pdic := Dic("pdic")
   Dic_AllocBString(pKey, var, sKey)
   DllCall(Dic_VTable(pdic,12), UInt,pdic, UInt,&var, IntP,bExist)
   DllCall("oleaut32\SysFreeString", UInt,pKey)
   Return bExist
}

Dic_Rename(sKeyFr, sKeyTo) {
   pdic := Dic("pdic")
   Dic_AllocBString(pKeyFr, var1, sKeyFr)
   Dic_AllocBString(pKeyTo, var2, sKeyTo)
   DllCall(Dic_VTable(pdic,14), UInt,pdic, UInt,&var1, UInt,&var2)
   DllCall("oleaut32\SysFreeString", UInt,pKeyFr)
   DllCall("oleaut32\SysFreeString", UInt,pKeyTo)
}

Dic_Remove(sKey) {
   pdic := Dic("pdic")
   Dic_AllocBString(pKey, var, sKey)
   DllCall(Dic_VTable(pdic,16), UInt,pdic, UInt,&var)
   DllCall("oleaut32\SysFreeString", UInt,pKey)
}

Dic_RemoveAll() {
   pdic := Dic("pdic")
   Return DllCall(Dic_VTable(pdic,17), UInt,pdic)
}

Dic_SetCompareMode(nCompMode = 1) { ; 0: Binary, 1: Text, 2: Database, n: LCID
   pdic := Dic("pdic")
   Modes = btd
   Loop, Parse, Modes
      If RegExMatch(nCompMode, "i)^" A_LoopField)
         nCompMode := A_Index - 1
   DllCall(Dic_VTable(pdic,18), UInt,pdic, Int,nCompMode)
}

Dic_GetCompareMode() {
   pdic := Dic("pdic")
   DllCall(Dic_VTable(pdic,19), UInt,pdic, IntP,nCompMode)
   Return nCompMode
}

Dic_HashVal(sKey) {
   pdic := Dic("pdic")
   Dic_AllocBString(pKey, var1, sKey)
   DllCall(Dic_VTable(pdic,12), UInt,pdic, UInt,&var1, IntP,bExist)
   VarSetCapacity(var2, 16, 0)
   DllCall(Dic_VTable(pdic,21), UInt,pdic, UInt,&var1, UInt,&var2)
   nHashVal := *(&var2+8) | *(&var2+9) << 8 | *(&var2+10) << 16 | *(&var2+11) << 24
   DllCall("oleaut32\SysFreeString", UInt,pKey)
   Return nHashVal
}

;#########  PRIVATE FUNCTIONS   ################################################

Dic_CreateDictionary() {
    CLSID_Dictionary := "{EE09B103-97E0-11CF-978F-00A02463E06F}"
    IID_IDictionary  := "{42C642C1-97E1-11CF-978F-00A02463E06F}"
    Return Dic_CreateObject(CLSID_Dictionary, IID_IDictionary)
  }

Dic_DestroyDictionary(pdic) {
    DllCall(Dic_UInt@(Dic_UInt@(pdic)+8), UInt,pdic)
  }

Dic_AllocBString(ByRef Key, ByRef Var, sString) {
   Dic_Ansi2Unicode(sString, wString)
   Key := DllCall("oleaut32\SysAllocString", Str,wString)
   VarSetCapacity(Var, 16, 0)
   DllCall("ntdll\RtlFillMemoryUlong", UInt,&Var,  UInt,4, UInt,8)
   DllCall("ntdll\RtlFillMemoryUlong", UInt,&Var+8,UInt,4, UInt,Key)
}

Dic_UInt@(ptr) {
   Return *ptr | *(ptr+1) << 8 | *(ptr+2) << 16 | *(ptr+3) << 24
}

Dic_CreateObject(ByRef CLSID, ByRef IID, CLSCTX = 5) {
   If StrLen(CLSID) = 38
      Dic_GUID4String(CLSID, CLSID)
   If StrLen(IID) = 38
      Dic_GUID4String(IID, IID)
   DllCall("ole32\CoCreateInstance", Str,CLSID, UInt,0, UInt,CLSCTX, Str,IID, UIntP,ppv)
   Return ppv
}
Dic_GUID4String(Byref CLSID, sString) {
   VarSetCapacity(CLSID, 16)
   Dic_Ansi2Unicode(sString, wString, 38)
   DllCall("ole32\CLSIDFromString", Str,wString, Str,CLSID)
}
Dic_Ansi2Unicode(ByRef sString, ByRef wString, nLen = 0) {
   If !nLen
      nLen := DllCall("MultiByteToWideChar", UInt,0, UInt,0, UInt,&sString, Int,-1, UInt,0, Int,0)
   VarSetCapacity(wString, nLen*2 + 1)
   DllCall("MultiByteToWideChar", UInt,0, UInt,0, UInt,&sString, Int,-1, UInt,&wString, Int,nLen)
}
Dic_VTable(ppv, idx) {
   Return Dic_UInt@(Dic_UInt@(ppv) + idx*4)
}
Dic_Unicode2Ansi(ByRef wString, ByRef sString, nLen = 0) {
   pString := wString + 0 > 65535 ? wString : &wString
   If !nLen
      nLen := DllCall("WideCharToMultiByte", UInt,0, UInt,0, UInt,pString, Int,-1, UInt,0, Int,0, UInt,0, UInt,0)
   VarSetCapacity(sString, nLen)
   DllCall("WideCharToMultiByte", UInt,0, UInt,0, UInt,pString, Int,-1, Str,sString, Int,nLen, UInt,0, UInt,0)
}
I hope I didn't screw up the code. I do not know if it is ok that i removed the optional pdic parameter from NextKey() function. What was the purpose to have it optional anyway? Could it be renamed to "Next"? Acctually I do not 100% understand what NextKey does. Could you please explain it?
_________________
Ciao
toralf
Back to top
View user's profile Send private message Send e-mail Visit poster's website
toralf



Joined: 31 Jan 2005
Posts: 3842
Location: Bremen, Germany

PostPosted: Fri May 11, 2007 11:19 pm    Post subject: Reply with quote

And this would be the very slim version (only 90 lines of code for the module with only Set and Get functionality)
Code:
Dic()

Dic_Set("Result1", "This is a Result1")   ; create and set
Dic_Set("Result2", "This is a Result2")
Dic_Set("Result1", "This is a Result3")   ; overwrite

MsgBox % "Result2:"  . Dic_Get("Result2")
     . "`ntest2:"    . Dic_Get("test2")     ; empty
     . "`nResult1:"  . Dic_Get("Result1")     ; empty

Dic("Exit")
Return


;###############################################################################
;###  Scripting.Dictionary COM object Modul                                  ###
;###############################################################################
Dic(Action=""){
    static initialized, pdic
    If (Action = "pdic")               ;return pdic
        Return pdic
    Else If Action {                   ;exit - clean up
        Dic_DestroyDictionary(pdic)
        DllCall("ole32\CoUninitialize")
    }Else{                             ;create a dictionary
        If !initialized {
            initialized := True
            DllCall("ole32\CoInitialize", UInt,0)
          }
        pdic := Dic_CreateDictionary()
      }
    Return 1
  }

Dic_Set(sKey, sItm) {  ; If key exists, update the item, Else create a new entry
   pdic := Dic("pdic")
   Dic_AllocBString(pKey, var1, sKey)
   Dic_AllocBString(pItm, var2, sItm)
   DllCall(Dic_VTable(pdic,8), UInt,pdic, UInt,&var1, UInt,&var2)  ; 8 (Set0 -> 7)
   DllCall("oleaut32\SysFreeString", UInt,pKey)
   DllCall("oleaut32\SysFreeString", UInt,pItm)
}

Dic_Get(sKey) {  ; empty if not exists
   pdic := Dic("pdic")
   Dic_AllocBString(pKey, var1, sKey)
   DllCall(Dic_VTable(pdic,12), UInt,pdic, UInt,&var1, IntP,bExist)
   If bExist {     ; to avoid creating an unwanted new entry
      VarSetCapacity(var2, 16, 0)
      DllCall(Dic_VTable(pdic,9), UInt,pdic, UInt,&var1, UInt,&var2)
      pItm := *(&var2+8) | *(&var2+9) << 8 | *(&var2+10) << 16 | *(&var2+11) << 24
      Dic_Unicode2Ansi(pItm, sItm)
      DllCall("oleaut32\SysFreeString", UInt,pItm)
   }
   DllCall("oleaut32\SysFreeString", UInt,pKey)
   Return sItm
}
;#########  PRIVATE FUNCTIONS   ################################################

Dic_CreateDictionary() {
    CLSID_Dictionary := "{EE09B103-97E0-11CF-978F-00A02463E06F}"
    IID_IDictionary  := "{42C642C1-97E1-11CF-978F-00A02463E06F}"
    Return Dic_CreateObject(CLSID_Dictionary, IID_IDictionary)
  }

Dic_DestroyDictionary(pdic) {
    DllCall(Dic_UInt@(Dic_UInt@(pdic)+8), UInt,pdic)
  }

Dic_AllocBString(ByRef Key, ByRef Var, sString) {
   Dic_Ansi2Unicode(sString, wString)
   Key := DllCall("oleaut32\SysAllocString", Str,wString)
   VarSetCapacity(Var, 16, 0)
   DllCall("ntdll\RtlFillMemoryUlong", UInt,&Var,  UInt,4, UInt,8)
   DllCall("ntdll\RtlFillMemoryUlong", UInt,&Var+8,UInt,4, UInt,Key)
}

Dic_UInt@(ptr) {
   Return *ptr | *(ptr+1) << 8 | *(ptr+2) << 16 | *(ptr+3) << 24
}

Dic_CreateObject(ByRef CLSID, ByRef IID, CLSCTX = 5) {
   If StrLen(CLSID) = 38
      Dic_GUID4String(CLSID, CLSID)
   If StrLen(IID) = 38
      Dic_GUID4String(IID, IID)
   DllCall("ole32\CoCreateInstance", Str,CLSID, UInt,0, UInt,CLSCTX, Str,IID, UIntP,ppv)
   Return ppv
}
Dic_GUID4String(Byref CLSID, sString) {
   VarSetCapacity(CLSID, 16)
   Dic_Ansi2Unicode(sString, wString, 38)
   DllCall("ole32\CLSIDFromString", Str,wString, Str,CLSID)
}
Dic_Ansi2Unicode(ByRef sString, ByRef wString, nLen = 0) {
   If !nLen
      nLen := DllCall("MultiByteToWideChar", UInt,0, UInt,0, UInt,&sString, Int,-1, UInt,0, Int,0)
   VarSetCapacity(wString, nLen*2 + 1)
   DllCall("MultiByteToWideChar", UInt,0, UInt,0, UInt,&sString, Int,-1, UInt,&wString, Int,nLen)
}
Dic_VTable(ppv, idx) {
   Return Dic_UInt@(Dic_UInt@(ppv) + idx*4)
}
Dic_Unicode2Ansi(ByRef wString, ByRef sString, nLen = 0) {
   pString := wString + 0 > 65535 ? wString : &wString
   If !nLen
      nLen := DllCall("WideCharToMultiByte", UInt,0, UInt,0, UInt,pString, Int,-1, UInt,0, Int,0, UInt,0, UInt,0)
   VarSetCapacity(sString, nLen)
   DllCall("WideCharToMultiByte", UInt,0, UInt,0, UInt,pString, Int,-1, Str,sString, Int,nLen, UInt,0, UInt,0)
}

_________________
Ciao
toralf
Back to top
View user's profile Send private message Send e-mail Visit poster's website
Laszlo



Joined: 14 Feb 2005
Posts: 3877
Location: Pittsburgh

PostPosted: Fri May 11, 2007 11:36 pm    Post subject: Reply with quote

They should work with a single dictionary, but slower than a global handle. If you use informative dictionary names, the original would be much more flexible. After Global := CreateDictionary(), Module2 := CreateDictionary() you can use Add(Global,"x",1), Add(Global,"y",0), Add(Module2,"x", 2)…
Back to top
View user's profile Send private message Visit poster's website
toralf



Joined: 31 Jan 2005
Posts: 3842
Location: Bremen, Germany

PostPosted: Sat May 12, 2007 8:50 am    Post subject: Reply with quote

Yes it might be a bit slower. I did it to simplify the other functions. Since I plan only to use one dictionary. I could still mimic your example with Add("Global.x",1), Add("Global.y",0), Add("Module2.x", 2)…

I still do not understand why NextKey() in your code has an optional pdic parameter. What is the purpose you made it optional?
_________________
Ciao
toralf
Back to top
View user's profile Send private message Send e-mail Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page Previous  1, 2, 3, 4  Next
Page 2 of 4

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group