Hi
Sabestian Caine,
Based on my experience from using classes - only in ahk (moreover, the only language I know) - meta-functions are especially useful when your deal with derived classes especially when 1°'hooking' (
__Call); 2°extending and thus enrich features interfaced by the object (
__Call,
__Get,
__Set); but also when using 3°
function objects (
__Call) - the list is by no means exhaustive:
1° Let's say someone says it created an object which is intended to work like a black box however adding that users can set an event hook for the event (you can see this as a little bit like a custom
GuiActivate function label in ahk) - this example also illustrate when __Delete is called and what can prevent it:
Code: Select all
w := new MyHook()
GUI, Show, w400 h400
OnExit, handleExit
return
handleExit:
w.dispose() ; to illustrate __Delete
ExitApp
Class MyHook extends WM_ACTIVATE { ; here we take the most of the calls exposed by the black box
var := "huge string"
__Call(_callee, _params*) { ; set our custom hook
static _i := 0
if (_callee = "call") {
ToolTip % _callee "," ++_i
; return ; uncomment to prevent default behaviour
}
} ; hook the 'call' calls triggered by the message monitoring below
dispose() {
var := ""
base.dispose() ; Inside a method, the pseudo-keyword base can be used to access the super-class versions of methods or properties which are overridden in a derived class
}
}
Class WM_ACTIVATE { ; the black box
__New() {
OnMessage(0x06, this) ; WM_ACTIVATE:=0x06 - this will call 'call' on the instance when the event fires
}
dispose() {
OnMessage(0x06, this, MaxThreads:=0)
/*
unregister the object by specify 0 for MaxThreads - because all references to an object must be released before
the object can be freed, we must remove the circular reference to 'this' , otherwise __Delete will not be called
*/
}
call() {
; some internal black box stuff to handle the event
}
__Delete() {
MsgBox, TEST
}
}
2° In the following example of humanist ideology,
__Call is used to throw an exception if a method implemented by the derived class which is not supported by the class it is derived from is nevertheless call from it (and to handle it):
Code: Select all
e := new Animal("Elephas maximus")
MsgBox % e.ID
try e.speak()
catch exception {
MsgBox % exception
}
MsgBox % e["future"]
e := new HumanReality("John Smith")
MsgBox % e.ID
try e.speak()
MsgBox % e["future"]
try e.askQuestion()
Class Animal {
__New(_specie) {
base.__New(_specie)
this.ID := _specie
}
__Call(_callee) {
throw this.ID . " can not " . _callee "."
}
__Get(_k, _params*) {
if (_k = "future")
return this.ID
}
}
Class HumanReality extends Animal {
__New(_individual) {
base.__New(_individual)
}
askQuestion() {
MsgBox % "Why THERES'S " this.ID " instead of [>>>" this.future "<<<]?"
}
speak() {
MsgBox % "Hello, world!"
}
future[] { ; https://github.com/cocobelgica/AutoHotkey-JSON/blob/master/JSON.ahk#L339
get {
static empty := {}, undefined := ComObject(0, &empty, 1)
return undefined
}
}
}
3° Function objects,
i.e. user-defined objects which can be called like a function:
Code: Select all
SetTitleMatchMode, 2
fn := new FObject("WinActive")
return
!i::MsgBox % %fn%("(.*- )?YouTube(?= - Mozilla Firefox$) ahk_class MozillaWindowClass ahk_exe firefox.exe") ; acts like WinActive but check it own settitle match mode to determine if the specified window is active
!j::fn.titleMatchMode := "RegEx"
!k::fn.titleMatchMode := 1
class FObject { ; based on https://github.com/Lexikos/xHotkey.ahk/blob/master/xHotkey_test.ahk#L31
_titleMatchMode := 1
__New(fn) {
this.fn := IsObject(fn) ? fn : Func(fn)
}
titleMatchMode {
set {
return this._titleMatchMode := (Trim(value) ~= "i)^(1|2|3|RegEx)$") ? value : this._titleMatchMode
}
get {
return this._titleMatchMode
}
}
__Call(callee, args*) { ; each time the function is called we set the title match mode accordingly before resetting it
local
if (callee = "") {
titleMatchMode := A_TitleMatchMode
SetTitleMatchMode % this.titleMatchMode
fn := this.fn, r := %fn%(args*)
SetTitleMatchMode % titleMatchMode
return r
}
}
}
Hope this helps.