c++lua

Pass NestedStruct from c++ to lua


I'm trying to make a c++ function address mapping with lua, so that I can do hot fix,but I can correctly pass nested struct to lua (I guess, because I print table before call lua function), but I got lua error: attempt to call a nil value. What's wrong?

#include <sol/sol.hpp>
#include <iostream>
#include <string>
#include <unordered_map>

struct NestedData {
    int a;
    float b;
};

struct ComplexData {
    int id;
    std::string name;
    NestedData nested;
};

void stack_struct(lua_State* L, const ComplexData& data) {
    lua_newtable(L); 

    lua_pushstring(L, "id");
    lua_pushinteger(L, data.id);
    lua_settable(L, -3);

    lua_pushstring(L, "name");
    lua_pushstring(L, data.name.c_str());
    lua_settable(L, -3);

    lua_pushstring(L, "nested");
    {
        lua_newtable(L);

        lua_pushstring(L, "a");
        lua_pushinteger(L, data.nested.a);
        lua_settable(L, -3);

        lua_pushstring(L, "b");
        lua_pushnumber(L, data.nested.b);
        lua_settable(L, -3);

        lua_settable(L, -3);
    }
}

int main() {
    lua_State* L = luaL_newstate();
    luaL_openlibs(L);
    std::unordered_map<int64_t, std::function<double(ComplexData*)>> SkillMap;
    std::string script_name = "register.lua";
    std::string func_name = "WaterBall";
    int64_t id = 100;
    if (!L) {
        std::cerr << "Lua interpreter is not initialized!" << std::endl;
        return 0;
    }
    if (luaL_dofile(L, script_name.c_str()) != LUA_OK) {
        std::cerr << "Error loading Lua script: " << lua_tostring(L, -1) << std::endl;
        lua_pop(L, 1);
        return 0;
    }
    lua_getglobal(L, func_name.c_str()); // anonymity function at the top of the stack
    if (lua_isfunction(L, -1)) {
        
        SkillMap[id] = [L, func_name](ComplexData* info) -> double {
            stack_struct(L, *info);
            // print table here, values are correct
            if (lua_pcall(L, 1, 1, 0) != LUA_OK) {
                std::cerr << "Error calling Lua function: " << lua_tostring(L, -1) << std::endl;
                lua_pop(L, 1);
                return 0.0;
            }
            double result = 0.0;
            if (lua_isnumber(L, -1)) {
                result = lua_tonumber(L, -1);
            }
            else {
                std::cerr << "Lua function did not return a number!" << std::endl;
            }
            lua_pop(L, 1);
            return result;
            };
        std::cout << "Lua function " << func_name << " registered successfully!" << std::endl;
    }
    else {
        std::cerr << "Function " << func_name << " not found or not callable in Lua" << std::endl;
    }
    lua_pop(L, 1);

    ComplexData data = { 1, "example", {10, 3.14f} };

    auto _func1 = SkillMap.find(id)->second;
    double damage1 = _func1(&data);

    lua_close(L);

    std::wcout << damage1 << std::endl;
    return 0;
}

and the lua function:

function WaterBall(info)
    return 50
end

The Error: Error calling Lua function: attempt to call a nil value

sol/sol.cpp is a project from github https://github.com/ThePhD/sol2


Solution

  • If you simplify your process, it would be:

    lua_getglobal(L, func_name.c_str());     // push WaterBall
    if (lua_isfunction(L, -1))
        SkillMap[id] = ...blabla
    lua_pop(L, 1);                           // pop WaterBall
    
    auto _func1 = SkillMap.find(id)->second;
    double damage1 = _func1(&data);          // call WaterBall
    

    See? You want to call WaterBall, but WaterBall is popped before that, so a nil gets called.

    I see that your lambda function captured the variable func_name, do you want to get the function first?

    SkillMap[id] = [L, func_name](ComplexData* info) -> double {
        lua_getglobal(L, func_name.c_str());
        stack_struct(L, *info);
        ...
    

    Or do you want to cache the function?

    if (lua_isfunction(L, -1)) {
        int func = luaL_ref(L, LUA_REGISTRYINDEX);   // cache function
    
        SkillMap[id] = [L, func](ComplexData* info) -> double {
            lua_rawgeti(L, LUA_REGISTRYINDEX, func); // get function
            stack_struct(L, *info);
            ....
    }
    //lua_pop(L, 1);
    

    luaL_ref can save the object to the registry and pop the object from the stack, so the call to lua_pop after the if block should be removed.