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