[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Experiments with the C++ resumable functions (a.k.a. the coroutines TS) and Lua API
- From: nerditation <nerditation@...>
- Date: Sun, 10 May 2020 19:42:46 +0800
Hello lua-l,
I was learning the coroutines TS (which should be coming to C++20) the other day,
and realized it can be used to implement a yieldable/resumable `lua_CFunction`
without having to manually transform the code into CPS style.
as an exercise I translated the sieve[1] demo code from Lua into C++, and here's
some snippets of what I've come up with:
-------------------- code of C++ ---------------------
/**
-- generate all the numbers from 2 to n
function gen (n)
return coroutine.wrap(function ()
for i=2,n do coroutine.yield(i) end
end)
end
*/
static int gen(lua_State *L) {
auto n = luaL_checkinteger(L, 1);
lua_getglobal(L, "coroutine");
lua_getfield(L, -1, "wrap");
mylua_pushresumable(L, [n](lua_State *L) -> mylua_ResumableHandle {
for (int i = 2; i < n; ++i) {
lua_pushinteger(L, i);
// same as: co_await mylua_yield(L, 1);
co_yield 1;
}
co_return 0;
});
lua_call(L, 1, 1);
return 1;
}
/**
-- filter the numbers generated by `g', removing multiples of `p'
function filter (p, g)
return coroutine.wrap(function ()
for n in g do
if n%p ~= 0 then coroutine.yield(n) end
end
end)
end
*/
static int filter(lua_State *L) {
auto p = luaL_checkinteger(L, 1);
luaL_checktype(L, 2, LUA_TFUNCTION);
// the C++ lambda can't capture Lua value directly
auto g = luaL_ref(L, LUA_REGISTRYINDEX);
lua_getglobal(L, "coroutine");
lua_getfield(L, -1, "wrap");
mylua_pushresumable(L, [p, g](lua_State *L) -> mylua_ResumableHandle {
// repeatedly call the generator till it return nil
for (;;) {
lua_rawgeti(L, LUA_REGISTRYINDEX, g);
co_await mylua_call(L, 0, 1);
if (lua_isnil(L, -1)) {
break;
}
auto n = lua_tointeger(L, -1);
if ((n % p) != 0) {
co_yield 1;
} else {
lua_pop(L, 1);
}
}
luaL_unref(L, LUA_REGISTRYINDEX, g);
co_return 0;
});
lua_call(L, 1, 1);
return 1;
}
int main() {
auto L = luaL_newstate();
luaL_openlibs(L);
// register metatables for types
mylua_init(L);
lua_pushcfunction(L, &gen);
lua_setglobal(L, "gen");
lua_pushcfunction(L, &filter);
lua_setglobal(L, "filter");
luaL_dostring(L, R"(
N=N or 500 -- from command line
x = gen(N) -- generate primes up to N
while 1 do
local n = x() -- pick a number until done
if n == nil then break end
print(n) -- must be a prime number
x = filter(n, x) -- now remove its multiples
end
)");
return 0;
}
---------------------- end of C++ -----------------------
the code (full listing at [2]) is tested using Visual Studio 2019 and GCC 10.
actually I have implemented it in two ways. the snippets shown here
is implemented using the C++ `std::function<>` so that I can use
lambdas with captured state (i.e. closures generated by C++ compilers).
the other implementation accepts a plain function pointer so I have to
utilize the upvalues associated with the `lua_CFunction`.
disclaimer: this is just a quick experiment to help myself understanding the
C++ coroutines concepts. personally I have not used `lua_yieldk()`,
`lua_callk()` or `lua_pcallk()` very much in the past. I just feel it
might interest some people, so I commented the code a little bit and
want to share the results with you. just be warned the code surly contains
many errors.
cheers.
[1] https://www.lua.org/cgi-bin/demo?sieve
[2] https://gist.github.com/nerditation/441a2d4409a778ae77683fc0645b69de
_______________________________________________
lua-l mailing list -- lua-l@lists.lua.org
To unsubscribe send an email to lua-l-leave@lists.lua.org