 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
Lexikos
Joined: 17 Oct 2006 Posts: 2739 Location: Australia, Qld
|
Posted: Fri Feb 01, 2008 12:02 am Post subject: |
|
|
| RaptoR wrote: | | ...and I get the same "corrupting problem" again (loc disappears after exiting from the function). | How is Set_set_locs implemented? If you are storing everything in variables, there is only one way for it to work:
- this and loc are passed ByRef to Set_set_locs.
- If this does not already have space in it for loc, it must be expanded. This involves copying the current contents to a temporary variable, calling VarSetCapacity, and copying the contents back to this.
- The contents of loc are copied into this.
| Quote: | I consider GlobalAlloc as the last way to try, because it has obvious disadvantage - need to free memory manually.
| First, variables are never deleted (making a distinction between variables and the contents of variables.) Second, if the initial memory allocated to a variable is less than 64 bytes, AutoHotkey uses a method which avoids the overhead of dynamic memory, but prevents the memory from being freed before the script exits.
With this method...
| Code: | Bug_new(x) ; constructor
{
global ; all vars except x and obj are global.
static obj := 0
obj++
VarSetCapacity(this%obj%, (MAXS + 1) * 4, 0)
NumPut(x, this%obj%)
return &this%obj%
} | ...the "object" is not freed until you explicitly free it:
| Code: | VarSetCapacity(this%obj%, 0)
| Additionally, the variable this%obj% is never deleted. Each variable consumes at least 21 + StrLen(name) bytes, plus the size of the initial memory (+1 for null-terminator) if less than 64 bytes. |
|
| Back to top |
|
 |
