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")