v2.1-alpha.9 (out now)
Rather than constructing a struct and then replacing the already-allocated pointer with ObjSetDataPtr (which will probably be removed),
StructFromPtr can be used to create a "typed pointer". At the moment it is more or less the same as a struct instance, but with __delete disabled.
I'm not really sure how to handle type identity. For the moment,
StructFromPtr(RECT, p) is RECT is true, by design. The pointer instance does not redirect get/set/call, but rather behaves as an instance, inheriting all defined properties from
RECT.Prototype, which is its actual base object. I think it's probably fine.
However, some structs contain pointers to other structs (but I can't think of any easy examples). I think that there should be a built-in way for the script to specify that a DllCall parameter or property/struct field is a pointer-to-something, so getting it would do the equivalent of calling
StructFromPtr(something, value). Setting it (or passing a value if used as a DllCall parameter type) would verify the type of struct being passed.
The following demonstrates some of the ideas I have in mind (with Struct hypothetically being built-in, but actually being defined below):
Code: Select all
#Requires AutoHotkey v2.1-alpha.9
class RECT extends Struct {
left: i32, top: i32, right: i32, bottom: i32
}
NumPut('int', 1, 'int', 2, 'int', 3, 'int', 4, buf := Buffer(16, 0))
r := RECT.at(buf.ptr)
MsgBox "RECT.size = " RECT.size
. "`nr.size = " r.size
. "`nStruct.Size(r) = " Struct.Size(r)
. "`nr = {" r.left "," r.top "," r.right "," r.bottom "}"
; r.foo := "bar" ; Error
; In lieu of an API that returns a struct pointer:
make_RECT := DllCall.Bind("msvcrt\malloc", "ptr", 16, RECT.Pointer)
free_RECT := DllCall.Bind("msvcrt\free", RECT.Pointer, )
GetWindowRect := DllCall.Bind("GetWindowRect", "ptr", , RECT.Pointer, )
r := make_RECT()
; GetWindowRect(WinExist("A"), buf := Buffer(16, 0)) ; Error (buf is not a RECT)
GetWindowRect(WinExist("A"), r)
MsgBox "r = {" r.left "," r.top "," r.right "," r.bottom "}"
free_RECT(r)
class Struct {
static __new() {
if this != Struct
throw
this.Prototype.DefineProp('Ptr', {get: ObjGetDataPtr})
this.Prototype.DefineProp('Size', {get: ObjGetDataSize})
this.DeleteProp('__new')
this.DefineProp('at', {call: StructFromPtr})
}
static Size => this.Prototype.Size
static Size(obj) => ObjGetDataSize(obj.Prototype ?? obj)
static Pointer => (this.DefineProp('Pointer', {value: c := PointerTo(this)}), c)
__Set(name, args, value) {
if args.Length
return this.%name%[args*]
throw PropertyError('This value of type "' type(this) '" has no property named "' name '".', -1)
}
}
PointerTo(structClass) {
static cache := Map()
if c := cache[structClass] ?? false
return c
if !ObjGetDataSize(structClass.Prototype)
throw TypeError("Invalid class", -1)
c := Class(Object)
c.Prototype.cls := structClass
c.Prototype.DefineProp('Ptr', {type: 'uptr'})
c.Prototype.DefineProp('__value', {
get: this => this.ptr && StructFromPtr(this.cls, this.ptr),
set: (this, value) {
if value is Integer
this.ptr := value
else if value is this.cls
this.ptr := value.ptr
else
throw TypeError("Expected a " this.cls.Prototype.__class " or Integer; received a " type(value), -1)
}})
cache[structClass] := c
return c
}
Typed pointers might be restricted to Struct-derived classes, with structs being restricted to typed or inherited properties only; no own properties. Otherwise, a typed pointer would provide a sort of limited view of an object, with methods and dynamic properties being unable to see or access own properties of the original object.
Once typed array support is added, that would be integrated into Struct; e.g.
RECT[n] would return a class object usable in typed property definitions.
I consider implementing
struct x and
struct x extends y as simple syntax sugar equivalent to
class x extends Struct or
class x extends y (with
y restricted to Struct subclasses).
Is anyone using typed properties in any interesting publicly-available scripts?
The feature set is certainly incomplete, but does anyone who has been putting these features to use have thoughts on what's needed in particular, or areas that are especially difficult?
@thqby no, that's been fixed.