[a136] broken subclass instantiation Topic is solved

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

[a136] broken subclass instantiation

Post by swagfag » 28 May 2021, 16:47

[Moderator's note: Topic moved from Bug Reports.]

Code: Select all

#Requires AutoHotkey v2.0-a136-feda41f4

class Parent {
	static Call() => MsgBox(A_ThisFunc)
}

class Child extends Parent {
	__New() => MsgBox(A_ThisFunc)
}

C := Child()
; returns "Parent.Call" -> it's calling Parent's static Call, 
; instead of Child's actual constructor "Child.Prototype.__New", why is that?

lexikos
Posts: 9560
Joined: 30 Sep 2013, 04:07
Contact:

Re: [a136] broken subclass instantiation

Post by lexikos » 28 May 2021, 19:32

Child() is Child.Call(). Why wouldn't you expect it to call Parent.Call? Child.Call = Parent.Call.

There's nothing special about calling a class; just like every other object, it calls the Call method.

Topic moved.

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: [a136] broken subclass instantiation

Post by swagfag » 29 May 2021, 12:37

its not so much that i wouldnt expect it to call Parent.Call(it wouldnt have been ideal if it did, but i could have lived with that). its that i would expect it to also call Child.Prototype.__New!
and i would expect it to do so since(yellow highlight):
image.png
image.png (32.13 KiB) Viewed 1164 times
so i guess what i had overlooked here was the fact that it says the default implementation(ie the one belonging to Object in this case). fair enough, if u provide ur own implementation such as:

Code: Select all

class Parent {
	static Call() => MsgBox(A_ThisFunc)
}
then it can no longer be considered a default one, can it? so there actually shouldnt be any expectation that it would call Child.Prototype.__New. in that case, then its fine
so to fix this, if its ur code, u can either:
  1. rewrite ur Parent.Call with derived instantiation in mind
  2. patch out Parent.Call at runtime, something like this i imagine:

    Code: Select all

    #Requires AutoHotkey v2.0-a136-feda41f4
    
    class Parent {
    	InstancePropOfParent := 'InstancePropOfParent'
    
    	; this is the original implementation of Parent(arg1, arg2), requiring 2 arguments
    	static Call(arg1, arg2) {
    		; imagine it does something specific
    		MsgBox A_ThisFunc '`n' arg1 '`n' arg2
    	}
    }
    
    class Child extends Parent {
    	InstancePropOfChild := 'InstancePropOfChild'
    
    	__New(arg1, arg2, arg3) {
    		MsgBox A_ThisFunc '`n' arg1 '`n' arg2 '`n' arg3 '`n' this.InstancePropOfChild '`n' this.InstancePropOfParent
    	}
    }
    
    OriginalParentCall := Parent.Call
    Parent.DefineProp('Call', {Call: MonkeyPatchedParentCallWithDerivedInstantiationSupport})
    MonkeyPatchedParentCallWithDerivedInstantiationSupport(this, Args*) { ; has to be variadic, since we cant foresee the arity of derived constructors
    	if (this = Parent) ; clientcode is calling Parent() somewhere, but potentially also Parent(..., ..., ..., *)
    		return OriginalParentCall(this, Args*)
    	else
    		return (Object.Call)(this, Args*)
    }
    
    ; P := Parent() ; throw missing required param 'arg1', this is OK
    ; P := Parent(111) ; throw missing required param 'arg2', this is OK
    P := Parent(111, 222) ; calls the original implementation, this is OK
    ; P := Parent(111, 222, 333) ; throw too many params passed to 'Parent.Call', this is OK 
    
    ; C := Child() ; calls Child's __New via Object.Call, throws missing required param 'arg1', this is OK
    ; C := Child(777) ; calls Child's __New via Object.Call, throws missing required param 'arg2', this is OK
    ; C := Child(777, 888) ; calls Child's __New via Object.Call, throws missing required param 'arg3', this is OK
    C := Child(777, 888, 999) ; calls Parent's __Init and Child's __Init and __New via Object.Call, this is OK
    ; C := Child(777, 888, 999, 101010) ; calls Child's __New via Object.Call, throws too many params passed to 'Child.Prototype.__New', this is OK
but if its not ur code, then u can only either:
  1. ask the author to rewrite their code with derived instantiation in mind, or to not use static Call at all. good luck with that
  2. rewrite the code urself. great, u might have inadvertently broken something, plus u now also have to maintain the library urself
  3. use above monkey patch. its a tossup whether that ends up breaking something in the library's code(eg something in it might specifically rely on static Call not being messed with)

and ultimately all this would have been fine if it actually helped solve my problem, so heres the next question for u @Lexikos - complete the code so it compiles:

Code: Select all

#Requires AutoHotkey v2.0-a136-feda41f4

class IUnknown extends ComValue {
	__New(Ptr := 0) {
		; ???
	}
}

a := IUnknown()
MsgBox a.Ptr = 0 ; true
MsgBox a is IUnknown ; true
MsgBox a is ComValue ; true
MsgBox ComObjType(a) ; 13, ie 0xD VT_UNKNOWN

b := IUnknown(someValidNakedIUnknownPtr)
MsgBox b.Ptr = someValidNakedIUnknownPtr; true
MsgBox b is IUnknown ; true
MsgBox b is ComValue ; true
MsgBox ComObjType(b) ; 13, ie 0xD VT_UNKNOWN
while i can fiddle around with redirecting static Calls and whatnot, theres no way to instantiate the derived class. cant use the monkey patch method either, since it complains about "the base being invalid"

lexikos
Posts: 9560
Joined: 30 Sep 2013, 04:07
Contact:

Re: [a136] broken subclass instantiation

Post by lexikos » 31 May 2021, 04:21

The whole point of overriding Call is to change what it does. Usually it is specifically to avoid having it create a new object that you're going to discard, such as by returning the result of MsgBox instead of an object. If you define it as static Call() => MsgBox(A_ThisFunc), obviously it's only going to call MsgBox and not create any objects. Obj() is the same as Obj.Call() for nearly all types of values, including classes; i.e. it's just a normal method call, except that Obj() never calls the __Call meta-method. If you redefine a method, the previous definition is only relevant if you call it.
(ie the one belonging to Object in this case)
In the case of class Parent, yes. It should apply to all built-in classes derived from Object which can be instantiated by calling ClassName(). Look for NewObject<> in the "class tree".

Other classes which do not derive from Object have different built-in functions in place of Call. ComValue.Call is effectively just the former ComObject() function, ComArray.Call is ComObjArray(), etc. Float(), Integer() and String() are the same functions as before the classes, just adjusted to exclude the class object parameter.
heres the next question for u
Where? I see no question.


Regarding your unreasonable demand to fix code, ;)
ComValue itself is a class derived from Any, but is used only to create or identify COM wrapper objects.
Source: ComValue - Syntax & Usage | AutoHotkey v2
You can't extend a COM wrapper. It is like a Primitive; base is determined by type, not the other way around.

