首页 > 解决方案 > 比较从 Lua 中的 C 函数推送的字符串失败,除非复制到新字符串

问题描述

我正在开发一个程序,该程序在使用 C 和 C++ 混合编写的 SDL 窗口中创建 Lua 5.1 环境。我已经建立了一个事件系统,它对 SDL 事件进行排队,以便 Lua 可以将事件从队列中弹出。键入可打印字符时发送事件之一。当检测到事件时,它将字符串推入一个新的lua_State,将其排入队列std::queue,一旦它被拉出,将lua_Stateget 中的值复制到主协程状态,然后由coroutine.yield. 但是当我将字符串与 Lua 端的常量(例如:)ev[2] == "q"进行比较时,比较结果为假。如果我将该值复制到一个新字符串并进行比较(例如:)"" .. ev[2] == "q",则比较结果为真。

我尝试使用多种推送字符串的方式(因为 SDL 提供 UTF-8 字符串而不是 ASCII 字符),包括:

但这些都没有解决问题。

这是我的 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) 中正确处理字符串比较。

标签: c++clua

解决方案


在按照 Egor Skriptunoff 的建议替换luaL_newstatelua_newthread,以及将lua_close呼叫替换为 后lua_pop,我能够解决问题。lua-users 邮件列表上的这篇文章说你不能关闭一个新线程:

格雷厄姆韦克菲尔德写道:

你好,

我有一些难以理解的行为;我使用 lua_newthread 创建新线程,并定期从 C++ 中对它们进行 lua_resume。但是,我可能希望在某个线程完成之前终止它;我尝试在线程的 lua_State 上调用 lua_close(),

你不能在线程上调用 lua_close();仅在主要状态。

最终导致分段错误。由于否则我会使用过多的内存,因此我最终替换lua_close为,lua_pop因为状态被推送到主堆栈上。应用这些修复后,我不再遇到任何段错误,并且内存使用量保持不变。


推荐阅读