Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

[AHK_L] curry.ahk - Function call hacking! (Updated!)


  • This topic is locked This topic is locked
7 replies to this topic
fincs
  • Moderators
  • 1662 posts
  • Last active:
  • Joined: 05 May 2007
A curry is a function call wrapper that adds or removes parameters. This for example allows to make DllCall wrappers.

Examples:
; Curries A, B, C: basic currying

a := curry("handler", "curry A", "All parameters are curried")
a.()

b := curry("handler", "curry B")
b.("Only the first parameter is curried")

c := curry("handler")
c.("curry C", "No parameters are curried")

; Curry D: using __empty() to insert a placeholder parameter

d := curry("handler", __empty(), "Only the second parameter is curried")
d.("curry D")

; Curry E: currying a curry

e1 := curry("handler2", __empty(), "Curry E1 fills in the second parameter...")
e2 := curry(e1, __empty(), "... whilst curry E2 fills in the third.")
e2.("curry E: currying a curry")

; Curry F: currying a BIF

f := curry("DllCall", ResolveFunc("user32\MessageBox"), "ptr", 0, "str", __empty(), "str", __empty(), "uint", __empty(), "int")
f.("curry F:`nIt is possible to curry built in functions, too!", A_ScriptName, 0)

; Using a curry as a callback parameter

arrayA := ["hello", "world", "pickles"]
arrayB := ["toast", "spaghetti", "dinner"]
arrayC := ["MsgBox", "FileSelectFile", "SetStoreCapslockMode"]

; These curries:
;   - Prepend a parameter
;   - Delete the key parameter via __drop()
foreach(arrayA, curry("Collect", "arrayA_c", __drop()))
foreach(arrayB, curry("Collect", "arrayB_c", __drop()))
msgbox % "[arrayA]`n" arrayA_c "`n[arrayB]`n" arrayB_c

; Using a curry as a function wrapper
foreach(arrayC, curry("CustomCollect"))
msgbox % CustomCollect()

handler(a, b)
{
	MsgBox %a%: %b%
}

handler2(a, b, c)
{
	msgbox %a%:`n`n%b%`n%c%
}

Collect(varname, value)
{
	global
	%varname% .= value "`n"
}

CustomCollect(k="", v="")
{
	static data
	if k
		data .= "[" k "]: " v "`n"
	else
	{
		q := data
		data := ""
		return q
	}
}

foreach(obj, callback)
{
	for k,v in obj
		callback.(k, v)
}

; Function taken from Lexikos' example
ResolveFunc(function)
{
	static API_SUFFIX := A_IsUnicode ? "W" : "A"
	
	if i := InStr(function, "\", 1, 0)
	{
		; Since we know the DllFile and Function, resolve it in advance for performance:
		dllfile := SubStr(function, 1, i-1), function := SubStr(function, i+1)
		if   !(dll := DllCall("GetModuleHandle", "str", dllfile))
		  && !(dll := DllCall("LoadLibrary", "str", dllfile))
		  || !(proc := DllCall("GetProcAddress", "ptr", dll, "astr", function))
		  && !(proc := DllCall("GetProcAddress", "ptr", dll, "astr", function API_SUFFIX))
			return
		return proc
	}else
		return function
}

Here is curry.ahk:
;
; Function: curry
; Description:
;      Wraps a function in order to automatically pass or remove parameters.
; Syntax: curry(function, arguments...)
; Parameters:
;      function - A function name or function object.
;      arguments - [i](Variadic)[/i] Arguments to automatically pass. Use [i]__empty()[/i] to
;          fill a hole and [i]__drop()[/i] to drop a parameter.
; Return value:
;      A function object that can be called via [i]obj.(params)[/i].
; Related: [bbcode]None.
; Example:
;      None.
;

curry(func, prm*)
{
	global _CurryObj
	return (prm.MaxIndex() != "") ? new _CurryObj(func, prm) : IsObject(func) ? func : IsFunc(func) ? Func(func) : ""
}

__empty()
{
   static _empty := {}
   return _empty
}

__drop()
{
   static _drop := {}
   return _drop
}

class _CurryObj
{
	__New(func, prm)
	{
		if not this.func := IsObject(func) ? func : IsFunc(func) ? Func(func) : ""
			return
		this.prm := prm
	}
	
	__Call(m, prm*)
	{
		static _empty := __empty(), _drop := __drop()
		
		i := 0
		for k,v in this.prm
			`(_empty != v) ? _drop != v ? prm.Insert(A_Index + i, v) : prm.Remove(A_Index + (i--)) : ""
		
		return this.func.(prm*)
	}
}


Frankie
  • Members
  • 2930 posts
  • Last active: Feb 05 2015 02:49 PM
  • Joined: 02 Nov 2008
Don't really get what it does, but you should note that it requires AHK_L and the min version needed to run it.

Edit: I looked closer at the example and I kinda get it now.
aboutscriptappsscripts
Request Video Tutorials Here or View Current Tutorials on YouTube
Any code ⇈ above ⇈ requires AutoHotkey_L to run

fincs
  • Moderators
  • 1662 posts
  • Last active:
  • Joined: 05 May 2007

[AHK_L] curry.ahk - Function call hacking!

It requires L60.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
Rather than ...
if !IsObject(m := this.func)
      %m%(prm*)
   else
      m.(prm*)
... why didn't you do this.func(prm*), or better yet return this.func(prm*) as in funcobj_call?

OceanMachine
  • Members
  • 790 posts
  • Last active: Aug 23 2013 02:10 PM
  • Joined: 15 Oct 2007

A curry is a function call wrapper that adds or removes parameters


No, no, no, this is a curry!!!

mmmmmmmmm.... currryyyyyyyyy

Posted Image

fincs
  • Moderators
  • 1662 posts
  • Last active:
  • Joined: 05 May 2007

... why didn't you do this.func(prm*), or better yet return this.func(prm*) as in funcobj_call?

Allright, I changed it. PS: the BBCode is not out of place, it's for GenDocs.

IsNull
  • Moderators
  • 990 posts
  • Last active: May 15 2014 11:56 AM
  • Joined: 10 May 2007
Thanks fincs, I wasn't aware that variadics also work when calling a function.

This is actually very cool
return this.func(prm*)

8)

fincs
  • Moderators
  • 1662 posts
  • Last active:
  • Joined: 05 May 2007
I've just updated this function + example to take advantage of new AutoHotkey features. Enjoy 8)