coroutine¶
resumable/yielding functions from Lua
A coroutine is a reference to a function in Lua that can be called multiple times to yield a specific result. It is a cooperative function. It is run on the lua_State that was used to create it (see thread for an example on how to get a coroutine that runs on a stack space separate from your usual “main” stack space lua_State).
The coroutine object is entirely similar to the protected_function object, with additional member functions to check if a coroutine has yielded (call_status::yielded) and is thus runnable again, whether it has completed (call_status::ok) and thus cannot yield anymore values, or whether it has suffered an error (see status()’s and call_status’s error codes).
For example, you can work with a coroutine like this:
1function loop()
2 while counter ~= 30
3 do
4 coroutine.yield(counter);
5 counter = counter + 1;
6 end
7 return counter
8end
This is a function that yields. We set the counter value in C++, and then use the coroutine to get a few values:
1#define SOL_ALL_SAFTIES_ON 1
2#include <sol/sol.hpp>
3
4#include <iostream>
5
6int main() {
7 sol::state lua;
8 lua.open_libraries(sol::lib::base, sol::lib::coroutine);
9 lua.script_file("co.lua");
10 sol::coroutine loop_coroutine = lua["loop"];
11 // set counter variable in C++
12 // (can set it to something else to
13 // have loop_coroutine() yield different values)
14 lua["counter"] = 20;
15
16 // example of using and re-using coroutine
17 // you do not have to use coroutines in a loop,
18 // this is just the example
19
20 // we start from 0;
21 // we want 10 values, and we only want to
22 // run if the coroutine "loop_coroutine" is valid
23 for (int counter = 0; counter < 10 && loop_coroutine;
24 ++counter) {
25 // Alternative: counter < 10 && cr.valid()
26
27 // Call the coroutine, does the computation and then
28 // suspends once it returns, we get the value back from
29 // the return and then can use it we can either leave
30 // the coroutine like that can come to it later, or
31 // loop back around
32 int value = loop_coroutine();
33 std::cout << "In C++: " << value << std::endl;
34 }
35
36 return 0;
37}
Note that this code doesn’t check for errors: to do so, you can call the function and assign it as auto result = loop_coroutine();, then check result.valid() as is the case with protected_function.
Finally, you can run this coroutine on another stack space (NOT a different computer thread: Lua uses the term ‘thread’ a bit strangely, as we follow its usage of the term, but it is NOT a separate thread) by doing the following:
1#define SOL_ALL_SAFTIES_ON 1
2#include <sol/sol.hpp>
3
4#include <iostream>
5
6int main() {
7 sol::state lua;
8 lua.open_libraries(sol::lib::base, sol::lib::coroutine);
9 lua.script_file("co.lua");
10 sol::thread runner = sol::thread::create(lua.lua_state());
11 sol::state_view runnerstate = runner.state();
12 sol::coroutine loop_coroutine = runnerstate["loop"];
13 lua["counter"] = 20;
14
15 for (int counter = 0; counter < 10 && loop_coroutine;
16 ++counter) {
17 // Call the coroutine, does the computation and then
18 // suspends
19 int value = loop_coroutine();
20 std::cout << "value is " << value << std::endl;
21 }
22
23 return 0;
24}
The following are the members of sol::coroutine:
members¶
coroutine(lua_State* L, int index = -1);
Grabs the coroutine at the specified index given a lua_State*.
call_status status() const noexcept;
Returns the status of a coroutine.
bool error() const noexcept;
Checks if an error occured when the coroutine was run.
bool runnable () const noexcept;
explicit operator bool() const noexcept;
These functions allow you to check if a coroutine can still be called (has more values to yield and has not errored). If you have a coroutine object coroutine my_co = /*...*/, you can either check runnable() or do if ( my_co ) { /* use coroutine */ }.
template<typename... Args>
protected_function_result operator()( Args&&... args );
template<typename... Ret, typename... Args>
decltype(auto) call( Args&&... args );
template<typename... Ret, typename... Args>
decltype(auto) operator()( types<Ret...>, Args&&... args );
Calls the coroutine. The second operator() lets you specify the templated return types using the my_co(sol::types<int, std::string>, ...) syntax. Check status() afterwards for more information about the success of the run or just check the coroutine object in an ifs tatement, as shown above.