1
votes

I have a pretty mind-bending setup right now. I have a regular function that returns a table with functions in it under keys "string" and "number":

function defGeneric()
    local function funcNumber(a)
        return 2*a^2
    end
    local function funcString(a)
        return a.." - test"
    end
    local returnTable={}
    returnTable["number"]=funcNumber
    returnTable["string"]=funcString
    return returnTable
end

And that works fine. But what I want to do now is make the table that this function returns callable. To illustrate, let's say we have v=defGeneric(). Specifically:

  1. If v is called with a string str, return the result of v["string"](str)
  2. If v is called with a number n, return the result of v["number"](n)

This is obviously a job for metatables, so I can (in my function) add the code to set a metatable:

local metaTable = {
        __call = function (...) -- "call" event handler
            return  
        end
    }
setmetatable(returnTable,metaTable)

But I don't know what I would put after that return statement. I don't think I can reference returnTable, because this table will be called like so:

v=defGeneric()
v("test")

And I need to reference v's "string" function (there certainly could be multiple defGeneric() tables in one program).

I think the answer here might be some self trick but I can't wrap my head around how. How do I reference a metatable's table from the metatable?

1
__call = function(t,x) return t[type(x)](x) end - Egor Skriptunoff
@EgorSkriptunoff Just figured that out! Thank you! Post that as an answer and I'll accept it. - Nico A

1 Answers

0
votes

The first argument passed to the __call function is the table it is being called on, the table returned from the function in this case. You can use type(a) to get the type of the argument as a string, so you could do something like this:

function defGeneric()
  local result = {
    ['number'] = function(a) return 2*a^2 end,
    ['string'] = function(a) return a.." - test" end
  }
  setmetatable(result, {
    __call = function(t,a)
      local f = t[type(a)]
      if f == nil then return "No handler for type "..type(a) end
    -- alternate:
    -- if f == nil and t['string'] ~= nil then return t['string'](tostring(a)) end

      return f(a)
    end
  })
  return result
end

local def = defGeneric()
print("string: "..tostring(def('sample string')))
print("number: "..tostring(def(5)))
print("table: "..tostring(def({})))
print("boolean: "..tostring(def(1 > 5)))

output

string: sample string - test
number: 50.0
table: No handler for type table
boolean: No handler for type boolean

alternate output

string: sample string - test
number: 50.0
table: table: 0x18537e0 - test
boolean: false - test