简体   繁体   中英

attempt to call method 'print' (a nil value) when implementation OOP in Lua

So, I'm trying to write a simple class in Lua for representing CSV fields:

csv_entry = {}
csv_entry.__index = csv_entry

function csv_entry.create(...)
   return arg
end

function csv_entry:tostring()
   local str = string.char()
   for i,v in ipairs(self) do
      if i < #self then
     str = str .. v
      else
     str = str .. v .. ", "
      end
   end
end

function csv_entry:print()
   print(self:tostring())
end

But when I try to use this class like this:

c = csv_entry.create("Volvo", 10000, "Eric")
c:print() -- line 25

I get the error message

lua: csv.lua:25: attempt to call method 'print' (a nil value)

And I can't really figure out the issue here. What am I doing wrong?

You probably meant to do is this:

function csv_entry.create(...)
   return setmetatable(arg, csv_entry)
end

Your posted version of cvs_entry.create just returns it's arguments packed into a table, so this code:

c = csv_entry.create("Volvo", 10000, "Eric")
c:print()

Is exactly equivalent to this code:

c = {"Volvo", 10000, "Eric"}
c:print()

c doesn't contain a print entry, so c.print returns nil and c:print() fails because you're trying to "call" nil .


Side note: the implicit arg parameter to variadic functions was removed in Lua 5.1 (6 years ago). The correct way to do this now is:

function csv_entry.create(...)
    local arg = {...}
    return setmetatable(arg, csv_entry)
end

Or simply:

function csv_entry.create(...)
   return setmetatable({...}, csv_entry)
end

As long as we're here: you're going to get no output from csv_entry:tostring because it doesn't return anything. Also, if all you're trying to do is to concatenate a bunch of items with comma separators, you can use table.concat :

function csv_entry:tostring()
    return table.concat(self, ', ')
end

I rewrite your code to meet what it is for, it runs OK for me:

csv_entry = {}  

function csv_entry:create(...)
    o = {content = {}}
    self.__index = self;
    setmetatable(o, self)
        for i = 1, arg.n do
            o.content[i] = arg[i];
        end
    return o;
end

function csv_entry:tostring()
    local resStr = ""
    for i, v in pairs(self.content) do
      resStr = resStr .. v;
      if i < #(self.content) then
          resStr = resStr .. ", "
      end
    end
    return resStr;
end

function csv_entry:print()
    print(self:tostring())
end

c = csv_entry:create("Volvo", 10000, "Eric")
c:print()

Like @Mud said, the create(...) in your code is just a regular call and returns all arguments from ..., if you want csv_entry works like a class, then you have to put codes which set metatable and __index into create(...), and return an instance from csv_entry class

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM