How to use __get, __set and __call meta functions in Classes?

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
Sabestian Caine
Posts: 528
Joined: 12 Apr 2015, 03:53

How to use __get, __set and __call meta functions in Classes?

22 Apr 2019, 12:43

Hello Friends..

I am trying to learn classes in AutoHotKey. I have learnt a lot of things in classes by these tutorials-

https://www.autohotkey.com/boards/viewtopic.php?f=7&t=6177
https://www.autohotkey.com/boards/viewtopic.php?f=7&t=41332
https://autohotkey.com/boards/viewtopic.php?f=7&t=54588
https://www.youtube.com/watch?v=yTCeM-txIp0&t=2619s
https://www.autohotkey.com/boards/viewtopic.php?t=6033

Still, I am confusing for the use of meta functions in classes which are following-
__get()
__set()
__call()
__delete()
__new()

However, I've known the use of __new() meta meta function in classes. As far as i know about __new() meta function is that- when we instantiate the class then it first of all calls __new meta function and runs the codes whichever written in __new function. For example-

Code: Select all

obj:=new myclass("Auto Hot Key")
return

class myclass
{
__new(var1)
{
MsgBox You have instantiated myclass and %var1% has been passed as __new's parameter
}
}
But, I am unable to understand the uses of rest meta functions like __get(), __set(), __call(), __delete().

Please help me guide..

Thanks a lot..
I don't normally code as I don't code normally.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: How to use __get, __set and __call meta functions in Classes?

22 Apr 2019, 12:51

Meta functions wrote:Meta-functions define what happens when a key is requested but not found within the target object. For example, if obj.key has not been assigned a value, it invokes the __Get meta-function.
Example,

Code: Select all

class C {
	__get(key){
		; when trying to get key, this method is called if instance_of_C.haskey(key) returns 0
		msgbox % "trying to get key:`t" . key . "`nWhat to do?"
		return "This is what you get..."	; not using return here has special meaning, it is described in the documentation.
	}
}

instance_of_C := new C
msgbox % instance_of_C["Hello"] ; Trying to get key "Hello"
Cheers.
A_AhkUser
Posts: 1147
Joined: 06 Mar 2017, 16:18
Location: France
Contact:

Re: How to use __get, __set and __call meta functions in Classes?

22 Apr 2019, 16:57

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.
my scripts
User avatar
Sabestian Caine
Posts: 528
Joined: 12 Apr 2015, 03:53

Re: How to use __get, __set and __call meta functions in Classes?

27 Apr 2019, 15:02

A_AhkUser wrote:
22 Apr 2019, 16:57
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.

Thanks dear A_AhkUser.

Dear A_AhkUser please tell me why __get() meta function not running in this example-

Code: Select all

ram:= new stu
MsgBox % ram.fn()
ram.set_value(5,10)

class stu
{
  fn()
  {
   return % "this is fun... "
  }
  set_value(a,b)
  {
  this.a:=a
  this.b:=b
  return this.b
  }
  __New()
	{
		MsgBox new
	}
	__Set()
	{
		MsgBox set
	}
	__Get()
	{
		MsgBox get
	}
}
When i run above codes it first runs __new() meta function, because when we instantiate the class then first of all __new() function is call which is ok. Then it runs fn() function, while it should run __get() function first, because i am returning (getting ) value from fn() function.

Then it two times runs __set() function which is again ok as i am passing two parameters in set_value() function.

I don't see why it runs __get() function first, when i calls fn() function?

Please help and guide. Thanks a lot...
I don't normally code as I don't code normally.
A_AhkUser
Posts: 1147
Joined: 06 Mar 2017, 16:18
Location: France
Contact:

Re: How to use __get, __set and __call meta functions in Classes?

27 Apr 2019, 17:12

Sabestian Caine wrote:
27 Apr 2019, 15:02
Hi Sabestian,

Consider the following example:

Code: Select all

#NoEnv
#Warn
#SingleInstance force

str := ""
for k, v in stu
	str .= "`n" k A_Tab v
MsgBox, 64,, Properties & methods that should not invoke µ-functions while getting and setting since there defined within the target base object:`n%str%

; ram := new stu
ram := new stu()
ram.fn()
ram.fn2()
ram.set_value(5, 10)
return

class stu {
	static q := 1
	p := 7
	__New() {
	MsgBox % A_ThisFunc
	}
	__Set(k, _params*) { ; syntax: __Set([Key, Key2, ...], Value)
		MsgBox % A_ThisFunc " | <" k "> is being set - value: " value:=_params.pop() ; pop removes and returns the last array element
	return value ; not using return here has special meaning, as already mentionned by Helgef
	}
	; __Get(k, params*) { ; syntax: __Get([Key, Key2, ...])
	; MsgBox % A_ThisFunc
	; }
	__Call(_callee, _p*) { ; syntax: __Call(Name [, Params...])
	MsgBox % A_ThisFunc " | <" _callee "> is being called."
	}
	fn() {
	return "this is fn..."
	}
	set_value(a, b) {
	return this.b:=b, this.a:=a
	}
}
@Helgef In particular, I might add, if I may, to your quote - i.e.
Meta-functions wrote:Meta-functions define what happens when a key is requested but not found within the target object.
the following one:
Objects: General explanation of objects. wrote:Yet, The proper use of objects (and in particular, classes) can result in code which is modular and reusable. [...] For instance, one can improve or modify one section of code without having to know the details of other sections, and without having to make corresponding changes to those sections.
(my italic).
In order to bring out one of the key meaning, purpose of the meta-methods. As an example, when one declare meta-functions, the final parameter is likely to be marked as variadic for this very reason.
To once again answer to the when (to use) in the same time as the how (to use) - in this case, - they always are closely intertwined.

Cheers
my scripts
User avatar
lmstearn
Posts: 694
Joined: 11 Aug 2016, 02:32
Contact:

Re: How to use __get, __set and __call meta functions in Classes?

31 Mar 2022, 09:10

A_AhkUser wrote:
27 Apr 2019, 17:12
Thanks for that example, very instructive. Added an extra point on the fn* calls

Code: Select all

#NoEnv
#Warn
#SingleInstance force

str := ""
for k, v in stu
	str .= "`n" k A_Tab v
MsgBox, 64,, Properties & methods that should not invoke µ-functions while getting and setting since there defined within the target base object:`n%str%

; ram := new stu
ram := new stu()
ram.fn()
ram.fn2()
ram.set_value(5, 10)
return

class stu {
	static q := 1
	p := 7
	__New() {
	MsgBox % A_ThisFunc
	}
	__Set(k, _params*) { ; syntax: __Set([Key, Key2, ...], Value)
		MsgBox % A_ThisFunc " | <" k "> is being set - value: " value:=_params.pop() ; pop removes and returns the last array element
	return value ; not using return here has special meaning, as already mentionned by Helgef
	}
	; __Get(k, params*) { ; syntax: __Get([Key, Key2, ...])
	; MsgBox % A_ThisFunc
	; }
	__Call(_callee, _p*) { ; syntax: __Call(Name [, Params...])
	if (ObjHasKey(stu, _callee))
	MsgBox % A_ThisFunc " | <" _callee "> is being called."
	else
	MsgBox % A_ThisFunc " | <" _callee "> does not exist."
	}
	fn() {
	msgbox "this is fn..."
	}
	set_value(a, b) {
	return this.b:=b, this.a:=a
	}
}
Esc::
exitapp
Interesting point on _params, guess it's like setting default values for the class. :)
:arrow: itros "ylbbub eht tuO kaerB" a ni kcuts m'I pleH

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Rohwedder and 250 guests