Lua与C++的一些交互
Lua调用C函数用的堆栈是临时的,调用结束之后就被销毁了.
C调用Lua函数时,C负责堆栈.
1、C++调用Lua
调用函数的通用方法:
1 int lua_general_call( lua_State* lua, const char* func, const char* fmt, ... ) 2 { 3 va_list vl; 4 int narg, nres; 5 int len; 6 7 8 len = lua_gettop( lua ); 9 10 va_start( vl, fmt ); 11 12 lua_getglobal( lua, func ); 13 14 if ( !lua_isfunction( lua, -1 ) ) 15 { 16 fprintf( stderr, "函数不存在!\n" ); 17 goto err_exit; 18 } 19 20 21 narg = 0; 22 23 while ( *fmt ) 24 { 25 switch ( *fmt++ ) 26 { 27 case 'd': 28 lua_pushinteger( lua, va_arg( vl, int ) ); 29 break; 30 31 case 'f': 32 lua_pushnumber( lua, va_arg( vl, double ) ); 33 break; 34 35 case 's': 36 lua_pushstring( lua, va_arg( vl, char* ) ); 37 break; 38 39 case '>': 40 goto endwhile; 41 42 default: 43 fprintf( stderr, "未知参数格式!\n" ); 44 goto err_exit; 45 } 46 47 narg ++; 48 49 //栈上至少有一个空余,否则增长 50 if ( !lua_checkstack( lua, 1 ) ) 51 { 52 fprintf( stderr, "LUA栈空间不足!\n" ); 53 goto err_exit; 54 } 55 } 56 57 endwhile: 58 59 nres = strlen( fmt ); 60 61 if ( lua_pcall( lua, narg, nres, 0 ) != 0 ) 62 { 63 fprintf( stderr, "调用错误 --- [%s]\n", lua_tostring( lua, -1 ) ); 64 goto err_exit; 65 } 66 67 nres = -nres; 68 69 while ( *fmt ) 70 { 71 switch ( *fmt++ ) 72 { 73 case 'd': 74 if ( !lua_isnumber( lua, nres ) ) 75 { 76 fprintf( stderr, "结果格式不对!\n" ); 77 goto err_exit; 78 } 79 80 *va_arg( vl, int* ) = ( int )lua_tointeger( lua, nres ); 81 break; 82 83 case 'f': 84 if ( !lua_isnumber( lua, nres ) ) 85 { 86 fprintf( stderr, "结果格式不对!\n" ); 87 goto err_exit; 88 } 89 90 *va_arg( vl, double* ) = ( double )lua_tonumber( lua, nres ); 91 break; 92 93 case 's': 94 if ( !lua_isstring( lua, nres ) ) 95 { 96 fprintf( stderr, "结果格式不对!\n" ); 97 goto err_exit; 98 } 99 100 strcpy( va_arg( vl, char* ), lua_tostring( lua, nres ) ); 101 break; 102 103 default: 104 fprintf( stderr, "未知结果格式!\n" ); 105 goto err_exit; 106 } 107 108 nres++; 109 } 110 111 va_end( vl ); 112 lua_settop( lua, len ); 113 114 return 0; 115 116 err_exit: 117 va_end( vl ); 118 lua_settop( lua, len ); 119 return -1; 120 }
Lua经常做为配置文件用
通用方法:
1 #define MAX_CFG_LEN 256 2 3 //GUID生成 4 static const char* g_tmp_cfg_name = "TMP_GUID"; 5 6 //此处cfg_name 类似于xx.xx.xx 7 int read_cfg( lua_State* lua, const char* cfg_name, char ctt[MAX_CFG_LEN] ) 8 { 9 int len; 10 char str[MAX_CFG_LEN * 2]; 11 12 len = lua_gettop( lua ); 13 memset( str, 0, sizeof( str ) ); 14 snprintf( str, sizeof( str ), "%s = %s", g_tmp_cfg_name, cfg_name ); 15 16 if ( luaL_dostring( lua, str ) != 0 ) 17 { 18 fprintf( stderr, "LUA内存不足或语法错误\n" ); 19 goto err_exit; 20 } 21 22 lua_getglobal( lua, g_tmp_cfg_name ); 23 24 if ( lua_type( lua, -1 ) == LUA_TNIL ) 25 { 26 fprintf( stderr, "变量为空值\n" ); 27 goto err_exit; 28 } 29 30 snprintf( ctt, MAX_CFG_LEN, lua_tostring( lua, -1 ) ); 31 32 lua_settop( lua, len ); 33 return 0; 34 35 err_exit: 36 lua_settop( lua, len ); 37 return -1; 38 }
一般方法
1 lua_getglobal(lua, "中文配置测试1"); 2 lua_getfield(lua, -1, "基本配置"); //键是字符串 3 lua_pushstring(lua, "数据目录"); //任何键都可以 4 lua_gettable(lua, -2); 5 fprintf(stdout, "%s\n", lua_tolstring(lua, -1, NULL));
2、Lua调用C++(LuaCallDllTest1.so)
实质上,Lua载入SO的方式有两种:
1 -- 方式一 2 require("LuaCallDllTest1"); 3 --[[此句等同于: 4 local reg_fn_tmp = package.loadlib("LuaCallDllTest1.so", "luaopen_LuaCallDllTest1"); 5 reg_fn_tmp(); -- 此函数往往为注册函数,执行一次等于注册了一个函数集 6 --]] 7 8 9 -- 方式二 10 local TestFn = package.loadlib("LuaCallDllTest1.so", "lua_hello_fn"); 11 12 13 14 -- 方式一等同于自动注册,方式二等同于手工注册.
C函数的撰写
1 int lua_add(lua_State* lua) 2 { 3 int ret; 4 5 if (lua_gettop(lua) != 2 || !lua_isnumber(lua, -1) || !lua_isnumber(lua, -2)) 6 { 7 return 0; 8 } 9 10 ret = lua_tointeger(lua, -1) + lua_tointeger(lua, -2); 11 lua_pushinteger(lua, ret); 12 13 return 1; 14 } 15 16 int lua_avg(lua_State* lua) 17 { 18 int i; 19 int len; 20 int sum; 21 22 len = lua_gettop(lua); 23 for (sum = 0, i = 1; i <= len; i ++) 24 { 25 if (!lua_isnumber(lua, i)) 26 { 27 return 0; 28 } 29 sum += lua_tointeger(lua, i); 30 } 31 32 lua_pushinteger(lua, sum); 33 lua_pushnumber(lua, (double)sum / len); 34 35 return 2; 36 } 37 38 // 压入表,与上面一些代码是逆过程 39 int lua_table(lua_State* lua) 40 { 41 lua_newtable(lua); 42 43 44 // 下面三行适应任何键 45 lua_pushstring(lua, "key1"); 46 lua_pushstring(lua, "Value1"); 47 lua_settable(lua, -3); 48 49 // 下面二行只适应字符串键 50 lua_pushinteger(lua, 66666); 51 lua_setfield(lua, -2, "key2"); 52 53 { 54 lua_newtable(lua); 55 lua_pushnumber(lua, 3.1415926); 56 lua_setfield(lua, -2, "key31"); 57 } 58 59 lua_setfield(lua, -2, "subtable"); 60 61 return 1; 62 } 63 64 65 // 压入闭包 66 int _my_counter(lua_State *lua) 67 { 68 int val; 69 int dir; 70 71 //取UPVALUE值 72 val = lua_tointeger(lua, lua_upvalueindex(1)); 73 dir = lua_tointeger(lua, lua_upvalueindex(2)); 74 //把第一个UPVALUE值处理后压栈,该值用于返回 75 if (dir == 0) 76 { 77 lua_pushinteger(lua, ++ val); 78 } 79 else 80 { 81 lua_pushinteger(lua, -- val); 82 } 83 84 //COPY该栈顶 该值用于更新UPVALUE 85 lua_pushvalue(lua, -1); 86 //弹出栈顶元素,覆盖第一个UPVALUE 即更新UPVALUE 87 lua_replace(lua, lua_upvalueindex(1)); 88 return 1; 89 } 90 91 int lua_counter(lua_State *lua) 92 { 93 int idx; 94 int dir; 95 96 //取初始值 97 idx = lua_tointeger(lua, 1); 98 //取方向 99 dir = lua_tointeger(lua, 2); 100 //UPVALUE入栈 101 lua_pushinteger(lua, idx); 102 lua_pushinteger(lua, dir); 103 //指明闭包,指明UPVALUE个数 104 lua_pushcclosure(lua, _my_counter, 2); 105 return 1; 106 }
Lua调用上面相关函数
1 require("LuaCallDllTest1"); 2 3 TLib = TestLuaCallLibName -- 在注册函数中注册的函数集名称 4 5 MSG = TLib.TestLuaCallMsg -- 一个显示函数,上面未列出 6 7 MSG('MSG From Lua'); 8 9 MSG(TLib.TestLuaCallAdd(1, 2)..'') 10 MSG(TLib.TestLuaCallAvg(2, 7)) 11 12 local t = TLib.TestLuaCallTable('Hello World!') 13 14 MSG(t.key1) 15 MSG(t.key2..'') 16 MSG(t.subtable.key31..'') 17 18 19 20 count1 = TLib.TestLuaCallCounter(25, 1) 21 MSG(count1()) 22 MSG(count1()) 23 MSG(count1()) 24 MSG(count1()) 25 26 27 count2 = TLib.TestLuaCallCounter(25, 0) 28 MSG(count2()) 29 MSG(count2()) 30 MSG(count2()) 31 MSG(count2())