RaptoR
Joined: 27 Jun 2007 Posts: 22 Location: Earth, Cambrian period
|
Posted: Fri Feb 01, 2008 5:04 am Post subject: The AHK object system, ver 1.0 |
|
|
Aha, I finally have found some solution!
We need to use one basic function, Object_new(size) that allocates memory and returns integer numerically equal to the pointer to the memory. In this very first version I'm using global array to prevent object be disposed too early. Maybe in a future version I will be able to implement something like refcounter or even garbage collector
Classes are encoded as number of functions that accept "this" (except constructor) and have names like "Class_method". Each field is supported by two synthethic functions "Class$field" (getter) and "Class$$field" (setter). Because of overloading absence in AHK I have to add suffixes to resolve name conflicts (in the example below look at "add*" funtions).
The example is below (prototype java code in comments):
| Code: |
;; Log() function are here, it just outputs parameter in
#Include lib/log.ahk
;; ultimate allocating function
Object_new(size)
{
global
static objc := 0
objc++
VarSetCapacity(_this_%objc%, size, 0)
return &_this_%objc%
}
MAXS := 5
MAXL := 10
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; code below was in the "main" method
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;Set mc = new Set();
;;System.out.println(mc);
mc := Set_new()
Log(Set_toString(mc))
;;Pair p;
;;p = new Pair(); // random
;;mc.add(p.a, p.b);
;;System.out.println(mc);
p := Pair_new0()
Set_add2(mc, Pair$a(p), Pair$b(p))
Log(Set_toString(mc))
;;p = new Pair(); // random
;;mc.add(p.a, p.b, 1);
;;System.out.println(mc);
p := Pair_new0()
Set_add3(mc, Pair$a(p), Pair$b(p), 1)
Log(Set_toString(mc))
;;mc.combine(1);
;;System.out.println(mc);
Set_combine(mc, 1)
Log(Set_toString(mc))
;;mc.combine(2);
;;System.out.println(mc);
Set_combine(mc, 2)
Log(Set_toString(mc))
;;mc.combine(3);
;;System.out.println(mc);
Set_combine(mc, 3)
Log(Set_toString(mc))
;;mc.do_switch(1);
;;System.out.println(mc);
Set_doSwitch(mc, 1)
Log(Set_toString(mc))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; class Set
;; Loc [] locs = new Loc[Set.MAXS];
;; int size;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; constructor
Set_new()
{
global MAXS
this := Object_new((MAXS + 1) * 4)
loop % MAXS
{
loc := Loc_new(a_index * 10, a_index)
Set$$locs(this, a_index-1, loc)
}
Set$$size(this, MAXS) ; debug only
return this
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; synthethic methods for fields
Set$locs(this, index) ;; index = 0..(MAXS-1)
{
return NumGet(this+0, index * 4)
}
Set$size(this)
{
global MAXS
return NumGet(this+0, MAXS * 4)
}
Set$$locs(this, index, value) ;; index = 0..(MAXS-1)
{
NumPut(value, this+0, index * 4)
}
Set$$size(this, size)
{
global MAXS
NumPut(size, this+0, MAXS * 4)
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; public void add(int x, int y)
; {
; size = Math.min(size + 1, MAXL);
; shiftr(size - 1);
; locs[0] = new Loc(x, y);
; }
Set_add2(this, x, y)
{
global MAXS
size := Set$size(this)
Log("size"size)
Set$$size(this, (MAXS < size + 1 ? MAXS : size + 1))
Set_shiftr(this, size - 1)
loc := Loc_new(x, y)
Set$$locs(this, 0, loc)
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; public void add(int x, int y, int n)
; {
; add(x, y);
; locs[0].set(n);
; }
Set_add3(this, x, y, n)
{
;;add(x, y);
;;locs[0].set(n);
Set_add2(this, x, y)
loc := Set$locs(this, 0)
Loc_set(loc, n)
;;Set$$locs(this, 0, loc)
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; public void combine(int n)
; {
; if (size == 0)
; {
; Pair p = random();
; add(p.a, p.b, n);
; return;
; }
; for (int i = 0; i < size; i++)
; locs[i].unset(n);
; locs[0].set(n);
; }
Set_combine(this, n)
{
;; if size == 0, do nothing
;if (size == 0) return
;for (int i = 0; i < size; i++)
; locs[i].unset(n);
;locs[0].set(n);
size := Set$size(this)
if (size == 0)
return
loop % size
{
loc := Set$locs(this, a_index - 1)
Loc_unset(loc, n)
}
loc := Set$locs(this, 0)
Loc_set(loc, n)
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; public void do_switch(int index)
; {
; Loc l = locs[index];
; shiftr(index);
; locs[0] = l;
; }
Set_doSwitch(this, index) ; index = 0..(MAXS-1)
{
loc := Set$locs(this, index)
Set_shiftr(this, index)
Set$$locs(this, 0, loc)
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; public void shiftr(int k)
; {
; for (int i = k; i > 0; --i )
; locs[i] = locs[i - 1];
; }
Set_shiftr(this, k)
{
;for (int i = k; i > 0; --i )
; locs[i] = locs[i - 1];
loop % k
{
i := k + 1 - a_index
loc := Set$locs(this, i - 1)
Set$$locs(this, i, loc)
}
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; public String toString()
; {
; String str = "{[" + size + "]\n";
; for(int i = 0; i < size; i++)
; str += (locs[i].toString() + "\n");
; str += "}";
; return str;
; }
Set_toString(this)
{
size := Set$size(this)
str := "{[" . size . "]`n"
loop % size
{
i := a_index - 1
loc := Set$locs(this, i)
str .= Loc_toString(loc) . "`n"
}
str .= "}"
return str
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; class Loc
;; boolean [] groups = new boolean[Set.MAXL];
;; int x, y;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; public Loc(int x, int y)
; {
; this.x = x;
; this.y = y;
; }
Loc_new(x, y)
{
global MAXL
this := Object_new((MAXL + 2) * 4)
Loc$$x(this, x)
Loc$$y(this, y)
return this
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; synthethic methods for fields
Loc$groups(this, i)
{
return NumGet(this+0, i * 4)
}
Loc$x(this)
{
global MAXL
return NumGet(this+0, MAXL * 4)
}
Loc$y(this)
{
global MAXL
return NumGet(this+0, (MAXL + 1) * 4)
}
Loc$$groups(this, i, v)
{
NumPut(v, this+0, i * 4)
}
Loc$$x(this, v)
{
global MAXL
NumPut(v, this+0, MAXL * 4)
}
Loc$$y(this, v)
{
global MAXL
NumPut(v, this+0, (MAXL + 1) * 4)
}
;; end of synthethic methods
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; public void set(int n) { groups[n] = true; }
Loc_set(this, n)
{
Loc$$groups(this, n, 1)
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; public void unset(int n) { groups[n] = false; }
Loc_unset(this, n)
{
Loc$$groups(this, n, 0)
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; public boolean get(int n) { return groups[n]; }
Loc_get(this, n)
{
return Loc$groups(this, n)
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; public String toString()
; {
; String str = "{(" + x + ", " + y + ") ";
; for (int i = 0; i < Loc.MAXL; i++)
; str += i;
; str += "}";
; return str;
; }
Loc_toString(this)
{
global MAXL
x := Loc$x(this)
y := Loc$y(this)
str := "{" . x . "," . y . ","
loop % MAXL
{
i := a_index - 1
str .= Loc_get(this, i)
}
str .= "}"
return str
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; class Pair
;; int a, b;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
Pair_new0()
{
this := Object_new(8)
Random a, 100, 200
Random b, 100, 200
Log("a" a " b" b)
Pair$$a(this, a)
Pair$$b(this, b)
return this
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Pair(int a, int b)
; {
; this.a = a;
; this.b = b;
; }
Pair_new2(a, b)
{
this := Object_new(8)
Pair$$a(this, a)
Pair$$b(this, b)
return this
}
Pair$a(this)
{
return NumGet(this+0, 0)
}
Pair$b(this)
{
return NumGet(this+0, 4)
}
Pair$$a(this, a)
{
NumPut(a, this+0, 0)
}
Pair$$b(this, b)
{
NumPut(b, this+0, 4)
}
|
The code works like charm, but there is something to do yet:
1. vtable
2. exceptions
3. smarter memory management
Despite these issues, I hope you enjoyed it  |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 2739 Location: Australia, Qld
|
Posted: Fri Feb 01, 2008 9:17 am Post subject: Re: The AHK object system, ver 1.0 |
|
|
| RaptoR wrote: | Maybe in a future version I will be able to implement something like refcounter or even garbage collector
| As I pointed out already, variables can not be deleted.
I don't understand your aversion to GlobalAlloc. The problems I see with your current method are:
- _this_%..% can never be deleted.
- No "object" (the contents of _this_%..%) can ever be deleted, since you need to know the var's name, not just its address.
- No "object" is ever deleted before the script exits, so it is the equivalent of using GlobalAlloc but never explicitly freeing the memory. In other words, GlobalAlloc's "obvious disadvantage" as you put it, does not exist.
If you did implement a "refcounter or garbage collector", you would need to explicitly add and release references. GlobalFree would simply be called when the last reference is "released".
Consider using this:
| Code: | Object_new(size) {
return DllCall("GlobalAlloc","uint",0x40,"uint",size)
}
Object_delete(ptr) {
DllCall("GlobalFree","uint",ptr)
} |
|
|
| Back to top |
|
 |
RaptoR
Joined: 27 Jun 2007 Posts: 22 Location: Earth, Cambrian period
|
Posted: Mon Feb 04, 2008 10:38 am Post subject: Re: The AHK object system, ver 1.0 |
|
|
lexiKos, thank you for the replies, I appreciate your help!
| lexiKos wrote: |
I don't understand your aversion to GlobalAlloc. The problems I see with your current method are:
- _this_%..% can never be deleted.
- No "object" (the contents of _this_%..%) can ever be deleted, since you need to know the var's name, not just its address.
- No "object" is ever deleted before the script exits, so it is the equivalent of using GlobalAlloc but never explicitly freeing the memory. In other words, GlobalAlloc's "obvious disadvantage" as you put it, does not exist.
|
I thought that memory allocated with GlobalAlloc would not automatically freed when the application exited. But actually windows will free all resources (not memory only, but also GDI handles, etc.) when a process exits - even if the process fails to do so on its own. So, yes, in general, there is no difference between AHK global variables and memory given by GlobalAlloc.
| Quote: |
If you did implement a "refcounter or garbage collector", you would need to explicitly add and release references. GlobalFree would simply be called when the last reference is "released".
|
Yes, you are right. Now I'm thinking to move from inventing a bicycle towards using scripts with AHK (like Scheme, Javascript, Python etc). |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|