lua-users home
lua-l archive

Experiments with the C++ resumable functions (a.k.a. the coroutines TS) and Lua API

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


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

AltStyle によって変換されたページ (->オリジナル) /