Could op.DefineProp 'p', {type: 'uptr'} be changed to op.DefineProp '__p', {type: 'uptr'}. .p is the built-in property name, but it might break the existing codes. At least for me, I sometimes expand a Classname by using Classname1.p currently. If I had already kept it in mind that p would become a built-in property name in the future, I dare not use p again. Property p really might have already been defined by user for his own purpose. Any way, using __p as a built-in property name seems much more reasonable than p.GetCursorPos(&mp1) ; Let the call construct a new struct.
MsgBox WinGetClass(WindowFromPoint(mp1))
GetCursorPos(mp2 := Point()) ; Output into an existing struct.
MsgBox WinGetClass(WindowFromPoint(mp2))
Out(sc) {
oc := Class()
oc.Prototype := op := {}
op.DefineProp 'p', {type: 'uptr'}
op.DefineProp '__value', {set: setvalue}
Typed properties - experimental build available
Re: Typed properties - experimental build available
Re: Typed properties - experimental build available
__value must be a property, not a method.
"VarRef doesn't have __value property" is not a valid reason. I think that you didn't think it through.
"VarRef doesn't have __value property" is not a valid reason. I think that you didn't think it through.
Re: Typed properties - experimental build available
I didn't notice this post before as it was on a new page.
I don't know what you were imagining, but 'p' is clearly just a property I am defining in the example, on a user-defined object that is separate to any other objects that you might create in the script.
Re: Typed properties - experimental build available
I am still interested to hear the answer to this:
It wasn't rhetorical. Am I overlooking something?
You wrote about added complexity distracting from the purpose, and that simpler is better, but I have to think you had some specific issue in mind, as the generalisation doesn't make much sense. To me it seems that less flexibility in what the new features can do means more that the script will have to do for itself. The implementation will be simpler, and the scripts more complex. What I aimed for, and I think mostly achieved, is flexibility and simplicity. A few small additions mesh together with what objects can already do to allow the creation of classes that can take most of the fuss out of dealing with external functions and data types.
It is my intention to implement built-in classes (or functions that generate classes) for typed arrays, strings, pointers to structs and output parameters, but it's already clear that scripts can implement such things and any other abstract types, so I feel this experiment is a success. I can't see how a different path could have been simpler, even putting aside some of my goals.
You should not need to define a size property as there should be no reason to get the struct's size. I don't think there's a good reason to allow implicitly passing a struct to NumGet/NumPut/StrGet/StrPut.my concern is more related to if we have to define size and ptr props for each struct, or write custom enums if want to traverse a class/struct's typed props only, or similar.
You do not need to define a ptr property for DllCall (anymore), but depending on the type, you may want to define one so that the "ptr" arg type passes some value other than the struct's address. BSTR, for example, can be passed to a "Ptr" parameter or StrGet, and the value of the struct (the BSTR pointer) will be used, not the struct's address.
I'm not sure why "traversing a class/struct's typed props only" would be a consideration. For general objects, there is only a single enumerator of own properties. This is utter simplicity, and creates complexity in scripts at the same time. If enumerating typed properties is a commonly-needed task, facilitating that is just a matter of adding an enumerator for that purpose, regardless of how everything else is implemented. If I had taken a different path where Struct is a new type derived from Any, not sharing implementation with Object, I suppose that the enumerator would have been specific to structs. Instead, there are some possibilities that can solve the more general problem of enumerating an object's (inherited and own) properties.
I was making an oversight when I wrote this:
After some more thinking, I concluded that it doesn't make sense for x : Map (for instance) to declare a nullable reference to a Map while x : POINT declares a nested struct, x : i32 declares an integer, and x : BSTR declares a BSTR (see example). The type expression should specify - or the type class should define - the type of the property, and to do that, it has to be instantiated.
Maybe C++ has biased my thinking.
Re: Typed properties - experimental build available
I merged this into v2.1-alpha.3, with the following changes:
- Removed the N and "structN" DllCall arg types.
- When unset is passed to a struct parameter, it is passed along to __value instead of throwing immediately.
- ObjAllocData and ObjFreeData are removed (actually, disabled by preprocessing).
- Fixed some bugs.
Re: Typed properties - experimental build available
That's a very nice addition, thank you much, ↓ is indeed true and painful
This requires calculating the offset of each individual field as needed, so is a laborious and error-prone process.
Re: Typed properties - experimental build available
Because I couldn't find from your code that when and where the propterty p will be read, as a output from dllcall. So, I assume that p would be read automatically by script when needing to get the struct data from the object. p seems some methods like __new() which will be used by script implicitly and automatically.
But now, I guess I have understood it. Maybe p is just a user property which can be defined with any name for an output.
Re: Typed properties - experimental build available
The struct is passed to DllCall, which passes the entire struct by value to the external function. It doesn't care what the property names are.
Re: Typed properties - experimental build available
Code: Select all
class Point {
x : i32, y : i32
}
WindowFromPoint := DllCall.Bind("WindowFromPoint", Point,, "uptr")
GetCursorPos := DllCall.Bind("GetCursorPos", Out(Point), unset)
GetCursorPos(&mp1) ; Let the call construct a new struct.
MsgBox WinGetClass(WindowFromPoint(mp1))
GetCursorPos(mp2 := Point()) ; Output into an existing struct.
MsgBox WinGetClass(WindowFromPoint(mp2))
Out(sc) {
oc := Class()
oc.Prototype := op := {}
op.DefineProp 'p', {type: 'uptr'}
op.DefineProp '__value', {set: setvalue}
setvalue(this, value) {
if value is VarRef
this.p := ObjGetDataPtr(%value% := sc())
else if value is sc
this.p := ObjGetDataPtr(value)
else
throw TypeError('Expected a VarRef or ' sc.Prototype.__Class ' but got a ' type(value), -1)
}
return oc
}
Last edited by V2User on 12 Sep 2023, 06:12, edited 1 time in total.
Re: Typed properties - experimental build available
@lexikos
After setting the data pointer, if the data is allocated by the object, it will be released, so the original data pointer held by the nested structure will point to invalid memory. I recommend iterating over nested structures to update their data Pointers synchronously.ObjSetDataPtr does not affect nested objects, as they each have their own data pointer (which points into the outer object's original data).
Then can Desc of DefineProp(Name, Desc) add the optional Offset property to specify the custom offset?For a typed property, the return value of GetOwnPropDesc is a new object with the own properties Type and Offset.
Re: Typed properties - experimental build available
@thqby
That would make sense, but the function was never really intended for use (on a live struct, in particular).
@V2User
They are not blank.
That would make sense, but the function was never really intended for use (on a live struct, in particular).
Allowing DefineProp to specify an offset seemed like the simplest way to support unions, so I already planned that. It's not as high priority as pointers to structs or arrays, though.Warning: This function is likely to be removed or replaced with a "pointer to struct" type, but is currently the only way to make direct use of typed properties with an externally allocated struct.
@V2User
They are not blank.
Re: Typed properties - experimental build available
[Code1:]
Code: Select all
class Point {
x : i32, y : i32
}
Maybe I have understood more about struct now.
That is to say, the define order of x,y in Pointer cannot be changed, such as changing to [Code2]. Otherwise, it will go wrong. Is that right?
[Code2:]
Code: Select all
class Point {
y : i32,x : i32
}
Re: Typed properties - experimental build available
POINT structure
If you pass class POINT to an API function expecting a POINT structure the order of the returned values is x, y. But of course you can choose the name y for x and vice versa if you have a reason to do it.
Code: Select all
typedef struct tagPOINT {
LONG x;
LONG y;
} POINT, *PPOINT, *NPPOINT, *LPPOINT;
Re: Typed properties - experimental build available
Thanks for your answer. I guess I have learned a big knowledge point about struct.just me wrote: ↑10 Sep 2023, 09:11POINT structureIf you pass class POINT to an API function expecting a POINT structure the order of the returned values is x, y. But of course you can choose the name y for x and vice versa if you have a reason to do it.Code: Select all
typedef struct tagPOINT { LONG x; LONG y; } POINT, *PPOINT, *NPPOINT, *LPPOINT;
Re: Typed properties - experimental build available
@lexikos
Wish to support Type#X syntax to mark the structure member order.
The defining order of the members of structures are not important for C/C++ and other compiled languages, as they recognize the order by the members' names before compiling. However, the defining order is very important for any dynamic languages including AHK. Any changes to the order will go wrong. So this syntax is to solve the issue.
Just an example, maybe not so properly:
After the first instance of Rect is created, the 4 struct members will be sorted in ascending order by i32#X. After that, the order cannot be changed, and #X cannot be added or overridden any longer.
Without this syntax, the struct order will be left,right,top,bottom. It's not right and will go wrong.
But with this syntax, the struct order will be right. Also, the order will be more robust and can be controlled more easily and flexibly. It has more expressiveness and brings more assurance for the correctness of the structure passed to DllCall.
Wish to support Type#X syntax to mark the structure member order.
The defining order of the members of structures are not important for C/C++ and other compiled languages, as they recognize the order by the members' names before compiling. However, the defining order is very important for any dynamic languages including AHK. Any changes to the order will go wrong. So this syntax is to solve the issue.
Just an example, maybe not so properly:
Code: Select all
class RectWidth {
left:i32#1
right:i32#3
}
class Rect extends RectWidth{
top:i32#2
bottom:i32#4
}
rr:=Rect()
DllCall('GetClientRect','ptr',WinExist('A'),Rect,rr)
Without this syntax, the struct order will be left,right,top,bottom. It's not right and will go wrong.
But with this syntax, the struct order will be right. Also, the order will be more robust and can be controlled more easily and flexibly. It has more expressiveness and brings more assurance for the correctness of the structure passed to DllCall.
Last edited by V2User on 14 Sep 2023, 10:19, edited 7 times in total.
Re: Typed properties - experimental build available
@V2User, do you know any function accepting a RectWidth structure?
Your suggestion makes no sense to me. Filling arbitrary 'holes' in previously defined 'perforated' structures only confuses.
Your suggestion makes no sense to me. Filling arbitrary 'holes' in previously defined 'perforated' structures only confuses.
Re: Typed properties - experimental build available
Maybe you are right. Perhaps because I am a beginner in WinAPI, I can't find a much proper example at this moment.
But I think in common, most methods and properties do fill the "holes" spared by the superclasses, acting like #X.
Re: Typed properties - experimental build available
Code: Select all
MsgBox(ObjGetDataPtr(obj := Object())) ; Nonzero value
Is it necessary for a class to allocate memory when it has no typed properties?
Re: Typed properties - experimental build available
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):
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.
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
}
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.
Last edited by lexikos on 27 Mar 2024, 04:26, edited 1 time in total.
Reason: Fixed __value to return 0 for null pointers
Reason: Fixed __value to return 0 for null pointers
Return to “AutoHotkey Development”
Who is online
Users browsing this forum: No registered users and 2 guests