Your solution, Lexikos, is one of those simple and beautiful solutions that just make me go "well, why didn't I think of that?". I guess that solution shows the hallmark of a master - coming up with those simple but ingenious solutions. What a joy! And good to hear by the way that that behavior is/will be standard in version 2.

I also want to thank you, Coco, for taking the time to make the solution you came up with. Even though Lexikos' solution was simpler and more complete, I really appreciate your helpfulness. Thank you!

Hm, Lexikos' solution seems to kill the builtin Object() methods of classes inheriting from ThrowsNonexistentMethod. This includes the method _NewEnum(), which means that you cannot iterate over the object any more with a for loop:

for key, value in object …

This 'for' statement triggers the __Call() method, giving the error:

"Non-existent method, specifically _NewEnum"

How can I make __Call() not override the default methods, or at least pass any calls to the default methods along to the actual default methods?

Or is there a version of __Call() that is only called if a method with a given name does not exist at all in the given object?

jhertel wrote:I didn't realize it was also called when a method was called with too many or too few parameters.

It's not. Like __Get and __Set, it's called if the key isn't defined in this. The methods are defined in this.base. The reason Coco added a second __Call rather than using the first one is that the __Call in the super-class is not called for methods defined in Test, even if the methods themselves can't be called. If you subclass Test, any methods you define in the subclass will not be validated unless you redefine __Call, like so:

[trismarck beat me to it, but I'm leaving my explanation:]Also note that because __Call is only called for keys which don't exist in the target object, it isn't called for static methods. That is, it is called for t.ExistentMethod() but not Test.ExistentMethod(), because ObjHasKey(t, "ExistentMethod") is false while ObjHasKey(Test, "ExistentMethod") is true.

Btw, if you pass more parameters than needed, it will normally not prevent the method from being called. Only passing too few parameters is considered an error when dynamically calling a function or method. In v2, generally an exception is thrown if a required parameter is missing a value.

Unless required by functionality, a well-designed __Get should not tamper with or override the result when the caller tries to access a key which happens to be a method(declared in the class) or a Property. IMO, devs should always allow meta-functions to finish/cast normally, it makes classes/objects easier to extend/subclass, maintain, debug(subjective), etc.

I did notice just before going to bed last night that what I thought worked in my last comment above did not work in subclasses, which is what I want it to do.

I can see that all this and all your comments requires of me to take some time to sit down with a nice cup of coffee and try to really wrap my head around how objects and classes work in AutoHotkey. Especially the intricate details of when and in what order the meta functions are called. I think I tried at first to partly use my intuition and experience from other languages I know (Python probably being the closest) without completely understanding how it works in AutoHotkey. I did try to read the docs of course, but I failed to understand all the details. Now I will try again, combined with you very helpful comments above, to see if I can get a clear understanding.

Coco wrote:Unless required by functionality, a well-designed __Get should not tamper with or override the result when the caller tries to access a key which happens to be a method(declared in the class) or a Property.

Coco, I agree with that (~well-designed meta-functions / classes should work in every case, even if meta-functions are called in the scenario above). Actually, this could be the point of why my example with base[aFnName] is broken.

To clarify, by "finish/cast normally" I understand that meta-functions should never explicitly return, unless its explicitly needed.

Lexikos wrote:this[aFnName] will also call a property if aFnName is a property name.

Thanks. So in the example I posted, if there was custom behaviour defined for the key aFnName somewhere upstream in the class hierarchy (i.e. a behaviour that forbids to _call_ (vs get) a Property), then base[aFnName] would just skip that behaviour (undesired). So that example is actually crap. Another note is that this[aFnName] really does __Get, not __Call. Yet another note is that even doing this[aFnName]() (__Call) will _still_ invoke 'get' of the Property; fortunately, call invocation of getter of Property can be forbidden i.e. inside of a meta-function of the class that (the class) defines the Property.

So it looks like the best practice is this:

for the existence of the method, at the top of the class hierarchy, define a class that reports non-existence

for the number of parameters, every class should act on its own and report errors about too few/too many params / uncallable Properties _only_ for keys of that class object (vs for base classes). Checking can be implemented inside of meta-functions - i.e. inside of __Call.

It also looks like Functors can't be used as meta-functions in the example above (i.e. as a replacement of __Call if there were multiple keys that we would like to handle and each key would require a different behaviour). Using Functors would change the target of the invocation, which (preserving the target) is needed to detect the existence of the method in the inheritance chain the target is part of.

trismarck wrote:Using Functors would change the target of the invocation, which (preserving the target) is needed to detect the existence of the method in the inheritance chain the target is part of.

Huh? The target of invocation is this, which is passed as an additional parameter to the functor in place of the method name. If you meant whichever class defined the method, you know that at load time because you know where you wrote the method. Likewise, for functors you just need to include some information in the functor to identify which class it belongs to.

Another option is to derive your object not from the class, but from another object which merely defines meta-functions. The meta-functions can then implement inheritance in whatever way you want.

trismarck wrote:Using Functors would change the target of the invocation, which (preserving the target) is needed to detect the existence of the method in the inheritance chain the target is part of.

Huh? The target of invocation is this, which is passed as an additional parameter to the functor in place of the method name. If you meant whichever class defined the method, you know that at load time because you know where you wrote the method.

Yes, I didn't actually think when I wrote this. What I've meant was this: lets suppose we're invoking x.fun3() and there is a key "fun3" at the end of the inheritance tree of x, in the third base. In the first or second base, there is a functor instance defined as the __Call meta-function. When that functor instance is invoked, the original invocation can't detect the existence of the method in the rest of inheritance tree of 'x' anymore, because that invocation has just ended (by 'has just ended' I actually mean that the invocation won't traverse automatically through the rest of the inheritance tree of x) (actually, I'd have to check if the original invocation really 'ends' at that point - probably not - the original invocation call is I guess still on the callstack during the whole time the new invocation was spawned). What I've meant was sort of described in this thread and just mentioned it again here, w/o providing the link (should have done that really). But lets leave that aside.

Lexikos wrote:Likewise, for functors you just need to include some information in the functor to identify which class it belongs to.

Didn't realize that it's possible to pass 'parameters' to the functor handler by just putting the parameters inside of the functor. Thanks.

Ok, so to implement checking of too few/too many params with functors: lets suppose we have the following classes:

; call the method of the classreturn aCurrentClass[aName].Call(aTarget, aP*)}

; if the class object lacks the requested key

; change the base of the target target_base := aTarget.base; relies on correct implementation of __Get of bases of aTarget aTarget.base:= aCurrentClass.base; relies on correct implementation of __Set in bases of aCurrentClass

; 'continue' the invocation the functor instance has interrupted, from the point of interuption ret := aTarget[aName](aP*)

; supposing __Call was defined in a class, if we want to call ; that __Call even if functor specified for the meta-function, ; append '_' at end of __Call. The functor will call __Call_. __Call_(aName, aP*){msgbox __Call_} fun1(a){msgbox fun1return4444} fun2(a){

The above is untested. Perhaps it could be used for debugging purposes.__CallstackDepthFix is specified manually because I didn't know how to fix it. The callstack depth changes whenever the functor handler 'continues' the previous invocation and because the base of target is temporarily changed for that resumed invocation, the functor handler can't scan the original inheritance tree of the target to determine the proper callstack depth fix (or at least it looks like it).

//edit: the example requires >=1.1.20 (because of this) or at least >=1.1.19 because of .Call.