Home > Net >  Registering callbacks in Lua
Registering callbacks in Lua

Time:10-16

I have the following structure (more general criticism on architecture welcome):

  • I have a rather large C program doing zillion things.
  • To provide scripting a large number of lowish level commands are handled by Lua.
  • Each "command" has an associated Lua fragment that's called when "command"must be executed.
  • Lua fragments may callback (very low level) C functions, but I don't it matters.
  • Lua fragment exits as soon as "command" is initiated.
  • "command" is actually performed outside from both Lua and C (think something like starting video play back or recording: Command may be "start", "stop", "rewind", ... but it is not actually carried out from program).
  • In order to know if "command" terminated and, eventually, get exit status I need to actively poll command (I have no callbacks, join or similar functions).

Now the real question:

I should, in certain instances, known to Lua fragments, schedule some timed call of specific Lua function; this may be one-shot (to handle timeouts) or cyclic (to handle polling).

I can easily provide a C function starting a thread to handle needs, but I don't know how how to provide C with the information about which Lua function will have to be called and how to use it.

My current Lua class (no timed callbacks yet) looks like:

static inline bool is_whitespace(const std::string& s) {
    return std::all_of(s.begin(), s.end(), isspace);
}

class Lua {
    lua_State *state;
    std::map<std::string, int> fragments;

    void push(std::string const& str) {  lua_pushstring(state, str.c_str()); }
    void push(char const* str)        {  lua_pushstring(state, str);         }
    void push(bool value)             { lua_pushboolean(state, value);       }
    void push(uint32_t value)         { lua_pushinteger(state, value);       }
    void push(int value)              { lua_pushinteger(state, value);       }
    void push(double value)           {  lua_pushnumber(state, value);       }

public:
    void push(std::nullptr_t) = delete;

    Lua() : state(luaL_newstate()) {
        luaL_openlibs(state);
    }

    void register_c_func(const char *name, lua_CFunction func) {
        lua_register(state, name, func);
    }

    bool is_snippet(const std::string &name) {
        return fragments.count(name);
    }

    int register_snippet(const char *name, const char *fragment) {
        if (is_snippet(name)) {
            SPDLOG_ERROR("lua::register_snippet({}): ERROR name already present", name);
            return LUA_NOREF;
        }
        if (!is_whitespace(fragment)) {
            auto ret = luaL_loadstring(state, fragment);
            if (ret != LUA_OK) {
                SPDLOG_ERROR("lua::register_snippet({}): Lua registration failed ({})", name, ret);
            } else {
                auto r = luaL_ref(state, LUA_REGISTRYINDEX);
                SPDLOG_TRACE("lua::register_snippet({}, {}): {}", name, fragment, r);
                fragments[name] = r;
                return r;
            }
        } else {
            SPDLOG_WARN("lua::register_snippet({}): has empty Lua fragment", name);
        }
        return LUA_NOREF;
    }

    template<typename... Args> bool exec(const std::string &name, Args... args){
        if (is_snippet(name)) {
            lua_rawgeti(state, LUA_REGISTRYINDEX, fragments.at(name));
            ((push(std::forward<Args>(args))), ...);
            int nargs = sizeof ...(Args);
            SPDLOG_TRACE("lua::exec({}, {}): {}", name, fragments.at(name), nargs);
            lua_pcall(state, nargs, LUA_MULTRET, 0);
            return true;
        } else {
            SPDLOG_WARN("lua::exec({}): has no Lua fragment", name);
            return false;
        }
    }

    void exec(const char* fragment) {
        SPDLOG_TRACE("lua::exec(): '{}'", fragment);
        auto ret = luaL_dostring(state, fragment);
        if (ret)
            SPDLOG_ERROR("lua::exec(): ERROR {}", ret);
    }

};

extern Lua master_lua;

Lua initialization, C function registering and snippet registering is outside this scope.

CodePudding user response:

The C function just need to be in the following form.

static int registerCallback(lua_State* L)
{
    const char* callbackName = luaL_checkstring(L, 1);
    //make a copy if you want to save it out of the scope
    return 0;
}

lua_register(L, "registerCallback", registerCallback);

Then in lua you can pass the name to it.

registerCallback("my_callback_function_name")
  • Related