lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On Mon, Sep 23, 2013 at 12:20 PM, Andrew Starks <andrew.starks@trms.com> wrote:
I have many setter methods that handle properties that represent an
inventory of one or more objects of the same class (to borrow from
OOP).

So that I don't need to write a "set_prop" and "set_props" function, I
write this quite a bit:

local function f(arg, ...)
    if (...) then
        return arg, f(...)
    else
        return arg
    end
end


A more compact variant doesn't work:

local function f2(arg, ...)
    return arg, (...) and f2(...)
end

...for more than two recursions.


It's not a big deal, at all, but if there was some way to get closer
to the second example, and still have it work for n arguments, I'd
like to know it.

:)

-Andrew


In a couple of my apps I've run into a similar problem, when I want to implement callbacks. The idea is, any module can register a callback on a certain event, and every registered callback will be called in order; each function is expected to return its arguments, but could modify them, or return nil to prevent the rest of the functions in the chain from being called. This is used for filtering events.

The only generic way I've found to implement this ends up looking kinda gross:
local function concat_args(args)
    --this function is used to build argument lists for error messages
    --it's similar to table.concat but uses args.n instead of #args
    --and wraps strings in quotes so types are more apparent
    --in the output.
    local str = ''

    for i = 1, args.n do
        local v = args[i]
        if type(v) == 'string' then v = '"' .. v .. '"'
        else v = tostring(v)
        end
        str = str .. v .. ', '
    end

    return str:sub(1, -3) --strip last ', '
end


function call_callbacks(event, ...)
    local args = table.pack(...)
    for i, mdl in ipairs(modules) do
        if mdl[event] then

            local ret = table.pack(xpcall(
                mdl[event], debug.traceback, mdl,
                table.unpack(args, 1, args.n)))

            --table.remove only deals with sequences (1...#t),
            --so we have to do this manually instead.
            local ok = ret[1]
            for i = 1, ret.n do ret[i] = ret[i+1] end
            ret.n = ret.n - 1

            if ok then
                args = ret
                if not ret[1] then break end
            else log.error("Module '%s' error in '%s': %s\n(params: %s)",
                mdl._name, event, ret[1], concat_args(args))
            end
        end
    end
    return table.unpack(args, 1, args.n)
end

Between the varargs and the xpcall it's a bit awkward (and quite a bit of code) to receive an arbitrary number of return values and then pass them on to another function or return them. (It'd be even more awkward in Lua 5.1 where you don't have table.pack; {...} won't suffice as the list can contain nils.)

--
Sent from my Game Boy.