"Construction and Destruction" is a sub-heading of "Custom Objects", although class can obviously be used with non-Object classes. The documentation is perhaps a bit outdated.


It seems your demand was only unreasonable, not impossible, as this code does "compile":

Code: Select all

#Requires AutoHotkey v2.0-a136-feda41f4

a := IUnknown()
MsgBox a.Ptr = 0 ; true
MsgBox a is IUnknown ; true
MsgBox a is ComValue ; true
MsgBox ComObjType(a) ; 13, ie 0xD VT_UNKNOWN

someValidNakedIUnknownPtr := ObjPtrAddRef({})

b := IUnknown(someValidNakedIUnknownPtr)
MsgBox b.Ptr = someValidNakedIUnknownPtr ; true
MsgBox b is IUnknown ; true
MsgBox b is ComValue ; true
MsgBox ComObjType(b) ; 13, ie 0xD VT_UNKNOWN

class IUnknown {
	static __New() {
		this.Prototype := ComValue.Prototype
	}
	static Call(Ptr := 0) {
		return ComValue(13, Ptr)
	}
}

; This is not intended to be useful. ;)
cant use the monkey patch method either, since it complains about "the base being invalid"
I don't think you're using the term "monkey patch" correctly. One would monkey patch COM wrappers by modifying ComValue.Prototype. I assume you attempted to assign IUnknown.Prototype as the base of an Object. An Object is not a ComValue any more than it is a Primitive.

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: [a136] broken subclass instantiation

Post by swagfag » 18 Sep 2021, 18:16

i finally dug up this thread amongst 200 frozen tabs, whooops

appreciate the example, but thats not exactly what i had in mind. though it satisfies the constraints, the way it does it serves no purpose - u ask for an IUnknown object to be instantiated and instead get a ComValue back, meanwhile the IUnknown is effectively aliased to a ComValue.

what if the IUknown had instance methods(not shown here)? gone.
what if other IWhatevers(not shown here) deriving from the IUknown had their own instance methods? gone.

the point was IUnknown derives from ComValue, other IWhatevers derive from IUknown and so on, and all instantiated objects test positive for is ComValue and thus can also be passed to other function expecting ComValues(eg ComObjType())

lexikos
Posts: 9560
Joined: 30 Sep 2013, 04:07
Contact:

Re: [a136] broken subclass instantiation  Topic is solved

Post by lexikos » 25 Sep 2021, 00:34

I did say this is not intended to be useful.

I saw what you were trying to do and my answer was, in short, you can't.

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: [a136] broken subclass instantiation

Post by swagfag » 25 Sep 2021, 04:11

i see. well, thanks anyway :)

Post Reply

Return to “Ask for Help (v2)”