ObjectiveC Lua: Error in C function call from Lua - ios

I am using two libraries for objectiveC-lua bridge.
One is Lua library (with C API) provided by Lua official web page. Since that library
does not provide API to pass objectiveC object to lua, I opt for another library
for this purpose, and Wax seems to be the only option for now.
This program starts when runLua() is called, where it will invoke test.lua
iOS implementation
-(void) runLua{
[self initLua];
[self invokeLua:#"require test"];
}
-(void)initLua{
// initialize Lua and our load our lua file
L = luaL_newstate(); // create a new state structure for the interpreter
luaL_openlibs(L); // load all the standard libraries into the interpreter
lua_settop(L, 0);
initDone = TRUE;
}
-(void) invokeLua:(NSString*)luaSrcStr{
//NSLog(#"%#",luaSrcStr);
if(!initDone){
NSLog(#"inside invokeLua: not yet init");
[self initLua];
}
lua_settop(L, 0);
int ok = luaL_loadstring(L, [luaSrcStr UTF8String]);
if(ok == 0){
lua_getglobal(L,"debug");
lua_getfield(L,-1, "traceback");
lua_remove(L,-2);
lua_insert(L,-2);
ok = lua_pcall(L, 0, 0, -2);
if (ok != 0){
luaL_error(L, "cannot run lua file: %s",
lua_tostring(L, -1));
return;
}
}
test.lua
local testObject = myLib.newiOSObjWithName("TestObject")
testObject:setObjectLength(3.6)
myLib.passTestObjectToC(testObject)
There is no problem in executing 1st and 2nd line, only 3rd line, error occurs.
Error
PANIC: unprotected error in call to Lua API (cannot run lua file: attempt to index a nil
value stack traceback:
[C]: in function 'passTestObjectToC'
...-451C-9F1D-9CE481B4F9A0/test.app/test.lua:6: in main chunk
[C]: in function 'require'
[string "..."]:3: in main chunk)
The error seems to indicate that the function passTestObjectToC is not found in lua, which in this case is not true as the function is registered to lua when luaL_openlibs(L) is called.
My guess, the problem comes where objectiveC object is passed to lua using wax_instance_create(luaState *L, ...). After passing the object, Wax probably has changed the luaState *L that it doesnt remember that passTestObjectToC is already registered.
The C implementation as below.
C implementation
static int newiOSObjWithName(lua_State *L){
NSString *className = [NSString stringWithCString:lua_tostring(L, -1)
encoding:NSUTF8StringEncoding];
className = [className stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:
[[className substringToIndex:1] uppercaseString]];
id classId = [[NSClassFromString(className) alloc] init];
luaopen_wax_instance(L);
wax_instance_create(L, classId, YES);
return 1;
}
static int passTestObjectToC(lua_State *L){
// something to be done here
return 0;
}
static const struct luaL_Reg myLib [] = {
{"newiOSObjWithName", newiOSObjWithName},
{"passTestObjectToC", passTestObjectToC},
{NULL, NULL}
}
int luaopen_mylib (lua_State *L){
luaL_register(L, "myLib", myLib);
return 1;
}
linit.c of Lua Library
static const luaL_Reg lualibs[] = {
{"", luaopen_base},
{LUA_LOADLIBNAME, luaopen_package},
{LUA_TABLIBNAME, luaopen_table},
{LUA_IOLIBNAME, luaopen_io},
{LUA_OSLIBNAME, luaopen_os},
{LUA_STRLIBNAME, luaopen_string},
{LUA_MATHLIBNAME, luaopen_math},
{LUA_DBLIBNAME, luaopen_debug},
{"myLib", luaopen_mylib},
{NULL, NULL}
};
LUALIB_API void luaL_openlibs (lua_State *L) {
const luaL_Reg *lib = lualibs;
for (; lib->func; lib++) {
lua_pushcfunction(L, lib->func);
lua_pushstring(L, lib->name);
lua_call(L, 1, 0);
}
}
Anyone can tell me why there is an error for passTestObjectToC call? What is the reason?
There is no example of Wax implementation for passing iOSObject available. I hv not found it.
So I am not sure if this is the right way to do it. Most examples use Wax to implement iOS classes/functions directly in Lua script instead.

My mistake, there is no problem in the codes above. Function passTestObjectToC is successfully called, the error occurred because of the implementation inside passTestObjectToC and not during the calling of passTestObjectToC.
iOS object is successfully passed from iOS to Lua using Wax as above.

Related

lua, call script from C with limited modules, error on string.len

I am testing about calling LUA scripts from C. LUA 5.3.5.
To tighten the libraries used, in C I am using
L = luaL_newstate();
luaopen_base(L);
luaopen_string(L);
luaL_dofile(L, szFilename);
int tipo = lua_getglobal(L, "check");
int error = lua_pcall(L, 0, 0, 0);
(code simplified and without error handling)
This is the sample LUA script:
function check()
local buffer = "hello"
buffer = buffer .. " world!"
print(buffer)
print(string.len(buffer))
end
Why does string.len return error?
attempt to index a nil value (global 'string')
I thought luaopen_string loads the string library.
luaopen_string does not define a global variable string: it just leaves the table on the stack. So do
luaopen_string(L);
lua_setglobal(L,"string");
Alternatively, use luaL_requiref instead of calling luaopen_string directly:
luaL_requiref(L,"string", luaopen_string,1);
See the last paragraph of section 6 in the manual;
To have access to these libraries, the C host program should call the luaL_openlibs function, which opens all standard libraries. Alternatively, the host program can open them individually by using luaL_requiref to call ... luaopen_string (for the string library) ... These functions are declared in lualib.h.
Finally, if you want to customize the set of standard libraries that your program uses, edit linit.c and add it to your project. Change the list in loadedlibs. Then call luaL_openlibs.
After talking on the LUA mailing list, the best hint I got is to look at the source code.
luaL_openlibs is defined in linit.c
LUALIB_API void luaL_openlibs (lua_State *L) {
const luaL_Reg *lib;
/* "require" functions from 'loadedlibs' and set results to global table */
for (lib = loadedlibs; lib->func; lib++) {
luaL_requiref(L, lib->name, lib->func, 1);
lua_pop(L, 1); /* remove lib */
}
}
So to load only the libraries base and string
the solution for LUA 5.3 is
luaL_requiref(L, "_G", luaopen_base, 1);
lua_pop(L, 1);
luaL_requiref(L, LUA_STRLIBNAME, luaopen_string, 1);
lua_pop(L, 1);
And for LUA 5.4
luaL_requiref(L, LUA_GNAME, luaopen_base, 1);
lua_pop(L, 1);
luaL_requiref(L, LUA_STRLIBNAME, luaopen_string, 1);
lua_pop(L, 1);

GLib parsing integer from string

I'm trying to parse value from string in C for GLib application.
The result is well-compiled freezing app with no error/warning.
I post the function used to be launched for specific thread. The function is a stub only.
int i;
gchar *buffer="";
void *mainthreadfunc(){
buffer = g_strdup ("25");
i = atoi((char *)buffer);
for(;i<10;i++){
g_print("The i is: %d\n", i);
g_usleep (1000000);
}
}

Lua 'require' but files are only in memory

Setting: I'm using Lua from a C/C++ environment.
I have several lua files on disk. Those are read into memory and some more memory-only lua files become available during runtime. Think e.g. of an editor, with additional unsaved lua files.
So, I have a list<identifier, lua_file_content> in memory. Some of these files have require statements in them. When I try to load all these files to a lua instance (currently via lua_dostring) I get attempt to call global require (a nil value).
Is there a possibility to provide a require function, which replaces the old one and just uses the provided in memory files (those files are on the C side)?
Is there another way of allowing require in these files without having the required files on disk?
An example would be to load the lua stdlib from memory only without altering it. (This is actually my test case.)
Instead of replacing require, why not add a function to package.loaders? The code is nearly the same.
int my_loader(lua_State* state) {
// get the module name
const char* name = lua_tostring(state);
// find if you have such module loaded
if (mymodules.find(name) != mymodules.end())
{
luaL_loadbuffer(state, buffer, size, name);
// the chunk is now at the top of the stack
return 1;
}
// didn't find anything
return 0;
}
// When you load the lua state, insert this into package.loaders
http://www.lua.org/manual/5.1/manual.html#pdf-package.loaders
A pretty straightforward C++ function that would mimic require could be: (pseudocode)
int my_require(lua_State* state) {
// get the module name
const char* name = lua_tostring(state);
// find if you have such module loaded
if (mymodules.find(name) != mymodules.end())
luaL_loadbuffer(state, buffer, size, name);
// the chunk is now at the top of the stack
lua_call(state)
return 1;
}
Expose this function to Lua as require and you're good to go.
I'd also like to add that to completely mimic require's behaviour, you'd probably need to take care of package.loaded, to avoid the code to be loaded twice.
There is no package.loaders in lua 5.2
It called package.searchers now.
#include <stdio.h>
#include <string>
#include <lua.hpp>
std::string module_script;
int MyLoader(lua_State *L)
{
const char *name = luaL_checkstring(L, 1); // Module name
// std::string result = SearchScript(name); // Search your database.
std::string result = module_script; // Just for demo.
if( luaL_loadbuffer(L, result.c_str(), result.size(), name) )
{
printf("%s", lua_tostring(L, -1));
lua_pop(L, 1);
}
return 1;
}
void SetLoader(lua_State* L)
{
lua_register(L, "my_loader", MyLoader);
std::string str;
// str += "table.insert(package.loaders, 2, my_loader) \n"; // Older than lua v5.2
str += "table.insert(package.searchers, 2, my_loader) \n";
luaL_dostring(L, str.c_str());
}
void SetModule()
{
std::string str;
str += "print([[It is add.lua]]) \n";
str += "return { func = function() print([[message from add.lua]]) end } \n";
module_script=str;
}
void LoadMainScript(lua_State* L)
{
std::string str;
str += "dev = require [[add]] \n";
str += "print([[It is main.lua]]) \n";
str += "dev.func() \n";
if ( luaL_loadbuffer(L, str.c_str(), str.size(), "main") )
{
printf("%s", lua_tostring(L, -1));
lua_pop(L, 1);
return;
}
}
int main()
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);
SetModule(L); // Write down module in memory. Lua not load it yet.
SetLoader(L);
LoadMainScript(L);
lua_pcall(L,0,0,0);
lua_close(L);
return 0;
}

My lua script load .so library, how can I write the host program with Lua 5.2?

I searched and tried for days. The problem is this:
I wrote a script which load a shared library locker.so, it runs well with lua interpretor, but I can not write out the correct host program.
My lua script load_so.lua is very simple:
locker = require("locker")
print(type(locker))
for k, v in pairs(locker) do
print(k, v)
end
My host program is:
int main(int argc, const char *argv[])
{
lua_State * L = luaL_newstate();
luaL_openlibs(L);
if (luaL_dofile(L, "load_so.lua") != 0) {
fprintf(stderr, "luaL_dofile error: %s\n", lua_tostring(L, -1));
lua_pop(L, 1);
}
lua_close(L);
return 0;
}
When I run my host program, error print out:
luaL_dofile error: error loading module 'locker' from file './locker.so':
./locker.so: undefined symbol: lua_pushstring
And the locker.c:
static int elock_get(lua_State * L) {...}
static int elock_set(lua_State * L) {...}
static const struct luaL_Reg lockerlib[] = {
{"get", elock_get},
{"set", elock_set},
{NULL, NULL}
};
int luaopen_locker(lua_State *L)
{
//luaL_newlib(L, lockerlib);
//lua_pushvalue(L, -1);
//lua_setglobal(L, LOCKER_LIBNAME);
//set_info(L);
luaL_newlibtable(L, lockerlib);
luaL_setfuncs(L, lockerlib, 0);
return 1;
}
Most articles, books, questions shows how to do it in Lua 5.1, and yes, the program runs correctly in Lua 5.1. But how can I make it support Lua 5.2, and why?
P.S: I don't want to load the library in my C host program like luaL_requiref(L, "locker", luaopen_locker, 1), because I don't know which .so library will load in Lua script.
Thanks.
In Linux, if you're linking liblua.a statically into your main program, you need to use -Wl,-E when linking to export the Lua API symbols; this is how the standard command line interpreter is built.

Forcing a Lua script to exit

How do you end a long running Lua script?
I have two threads, one runs the main program and the other controls a user supplied Lua script. I need to kill the thread that's running Lua, but first I need the script to exit.
Is there a way to force a script to exit?
I have read that the suggested approach is to return a Lua exception. However, it's not garanteed that the user's script will ever call an api function ( it could be in a tight busy loop). Further, the user could prevent errors from causing his script to exit by using a pcall.
You could use setjmp and longjump, just like the Lua library does internally. That will get you out of pcalls and stuff just fine without need to continuously error, preventing the script from attempting to handle your bogus errors and still getting you out of execution. (I have no idea how well this plays with threads though.)
#include <stdio.h>
#include <setjmp.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
jmp_buf place;
void hook(lua_State* L, lua_Debug *ar)
{
static int countdown = 10;
if (countdown > 0)
{
--countdown;
printf("countdown: %d!\n", countdown);
}
else
{
longjmp(place, 1);
}
}
int main(int argc, const char *argv[])
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);
lua_sethook(L, hook, LUA_MASKCOUNT, 100);
if (setjmp(place) == 0)
luaL_dostring(L, "function test() pcall(test) print 'recursing' end pcall(test)");
lua_close(L);
printf("Done!");
return 0;
}
You could set a variable somewhere in your program and call it something like forceQuitLuaScript. Then, you use a hook, described here to run every n instructions. After n instructions, it'll run your hook which just checks if forceQuitLuaScript is set, and if it is do any clean up you need to do and kill the thread.
Edit: Here's a cheap example of how it could work, only this is single threaded. This is just to illustrate how you might handle pcall and such:
#include <stdlib.h>
#include "lauxlib.h"
void hook(lua_State* L, lua_Debug *ar)
{
static int countdown = 10;
if (countdown > 0)
{
--countdown;
printf("countdown: %d!\n", countdown);
}
else
{
// From now on, as soon as a line is executed, error
// keep erroring until you're script reaches the top
lua_sethook(L, hook, LUA_MASKLINE, 0);
luaL_error(L, "");
}
}
int main(int argc, const char *argv[])
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);
lua_sethook(L, hook, LUA_MASKCOUNT, 100);
// Infinitely recurse into pcalls
luaL_dostring(L, "function test() pcall(test) print 'recursing' end pcall(test)");
lua_close(L);
printf("Done!");
return 0;
}
The way to end a script is to raise an error by calling error. However, if the user has called the script via pcall then this error will be caught.
It seems like you could terminate the thread externally (from your main thread) since the lua script is user supplied and you can't signal it to exit.
If that isn't an option, you could try the debug API. You could use lua_sethook to enable you to regain control assuming you have a way to gracefully terminate your thread in the hook.
I haven't found a way to cleanly kill a thread that is executing a long running lua script without relying on some intervention from the script itself. Here are some approaches I have taken in the past:
If the script is long running it is most likely in some loop. The script can check the value of some global variable on each iteration. By setting this variable from outside of the script you can then terminate the thread.
You can start the thread by using lua_resume. The script can then exit by using yield().
You could provide your own implementation of pcall that checks for a specific type of error. The script could then call error() with a custom error type that your version of pcall could watch for:
function()
local there_is_an_error = do_something()
if (there_is_an_error) then
error({code = 900, msg = "Custom error"})
end
end
possibly useless, but in the lua I use (luaplayer or PGELua), I exit with
os.exit()
or
pge.exit()
If you're using coroutines to start the threads, you could maybe use coroutine.yield() to stop it.
You might wanna take look at
https://github.com/amilamad/preemptive-task-scheduler-for-lua
project. its preemptive scheduler for lua.
It uses a lua_yeild function inside the hook. So you can suspend your lua thread. It also uses longjmp inside but its is much safer.
session:destroy();
Use this single line code on that where you are want to destroy lua script.
lua_KFunction cont(lua_State* L);
int my_yield_with_res(lua_State* L, int res) {
cout << " my_yield_with_res \n" << endl;
return lua_yieldk(L, 0, lua_yield(L, res), cont(L));/* int lua_yieldk(lua_State * L, int res, lua_KContext ctx, lua_KFunction k);
Приостанавливает выполнение сопрограммы(поток). Когда функция C вызывает lua_yieldk, работающая
сопрограмма приостанавливает свое выполнение и вызывает lua_resume, которая начинает возврат данной сопрограммы.
Параметр res - это число значений из стека, которые будут переданы в качестве результатов в lua_resume.
Когда сопрограмма снова возобновит выполнение, Lua вызовет заданную функцию продолжения k для продолжения выполнения
приостановленной C функции(смотрите §4.7). */
};
int hookFunc(lua_State* L, lua_Debug* ar) {
cout << " hookFunc \n" << endl;
return my_yield_with_res(L, 0);// хук./
};
lua_KFunction cont(lua_State* L) {// функция продолжения.
cout << " hooh off \n" << endl;
lua_sethook(L, (lua_Hook)hookFunc, LUA_MASKCOUNT, 0);// отключить хук foo.
return 0;
};
struct Func_resume {
Func_resume(lua_State* L, const char* funcrun, unsigned int Args) : m_L(L), m_funcrun(funcrun), m_Args(Args) {}
//имена функций, кол-во агрументов.
private:
void func_block(lua_State* L, const char* functionName, unsigned int Count, unsigned int m_Args) {
lua_sethook(m_L, (lua_Hook)hookFunc, LUA_MASKCOUNT, Count); //вызов функции с заданной паузой.
if (m_Args == 0) {
lua_getglobal(L, functionName);// получить имя функции.
lua_resume(L, L, m_Args);
}
if (m_Args != 0) {
int size = m_Args + 1;
lua_getglobal(L, functionName);
for (int i = 1; i < size; i++) {
lua_pushvalue(L, i);
}
lua_resume(L, L, m_Args);
}
};
public:
void Update(float dt) {
unsigned int Count = dt * 100.0;// Время работы потока.
func_block(m_L, m_funcrun, Count, m_Args);
};
~Func_resume() {}
private:
lua_State* m_L;
const char* m_funcrun; // имя функции.
unsigned int m_Count;// число итерации.
unsigned int m_Args;
};
const char* LUA = R"(
function main(y)
--print(" func main arg, a = ".. a.." y = ".. y)
for i = 1, y do
print(" func main count = ".. i)
end
end
)";
int main(int argc, char* argv[]) {
lua_State* L = luaL_newstate();/*Функция создает новое Lua состояние. */
luaL_openlibs(L);
luaL_dostring(L, LUA);
//..pushlua(L, 12);
pushlua(L, 32);
//do {
Func_resume func_resume(L, "main", 2);
func_resume.Update(1.7);
lua_close(L);
// } while (LUA_OK != lua_status(L)); // Пока поток не завершен.
return 0;
};

Resources