lua-users home
lua-l archive

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


I guess without a table, the arguments can be stored as upvalues. The
following makes an iterator generator that sets its arguments as
upvalues to a closure, with the total number of upvalues as the first
upvalue. (Is there a way to avoid that first upvalue?) Then the
closure receives an unused first argument and a second argument i, and
it returns the i-plus-one-th argument to the original function
(similar to the behavior of the function returned by ipairs). Not sure
if this is more or less efficient memory-wise than using a table.

#include <stdio.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

static int iter_varargs (lua_State * L) {
    lua_Integer i = luaL_checkinteger(L, 2);
    // Get total number of upvalues.
    lua_Integer n = luaL_checkinteger(L, lua_upvalueindex(1));
    if (i + 2 <= n) {
        lua_pushinteger(L, i + 1);
        // Return the i-th argument to gen_iter_varargs.
        lua_pushvalue(L, lua_upvalueindex(i + 2));
        return 2;
    } else {
        return 0;
    }
}

static int gen_iter_varargs (lua_State * L) {
    int n = lua_gettop(L);
    if (n + 1 > 255) { // maximum number of upvalues
        return luaL_error(L, "too many arguments");
    }
    // Insert total number of upvalues as first upvalue.
    lua_pushinteger(L, (lua_Integer) n + 1);
    lua_insert(L, 1);
    // Set arguments as remaining upvalues.
    lua_pushcclosure(L, iter_varargs, n + 1);
    lua_pushnil(L);
    lua_pushinteger(L, 0);
    return 3;
}

int main (int argc, char * * argv) {
    lua_State * L = luaL_newstate();
    luaL_openlibs(L);
    lua_register(L, "args", gen_iter_varargs);

    if (luaL_dostring(L, "for i, val in args(1, 2, 3) do print(val * val) end"))
        printf("failed to execute script\n");

    lua_close(L);

    return 0;
}

— Gabriel

On Thu, Dec 20, 2018 at 12:12 AM Dirk Laurie <dirk.laurie@gmail.com> wrote:
>
> The task is to write an iterator factory 'args' so that
>
>    for k,v in args(...) do --[[whatever]] end
>
> does the same as
>
>    for k=1,select('#',...) do local v = select(k,...) --[[whatever]] end
>
> It is quite easy by using a table, of course:
>
> ~~~~
> args = function(...)
>   local t = table.pack(...)
>   local n = t.n
>   local i = 0
>   return function()
>     i=i+1
>     if i<=n then return i,t[i] end
>   end
> end
> ~~~~
>
> It seems to be quite hard to do, even in the C API, without creating a
> table. In fact, I have so far not succeeded.
>