Related
I'm working on a program that creates a Lua 5.1 environment in an SDL window written with a mix of C and C++. I've set up an event system that queues SDL events so that Lua can pop events off the queue. One of the events is sent when a printable character is typed. When the event is detected it pushes the string into a new lua_State, queues it in an std::queue, and once it's pulled the values in the lua_State get copied into the main coroutine state, where they're then returned by coroutine.yield. But when I compare the string with a constant on the Lua side (ex: ev[2] == "q"), the comparison results in false. If I copy the value to a new string and compare that (ex: "" .. ev[2] == "q"), the comparison results in true.
I've tried using multiple ways of pushing the string (since SDL provides a UTF-8 string instead of an ASCII character), including:
lua_pushstring(L, e.text.text)
to insert the entire string
lua_pushlstring(L, e.text.text, 1)
to insert the first character
char tmp[2]; tmp[0] = e.text.text[0]; tmp[1] = 0; lua_pushstring(L, tmp)
to copy the string and insert that
but none of these fixed the issue.
Here's the basic structure in my C++ code:
const char * termGetEvent(lua_State *L) {
SDL_Event e;
if (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT) return "die";
else if (e.type == SDL_KEYDOWN && keymap.find(e.key.keysym.scancode) != keymap.end()) {
lua_pushinteger(L, keymap.at(e.key.keysym.scancode));
lua_pushboolean(L, false);
return "key";
} else if (e.type == SDL_KEYUP && keymap.find(e.key.keysym.scancode) != keymap.end()) {
lua_pushinteger(L, keymap.at(e.key.keysym.scancode));
return "key_up";
} else if (e.type == SDL_TEXTINPUT) { // this is the section producing errors
char tmp[2];
tmp[0] = e.text.text[0];
tmp[1] = 0;
lua_pushstring(L, tmp);
return "char";
} else if (e.type == SDL_MOUSEBUTTONDOWN) {
lua_pushinteger(L, buttonConvert(e.button.button));
lua_pushinteger(L, convertX(e.button.x));
lua_pushinteger(L, convertY(e.button.y));
return "mouse_click";
} else if (e.type == SDL_MOUSEBUTTONUP) {
lua_pushinteger(L, buttonConvert(e.button.button));
lua_pushinteger(L, convertX(e.button.x));
lua_pushinteger(L, convertY(e.button.y));
return "mouse_up";
} else if (e.type == SDL_MOUSEWHEEL) {
int x = 0, y = 0;
term->getMouse(&x, &y);
lua_pushinteger(L, e.button.y);
lua_pushinteger(L, convertX(x));
lua_pushinteger(L, convertY(y));
return "mouse_scroll";
} else if (e.type == SDL_MOUSEMOTION && e.motion.state) {
lua_pushinteger(L, buttonConvert2(e.motion.state));
lua_pushinteger(L, convertX(e.motion.x));
lua_pushinteger(L, convertY(e.motion.y));
return "mouse_drag";
} else if (e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_RESIZED) {
term->resize();
}
}
return NULL;
}
std::queue<std::pair<const char *, lua_State*> > eventQueue;
int getNextEvent(lua_State *L, const char * filter) {
std::pair<const char *, lua_State*> ev;
do {
while (eventQueue.size() == 0) {
lua_State *param = luaL_newstate();
if (!lua_checkstack(param, 4)) printf("Could not allocate event\n");
const char * name = termGetEvent(param);
if (name != NULL) {
if (strcmp(name, "die") == 0) running = 0;
eventQueue.push(std::make_pair(name, param));
} else if (param) {
lua_pushnil(param);
lua_close(param);
param = NULL;
}
}
ev = eventQueue.front();
eventQueue.pop();
} while (strlen(filter) > 0 && strcmp(std::get<0>(ev), filter) != 0);
// Copy the next event in
lua_State *param = std::get<1>(ev);
int count = lua_gettop(param);
if (!lua_checkstack(L, count + 1)) printf("Could not allocate\n");
lua_pushstring(L, std::get<0>(ev));
lua_xmove(param, L, count);
//lua_close(param);
return count + 1;
}
lua_State *L;
int main() {
int status, result, i;
double sum;
lua_State *coro;
start:
/*
* All Lua contexts are held in this structure. We work with it almost
* all the time.
*/
L = luaL_newstate();
coro = lua_newthread(L);
luaL_openlibs(coro); /* Load Lua libraries */
termInit(); // initializes SDL
/* Load the file containing the script we are going to run */
status = luaL_loadfile(coro, bios_path);
if (status) {
/* If something went wrong, error message is at the top of */
/* the stack */
fprintf(stderr, "Couldn't load file: %s\n", lua_tostring(L, -1));
exit(1);
}
tid = createThread(&termRenderLoop); // stops when running != 1
/* Ask Lua to run our little script */
status = LUA_YIELD;
int narg = 0;
while (status == LUA_YIELD && running == 1) {
status = lua_resume(coro, narg);
if (status == LUA_YIELD) {
if (lua_isstring(coro, -1)) narg = getNextEvent(coro, lua_tostring(coro, -1));
else narg = getNextEvent(coro, "");
} else if (status != 0) {
running = 0;
joinThread(tid);
//usleep(5000000);
termClose();
printf("%s\n", lua_tostring(coro, -1));
lua_close(L);
exit(1);
}
}
joinThread(tid);
termClose();
lua_close(L); /* Cya, Lua */
if (running == 2) {
//usleep(1000000);
goto start;
}
return 0;
}
Here's the Lua script I'm using to test:
while true do
local ev = {coroutine.yield()}
print(table.unpack(ev)) -- provided by previous scripts
if ev[1] == "char" then
print("\"" .. ev[2] .. "\"") -- prints "q" if 'q' was pressed
assert(ev[2] == "q") -- false, even if 'q' was pressed
assert(string.len(ev[2]) == 1) -- true
assert(#ev[2] == 1) -- true
assert(string.len(string.sub(ev[2], 2, 2)) == 0) -- true
assert(string.sub(ev[2], 1, 1) == ev[2]) -- false
assert("" .. ev[2] == "q") -- true if 'q' was pressed
end
if ev[1] == "char" and string.sub(ev[2], 1, 1) == "q" then break end
end
I expect all of the asserts in the script to result in true (assuming 'q' was pressed), but some of them result in false. I had to adjust the statement with the break in it to use only the first character. Why is the string not being compared correctly?
EDIT: I'm not trying to compare the strings on the C++ side, but on the Lua side. I handle string comparisons properly in the C code (strcmp).
After following Egor Skriptunoff's suggestion to replace luaL_newstate with lua_newthread, as well as replacing the lua_close calls with lua_pop, I was able to fix the problem. This post on the lua-users mailing list says you can't close a new thread:
Graham Wakefield wrote:
Hi,
I'm having some hard to understand behavior; I create new threads using lua_newthread, and lua_resume them periodically from C++. However, I may wish to at some point terminate a thread before it has completed; I tried calling lua_close() on the thread's lua_State,
You can't call lua_close() on a thread; only on the main state.
which ended up causing a segmentation fault. Since I was getting excessive memory use otherwise, I ended up replacing lua_close with lua_pop because the state is pushed onto the main stack. After applying these fixes I no longer got any segfaults and the memory usage stays constant.
I'm trying to get userdata from a Lua script(chunk A) in C++(through a returned variable from function in my example) and then, later pass this userdata back to Lua script(chunk B) from C++(through a function argument in my example) so the userdata can be used in chunk B as it was in the chunk A.
MyBindings.h
class Vec2
{
public:
Vec2():x(0), y(0){};
Vec2(float x, float y):x(x), y(y){};
float x, y;
};
MyBindings.i
%module my
%{
#include "MyBindings.h"
%}
%include "MyBindings.h"
main.cpp
#include <iostream>
#include <lua.hpp>
extern "C"
{
int luaopen_my(lua_State *L);
}
int main()
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
luaopen_my(L);
lua_settop(L, 0);
/* chunk A */
luaL_dostring(L, "local vec2 = my.Vec2(3, 4)\n"
"function setup()\n"
"return vec2\n"
"end\n");
/* chunk B */
luaL_dostring(L, "function test(p)\n"
"print(p.x)\n"
"end\n");
void *userDataPtr = nullptr;
/* call setup function */
int top = lua_gettop(L);
lua_getglobal(L, "setup");
if (lua_pcall(L, 0, LUA_MULTRET, 0))
{
std::cout << lua_tostring(L, -1) << '\n';
lua_pop(L, 1);
}
/* check the return value */
if (lua_gettop(L) - top)
{
/* store userdata to a pointer */
if (lua_isuserdata(L, -1))
userDataPtr = lua_touserdata(L, -1);
}
/* check if userDataPtr is valid */
if (userDataPtr != nullptr)
{
/* call test function */
lua_getglobal(L, "test");
lua_pushlightuserdata(L, userDataPtr); /* pass userdata as an argument */
if (lua_pcall(L, 1, 0, 0))
{
std::cout << lua_tostring(L, -1) << '\n';
lua_pop(L, 1);
}
}
lua_close(L);
}
The Result I get :
[string "local vec2 = my.Vec2(3, 4)..."]:6: attempt to index a
userdata value (local 'p')
The Result I expect :
3
Is it possible to get userdata from chunk A and then pass this to chunk B so it can be used like it was in chunk A?
You're losing all information about the object's type when you get raw pointer to userdata's data and pushing it to arguments as lightuserdata. The lightuserdata even has no individual metatables.
The correct way would be to pass the Lua value as it is. Leave the original returned value on Lua stack, or copy it into other Lua container (your Lua table for temporaries, or Lua registry), then copy that value on Lua stack to pass it as an argument. That way you don't have to know anything about binding implementation. You don't even have to care if that's a userdata or any other Lua type.
Based on your code, this might look like this:
#include <iostream>
#include <lua.hpp>
extern "C"
{
int luaopen_my(lua_State *L);
}
int main()
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
/* chunk A */
luaL_dostring(L, "local vec2 = {x=3, y=4}\n"
"function setup()\n"
"return vec2\n"
"end\n");
/* chunk B */
luaL_dostring(L, "function test(p)\n"
"print(p.x)\n"
"end\n");
/* call setup function */
int top = lua_gettop(L);
lua_getglobal(L, "setup");
if (lua_pcall(L, 0, LUA_MULTRET, 0))
{
std::cout << lua_tostring(L, -1) << '\n';
lua_pop(L, 1);
exit(EXIT_FAILURE); // simpy fail for demo
}
/* check the return value */
if (lua_gettop(L) - top)
{
// the top now contains the value returned from setup()
/* call test function */
lua_getglobal(L, "test");
// copy the original value as argument
lua_pushvalue(L, -2);
if (lua_pcall(L, 1, 0, 0))
{
std::cout << lua_tostring(L, -1) << '\n';
lua_pop(L, 1);
exit(EXIT_FAILURE);
}
// drop the original value
lua_pop(L, 1);
}else
{
// nothing is returned, nothing to do
}
lua_close(L);
}
In addition to the other answer I would like to show a variant where you store the a reference to the value in the Lua registry. The advantage of this approach is that you don't have to keep the value on the stack and think about what the offset will be. See also 27.3.2 – References in “Programming in Lua”.
This approach uses three functions:
int luaL_ref (lua_State *L, int t);
Pops from the stack the uppermost value, stores it into the table at index t and returns the index the value has in that table. Hence to save a value in the registry we use
userDataRef = luaL_ref(L, LUA_REGISTRYINDEX);
int lua_rawgeti (lua_State *L, int index, lua_Integer n);
Pushes onto the stack the value of the element n of the table at index (t[n] in Lua). Hence to retrieve a value at index userDataRef from the registry we use
lua_rawgeti(L, LUA_REGISTRYINDEX, userDataRef);
void luaL_unref (lua_State *L, int t, int ref);
Removes the reference stored at index ref in the table at t such that the reference can be garbage collected and the index ref can be reused. Hence to remove the reference userDataRef from the registry we use
luaL_unref(L, LUA_REGISTRYINDEX, userDataRef);
#include <iostream>
#include <lua.hpp>
extern "C" {
int luaopen_my(lua_State *L);
}
int main() {
lua_State *L = luaL_newstate();
luaL_openlibs(L);
luaopen_my(L);
lua_settop(L, 0);
/* chunk A */
luaL_dostring(L, "local vec2 = my.Vec2(3, 4)\n"
"function setup()\n"
"return vec2\n"
"end\n");
/* chunk B */
luaL_dostring(L, "function test(p)\n"
"print(p.x)\n"
"end\n");
int userDataRef = LUA_NOREF;
/* call setup function */
int top = lua_gettop(L);
lua_getglobal(L, "setup");
if (lua_pcall(L, 0, LUA_MULTRET, 0)) {
std::cout << lua_tostring(L, -1) << '\n';
lua_pop(L, 1);
}
/* check the return value */
if (lua_gettop(L) - top) {
/* store userdata to a pointer */
userDataRef = luaL_ref(L, LUA_REGISTRYINDEX);
}
/* check if userDataRef is valid */
if (userDataRef != LUA_NOREF && userDataRef != LUA_REFNIL) {
/* call test function */
lua_getglobal(L, "test");
lua_rawgeti(L, LUA_REGISTRYINDEX, userDataRef);
/* free the registry slot (if you are done) */
luaL_unref(L, LUA_REGISTRYINDEX, userDataRef);
if (lua_pcall(L, 1, 0, 0)) {
std::cout << lua_tostring(L, -1) << '\n';
lua_pop(L, 1);
}
}
lua_close(L);
}
Maybe you want to check out the Sol2 wrapper for the Lua-C-API. It can do exactly what you want with minimal boilerplate. However, it requires C++14.
#include <iostream>
#define SOL_CHECK_ARGUMENTS 1
#include <sol.hpp>
extern "C" int luaopen_my(lua_State *L);
int main() {
sol::state L;
L.open_libraries();
luaopen_my(L);
/* chunk A */
L.script("local vec2 = my.Vec2(3, 4)\n"
"function setup()\n"
"return vec2\n"
"end\n");
/* chunk B */
L.script("function test(p)\n"
"print(p.x)\n"
"end\n");
auto userDataRef = L["setup"]();
L["test"](userDataRef);
}
I'm trying to call A:update(x) and get a returned value x + 3 in C++.
Here's my code:
#include <lua.hpp>
void main()
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
lua_settop(L, 0);
luaL_dostring(L, "package.preload['A'] = function () local A = {}\n"
"function A:update(x) return x + 3 end \n"
"return A end");
//call function
lua_getglobal(L, "require");
lua_pushstring(L, "A");
if (lua_pcall(L, 1, LUA_MULTRET, 0) != 0) {
std::cerr << "lua:" << lua_tostring(L, 1) << '\n';
lua_pop(L, 1);
}
int top = lua_gettop(L);
lua_getfield(L, -1, "update");
if (!lua_isfunction(L, -1))
{
std::cerr << "lua:" << lua_tostring(L, 1) << '\n';
lua_pop(L, 1);
}
lua_pushnumber(L, 5); //pass the argument 5
if (lua_pcall(L, 1, LUA_MULTRET, 0))
{
std::cerr << "lua:" << lua_tostring(L, 1) << '\n';
lua_pop(L, 1);
}
if (lua_gettop(L) - top)
{
if (lua_isnumber(L, -1))
{
std::cout << "RETURNED : " << lua_tonumber(L, -1) << std::endl;
}
}
lua_pop(L, 1); // pop 'update'
lua_pop(L, 1); // pop 'A'
lua_close(L);
}
I expect it to print RETURNED : 8 but I get the following error:
Thread 1:EXC_BAD_ACCESS (code=1, address=0x0)
How should I correct my code to work?
EDITED: It worked as soon as I change A:update(x) to A.update(x). I thought they work identically except I can use self in a function that uses :. Could someone please explain to me why this happens?
The notation A:update(x) is syntactic sugar for A.update(A,x). That means you have to call the function update with two parameters. You are lacking the first of the two parameters.
The first parameter A is already on the stack but is located “below” the update function. Using lua_pushvalue we can push a copy of the table onto the stack.
Thus you have to call the function like this (omitting the error handling bits)
lua_getfield(L, -1, "update");
lua_pushvalue(L, -2); // push a copy of "A" onto the stack
lua_pushnumber(L, 5); //pass the argument 5
lua_pcall(L, 2, LUA_MULTRET, 0);
I have 2 scripts, each are in a different lua_State.
I am trying to be able to get a variable from one state and use it in the other.
My code below works for single variables and unidirectional arrays. Could I get some guidance on also making it work for multidimensional arrays?
void getValues(lua_State* L1, lua_State* L2, int& returns)
{
if (lua_isuserdata(L1, -1))
{
LuaElement* e = Luna<LuaElement>::to_object(L1, -1);
if (e != NULL)
{
Luna<LuaElement>::push_object(L2, e);
}
}
else if (lua_isstring(L1, -1))
{
lua_pushstring(L2, lua_tostring(L1, -1));
}
else if (lua_isnumber(L1, -1))
lua_pushnumber(L2, lua_tonumber(L1, -1));
else if (lua_isboolean(L1, -1))
lua_pushboolean(L2, lua_toboolean(L1, -1));
else if (lua_istable(L1, -1))
{
lua_pushnil(L1);
lua_newtable(L2);
while (lua_next(L1, -2))
{
getValues(L1, L2, returns);
lua_rawseti(L2,-2,returns-1);
lua_pop(L1, 1);
}
// lua_rawseti(L2,-2,returns); // this needs work
}
returns++;
}
Unfortunately I'm having a tough time getting the recursion right for this to work for multidimensional arrays.
Solved.
For anyone this might be useful:
void getValues(lua_State* L1, lua_State* L2, int ind)
{
if (lua_type(L1, -1) == LUA_TTABLE)
{
lua_newtable(L2);
lua_pushnil(L1);
ind = 0;
while (lua_next(L1, -2))
{
// push the key
if (lua_type(L1, -2) == LUA_TSTRING)
lua_pushstring(L2, lua_tostring(L1, -2));
else if (lua_type(L1, -2) == LUA_TNUMBER)
lua_pushnumber(L2, lua_tonumber(L1, -2));
else
lua_pushnumber(L2, ind);
getValues(L1, L2, ind++);
lua_pop(L1, 1);
lua_settable(L2, -3);
}
}
else if (lua_type(L1, -1) == LUA_TSTRING)
{
lua_pushstring(L2, lua_tostring(L1, -1));
}
else if (lua_type(L1, -1) == LUA_TNUMBER)
{
lua_pushnumber(L2, lua_tonumber(L1, -1));
}
else if (lua_type(L1, -1) == LUA_TBOOLEAN)
{
lua_pushboolean(L2, lua_toboolean(L1, -1));
}
else if (lua_type(L1, -1) == LUA_TUSERDATA)
{
// replace with your own user data. This is mine
LuaElement* e = Luna<LuaElement>::to_object(L1, -1);
if (e != NULL)
{
Luna<LuaElement>::push_object(L2, e);
}
}
}
Warning: L1 and L2 must be different states.
You can try lua_tinker::table:
I'm trying to get an understanding of how I can use co-routines to "pause" a script and wait until some processing is done before resuming.
Perhaps I'm looking at co-routines in the wrong way. But my attempt is structured similar to the example given in this answer.
The loop in loop.lua never reaches a second iteration, and hence never reaches the i == 4 condition required to exit the running loop in the C code. If I do not yield in loop.lua, then this code performs as expected.
main.cpp
#include <lua/lua.hpp>
bool running = true;
int lua_finish(lua_State *) {
running = false;
printf("lua_finish called\n");
return 0;
}
int lua_sleep(lua_State *L) {
printf("lua_sleep called\n");
return lua_yield(L,0);
}
int main() {
lua_State* L = lua_open();
luaL_openlibs(L);
lua_register(L, "sleep", lua_sleep);
lua_register(L, "finish", lua_finish);
luaL_dofile(L, "scripts/init.lua");
lua_State* cL = lua_newthread(L);
luaL_dofile(cL, "scripts/loop.lua");
while (running) {
int status;
status = lua_resume(cL,0);
if (status == LUA_YIELD) {
printf("loop yielding\n");
} else {
running=false; // you can't try to resume if it didn't yield
// catch any errors below
if (status == LUA_ERRRUN && lua_isstring(cL, -1)) {
printf("isstring: %s\n", lua_tostring(cL, -1));
lua_pop(cL, -1);
}
}
}
luaL_dofile(L, "scripts/end.lua");
lua_close(L);
return 0;
}
loop.lua
print("loop.lua")
local i = 0
while true do
print("lua_loop iteration")
sleep()
i = i + 1
if i == 4 then
break
end
end
finish()
EDIT: Added a bounty, to hopefully get some help on how to accomplish this.
Return code 2 from lua_resume is a LUA_ERRRUN. Check the string on the top of the Lua stack to find the error message.
A similar pattern has worked for me, though I did use coroutine.yield instead of lua_yield and I'm in C rather than C++. I don't see why your solution wouldn't work
On your resume call, it's not clear if you're over simplifying for an example, but I'd make the following changes in your while loop:
int status;
status=lua_resume(cL,0);
if (status == LUA_YIELD) {
printf("loop yielding\n");
}
else {
running=false; // you can't try to resume if it didn't yield
// catch any errors below
if (status == LUA_ERRRUN && lua_isstring(cL, -1)) {
printf("isstring: %s\n", lua_tostring(cL, -1));
lua_pop(cL, -1);
}
}
Edit 2:
For debugging, add the following before you run your resume. You've got a string getting pushed on the stack somewhere:
int status;
// add this debugging code
if (lua_isstring(cL, -1)) {
printf("string on stack: %s\n", lua_tostring(cL, -1));
exit(1);
}
status = lua_resume(cL,0);
Edit 3:
Oh good grief, I can't believe I didn't see this already, you don't want to run luaL_dofile when you're going to yield because you can't yield a pcall directly as best I know, which is what happens in the dofile (5.2 will pass it through, but I think you still need the lua_resume). Switch to this:
luaL_loadfile(cL, "scripts/loop.lua");
Last time i was messing with Lua coroutines I ended with such code
const char *program =
"function hello()\n"
" io.write(\"Hello world 1!\")\n"
" io.write(\"Hello world 2!\")\n"
" io.write(\"Hello world 3!\")\n"
"end\n"
"function hate()\n"
" io.write(\"Hate world 1!\")\n"
" io.write(\"Hate world 2!\")\n"
" io.write(\"Hate world 3!\")\n"
"end\n";
const char raw_program[] =
"function hello()\n"
" io.write(\"Hello World!\")\n"
"end\n"
"\n"
"cos = {}\n"
"\n"
"for i = 0, 1000, 1 do\n"
" cos[i] = coroutine.create(hello)\n"
"end\n"
"\n"
"for i = 0, 1000, 1 do\n"
" coroutine.resume(cos[i])\n"
"end";
int _tmain(int argc, _TCHAR* argv[])
{
lua_State *L = lua_open();
lua_State *Lt[1000];
global_State *g = G(L);
printf("Lua memory usage after open: %d\n", g->totalbytes);
luaL_openlibs(L);
printf("Lua memory usage after openlibs: %d\n", g->totalbytes);
lua_checkstack(L, 2048);
printf("Lua memory usage after checkstack: %d\n", g->totalbytes);
//lua_load(L, my_lua_Reader, (void *)program, "code");
luaL_loadbuffer(L, program, strlen(program), "line");
printf("Lua memory usage after loadbuffer: %d\n", g->totalbytes);
int error = lua_pcall(L, 0, 0, 0);
if (error) {
fprintf(stderr, "%s", lua_tostring(L, -1));
lua_pop(L, 1);
}
printf("Lua memory usage after pcall: %d\n", g->totalbytes);
for (int i = 0; i < 1000; i++) {
Lt[i] = lua_newthread(L);
lua_getglobal(Lt[i], i % 2 ? "hello" : "hate");
}
printf("Lua memory usage after creating 1000 threads: %d\n", g->totalbytes);
for (int i = 0; i < 1000; i++) {
lua_resume(Lt[i], 0);
}
printf("Lua memory usage after running 1000 threads: %d\n", g->totalbytes);
lua_close(L);
return 0;
}
It seems you could not load file as coroutine? but use function instead And it should be selected to the top of the stack.
lua_getglobal(Lt[i], i % 2 ? "hello" : "hate");