c++ - 比较从 Lua 中的 C 函数推送的字符串失败,除非复制到新字符串
问题描述
我正在开发一个程序,该程序在使用 C 和 C++ 混合编写的 SDL 窗口中创建 Lua 5.1 环境。我已经建立了一个事件系统,它对 SDL 事件进行排队,以便 Lua 可以将事件从队列中弹出。键入可打印字符时发送事件之一。当检测到事件时,它将字符串推入一个新的lua_State
,将其排入队列std::queue
,一旦它被拉出,将lua_State
get 中的值复制到主协程状态,然后由coroutine.yield
. 但是当我将字符串与 Lua 端的常量(例如:)ev[2] == "q"
进行比较时,比较结果为假。如果我将该值复制到一个新字符串并进行比较(例如:)"" .. ev[2] == "q"
,则比较结果为真。
我尝试使用多种推送字符串的方式(因为 SDL 提供 UTF-8 字符串而不是 ASCII 字符),包括:
lua_pushstring(L, e.text.text)
插入整个字符串lua_pushlstring(L, e.text.text, 1)
插入第一个字符char tmp[2]; tmp[0] = e.text.text[0]; tmp[1] = 0; lua_pushstring(L, tmp)
复制字符串并插入
但这些都没有解决问题。
这是我的 C++ 代码中的基本结构:
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;
}
这是我用来测试的 Lua 脚本:
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
我希望assert
脚本中的所有 s 都会导致 true(假设按下了“q”),但其中一些会导致 false。我不得不调整带有中断的语句以仅使用第一个字符。为什么没有正确比较字符串?
编辑:我不是在尝试比较 C++ 端的字符串,而是在 Lua 端。我在 C 代码 (strcmp) 中正确处理字符串比较。
解决方案
在按照 Egor Skriptunoff 的建议替换luaL_newstate
为lua_newthread
,以及将lua_close
呼叫替换为 后lua_pop
,我能够解决问题。lua-users 邮件列表上的这篇文章说你不能关闭一个新线程:
格雷厄姆韦克菲尔德写道:
你好,
我有一些难以理解的行为;我使用 lua_newthread 创建新线程,并定期从 C++ 中对它们进行 lua_resume。但是,我可能希望在某个线程完成之前终止它;我尝试在线程的 lua_State 上调用 lua_close(),
你不能在线程上调用 lua_close();仅在主要状态。
最终导致分段错误。由于否则我会使用过多的内存,因此我最终替换lua_close
为,lua_pop
因为状态被推送到主堆栈上。应用这些修复后,我不再遇到任何段错误,并且内存使用量保持不变。
推荐阅读
- java - 如何在一个事务期间在 REST API 中正确删除、更新和创建
- maven - Springboot maven war 适用于本地 weblogic 12.2.1.2 但不适用于其他系统
- .net - 使用路由更改页面名称和扩展名
- javascript - 过滤器函数:如何在 Objects JavaScript 中搜索多个键
- regex - 用于匹配特定日志模式的正则表达式
- r - 寻找一个函数来根据 R 中其他值的比率来估算缺失值。(寻找基于概率的)
- javascript - 类型错误:react__WEBPACK_IMPORTED_MODULE_6___default.a.useState 不是函数
- bash - 使用 Shell 将文本文件打印到标准输出以通过数据处理管道的最有效方法
- python - 如何激活 pyenv-venv 环境?
- c++ - 在 C++ 中局部 unordered_map 变量是线程安全的