Johannes
Johannes

Reputation: 6429

In Lua, can you get an object name from 'self'?

I'm probably missing something very simple, but when calling a method on an object in Lua, is there a way to get the name of that object from the self variable?

To illustrate what I mean, here is a somewhat contrived example - a simple Stack implementation:

Stack = {}
function Stack:new (...)
    instance = {}
    instance.elements = {...}
    setmetatable(instance, self)
    self.__index = self
    return instance
end
function Stack:push (...)
    for i,v in pairs({...}) do
        table.insert(self.elements,v)
    end
end
function Stack:pop ()
    if #self.elements > 0 then
        return table.remove(self.elements)
    else
        error("Cannot pop, Stack is empty")
    end
end

my_stack = Stack:new(2,4)
my_stack:push("dog")      -- my_stack.elements = {2,4,"dog"}
print(my_stack:pop())     -- "dog"
print(my_stack:pop())     -- "4"
print(my_stack:pop())     -- "2"
print(my_stack:pop())     -- error: "Cannot pop, Stack is empty"

Is it possible to use the self variable in the Stack:pop error message to output the name of the object it's being called from? I'd like the error message to say "Cannot pop, Stack 'my_stack' is empty".

Upvotes: 3

Views: 3959

Answers (2)

Egor Skriptunoff
Egor Skriptunoff

Reputation: 23757

local function get_my_self_name(self)
   local i = debug.getinfo(3, 'Sl');
   return "'"..(io.open(i.source:match'@(.*)'):read'*a'
      :gsub('.-\n','',i.currentline-1)
      :match('(.-)[:.]%s*'..debug.getinfo(2,'n').name..'%s*%(')
      :match'([%w_]+)%s*$' or '<unnamed>').."' at line #"..i.currentline
end
local Stack = {}
function Stack:new (...)
   local instance = {}
   instance.elements = {...}
   setmetatable(instance, self)
   self.__index = self
   return instance
end
function Stack:push (...)
   for i,v in ipairs({...}) do
      table.insert(self.elements,v)
   end
end
function Stack:pop ()
   if #self.elements > 0 then
      return table.remove(self.elements)
   else
      error("Can't pop, Stack "..get_my_self_name(self).." is empty")
   end
end

local my_stack = Stack:new(2,4)
my_stack:push("dog")  --my_stack.elements = {2,4,"dog"}
print(my_stack:pop()) --"dog"
print(my_stack:pop()) --"4"
print(my_stack:pop()) --"2"
print(my_stack:pop()) --error: Can't pop, Stack 'my_stack' at line #35 is empty

Upvotes: 4

Oliver
Oliver

Reputation: 29571

is there a way to get the name of that object from the self variable?

Objects in Lua don't have names. Only references have names, and variables and table fields are ultimately just references to objects. So if you are asking how to get the name of the reference, clearly you can't, since there could be any number of references with different names, which one would the "self.getName()" return?

You could give each object a unique identifier, say at construction time. Then one of the arguments to new() would be the object name, you would save it as self.name (for example) and then you could access it as required (I would imagine, for log messages or lookup within some associative array). So you would do

my_stack = Stack:new("my_stack", 2,4)
my_stack:push("dog")      -- my_stack.elements = {2,4,"dog"}

I am not aware of a way to make new() figure out that the new instance is going to be assigned to a variable called my_stack. In fact I suspect it is not possible not even with the debug module because the assignment will only take place once the new has returned. So there is nothing forcing the name of the object to be the same as the variable, it's entirely up to you. It's not even something you want because, again, you could have multiple references to same stack object:

a = Stack:new("a", 2,4)
b = a  -- b and a are the same object in memory

Should the error message mention a or b? I think it is better for you to assign an id to the object and then you always know which object is being talked about in an error message:

a = Stack:new("my_stack_1", 2,4)
b = a
t = { b = { c = a } }
a:push("dog") -- error will mention my_stack_1: clear which object you are referring to
t.b.c:push("dog") -- error will mention my_stack_1: still clear which object you are referring to

If you are worried that from the error message, you can't determine which line calls new(self, "dog"), that's where the debug module could come in handy: with it you can get the line number and file where the call is made, so you could say "object my_stack_1:pop(): no elements left in stack (called from line X of file Y)"

Upvotes: 5

Related Questions