local a = {}
function a:test1(value)
print(value)
end
local b = {}
function b:test2(v1, v2)
v2(100);
end
b:test2(_, a.test1)
Doesn't work. Value is nil. I could find a solution doing an encapsulation in an anonymous function
b:test2(variable, function(value) a:test1(value) end)
But I find it pretty bad mkay
What is the correct syntax ?
Function Call When calling a function with a function parameter, the value passed must be a pointer to a function. Use the function's name (without parentheses) for this: func(print); would call func , passing the print function to it.
To pass an object as an argument we write the object name as the argument while calling the function the same way we do it for other variables. Syntax: function_name(object_name); Example: In this Example there is a class which has an integer variable 'a' and a function 'add' which takes an object as argument.
An object can be passed to a function just like we pass structure to a function. Here in class A we have a function disp() in which we are passing the object of class A . Similarly we can pass the object of another class to a function of different class.
Passing a function as parameter to another functionC++ has two ways to pass a function as a parameter. As you see, you can use either operation() or operation2() to give the same result.
anotherObject:aFunction(variable, object.doStuff)
is the correct syntax.
Using a colon :
with a function is just syntactic sugar for a call or declaration with an implicit self
parameter as the first argument. If you would like to follow the pattern you've shown in your example in a cleaner way, you could use a helper function.
local function bind(t, k)
return function(...) return t[k](t, ...) end
end
You then apply it like so.
anotherObject:aFunction(variable, bind(object, 'doStuff'))
Edit: I believe the solution to your problem will require binding at some level, without resorting to modifying the Lua interpreter or using a code translation step. This is fundamentally because functions in Lua do not carry any information about their origin. I.e., tables do not inherently own the functions that they store.
For example, the following is perfectly legitimate Lua code.
function Circle:area() -- function Circle.area(self)
-- ...
end
-- Evaluate the function in the "area" slot with Square as the self parameter.
Circle.area(Square)
Of course, you could try a paradigm shift, but it may be too late for that if you're building an entire application based on the idea of functions being tied to the table that they have been indexed from, as you said. Therefore, I propose the following crazy solution.
local mt = {}
function mt:__index(k)
local v = self._slots[k]
if v == nil then
-- Ascend the inheritance tree.
-- This has to be done with rawget all the way up,
-- otherwise inherited functions would be repeatedly bound.
local p = self
repeat
p = rawget(p, '_parent')
if not p then break end
v = p._slots[k]
until v
end
if type(v) == 'function' then
-- Return a self-bound version of the function.
return function(...) return v(self, ...) end
end
return v
end
function mt:__newindex(k, v)
self._slots[k] = v
end
--- Demo & Tests ---
local function Object(parent)
local o = setmetatable({_slots = {}}, mt)
if parent then rawset(o, '_parent', parent) end
return o
end
local o1 = Object()
local o2 = Object(o1)
assert(o1.abc == nil, 'o1.abc should be nil')
o1.abc = 3
assert(o1.abc == 3, 'o1.abc should be 3')
assert(o2.abc == 3, 'o2.abc should be 3, inherited from o1')
o2.abc = 7
assert(o2.abc == 7, 'o2.abc should be 7, overriding o1')
assert(o1.abc == 3, 'o1.abc should be 3, unaffected by o2 setter')
function o1:test(bar)
return self.abc + bar
end
assert(type(o1.test) == 'function', 'o1.test should be a function')
assert(type(o2.test) == 'function', 'o2.test should be a function, inherited from o1')
assert(o1.test(5) == 8, 'o1.test(5) should return 3 + 5 = 8')
assert(o2.test(11) == 18, 'o2.test(11) should return 7 + 11 = 18')
function o2:test2(fn)
return self.abc + fn(7)
end
assert(o2.test2(o1.test) == 17, 'o2.test2(o1.test) should return 7 + (3 + 7) = 17')
o2.test3 = o1._slots.test -- proper function copying
assert(o2.test3(11) == 18, 'o2.test3(5) should return 7 + 11 = 18')
o2.abc = nil
assert(o2.abc == 3, 'o2.abc should be 3 again, inherited from o1 after clearing')
o2.abc = false
assert(o2.abc == false, 'o2.abc should be false, __index needs to differentiate between nil and false')
This metatable will provide you with what you want, with inherited and bound functions to boot. You will just need to make sure that all of the tables that you want to follow this pattern also follow the method of object creation shown in the example code.
To explain, each table made in this way has any new assignment redirected into the _slots
sub-table and any new retrieval checked up the _parent
inheritance tree. If the type of the value is a function
, then it returns a new closure with the original self
that started the check bound to the function found.
Obviously, calling a function from one of these objects with the :
colon syntax is going to be a silly idea, since it would evaluate to o.fn(o, o)
, and that is probably not what you want. Another caveat is that copying functions onto these objects, from these objects, will not work as expected. o1.newfn = o2.fn
will put an o2
bound function into o1
, which in turn will be re-bound to o1
. The end result would be something like o2.fn(o2, o1)
. You will have to copy functions from the _slots
table.
In conclusion: Even though this works, I would not personally recommend it in the long run, since it may be confusing to anyone used to how Lua works with tables, indexing, and functions, and there will be overhead. You might be able to do away with some it via memoizing the closures, but I'll leave that decision up to you. Good luck!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With