Chinaunix首页 | 论坛 | 博客
  • 博客访问: 80122
  • 博文数量: 13
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 100
  • 用 户 组: 普通用户
  • 注册时间: 2014-10-23 11:54
个人简介

让思想和双脚带我去梦的远方

文章分类

全部博文(13)

文章存档

2015年(7)

2014年(6)

我的朋友

分类: 嵌入式

2015-06-25 15:36:23

一、简介   

    和c语言通信的主要方法是一个无所不在的虚拟栈。所有的数据交换,无论是lua到c或者c到lua都是通过这个栈来完成,当Lua启动或调用c语言时,栈中至少会有20个空闲的槽,对于普通应用已经足够了。了解这个基本信息后才能更清楚的理解后面的API和实现。
    要实现供Lua调用的C库,必须使用Lua提供的C API,需要包含如下头文件:

点击(此处)折叠或打开

  1. #include <lua.h>
  2. #include <lualib.h>
  3. #include <lauxlib.h>
    头文件lua.h定义了Lua提供的基础函数,包括创建Lua环境、调用Lua函数(如lua_pcall)、读写Lua环境中全局变量、以及注册供Lua调用的新函数等。lua.h中定义的所有内容都有lua_前缀。
    头文件lualib.h是Lua的标准库,为了使Lua保持小巧,所有的标准库都被组织到了不同的包中。头文件lualib.h中定义了打开这些库的函数。
    头文件lauxlib.h定义了辅助库提供的函数,它的所有定义都以luaL_开头(如luaL_loadbuffer)。辅助库是一个使用lua.h中API编写出的一个较高的抽象层。
    

二、lua调用c库函数

    介绍了基本信息后,我们开始构建能够提供给Lua调用的c库。
    

点击(此处)折叠或打开

  1. #define USRSTAT_META             "usrstat"
  2. #define USRSTAT_CLIENT_META     "usrstat.clients"

  3. LUALIB_API int luaopen_usrstat(lua_State *L)
  4. {
  5.     luaL_register(L, USRSTAT_META, R_common);
  6.     luaL_newmetatable(L, USRSTAT_CLIENT_META);
  7.     luaL_register(L, NULL, R_common);
  8.     luaL_register(L, NULL, R_usrstat);
  9.     lua_pushvalue(L, -1);
  10.     lua_setfield(L, -2, "__index");
  11.     lua_setfield(L, -2, "clients");
  12.     return 1;
  13. }
     luaopen_***为库函数的入口,luaL_register函数为表注册函数,luaL_newmetatable函数创建一个新的元表,压入虚拟栈内,元表位于虚拟栈最顶端。后面再为新建的元表注册函数,当luaL_register第二个参数为NULL,则默认为虚拟栈顶端的表注册函数。

点击(此处)折叠或打开

  1. LUA_WRAP_STRING(clients,getClients)
  2. LUA_WRAP_STRING(clients,getClientConnection)

  3. static const luaL_reg R_usrstat[] = {
  4.     LUA_REG(clients,getClients),
  5.     LUA_REG(clients,getClientConnection),
  6.     { NULL, NULL }
  7. };

     创建我们需要的函数,这里用到两个宏,LUA_REG和LUA_WRAP_STRING

点击(此处)折叠或打开

  1. #define LUA_REG(type,op) \
  2.     { #op, usrstat_L_##type##_##op }

  3. #define LUA_WRAP_INT(type,op)                             \
  4.     static int usrstat_L_##type##_##op(lua_State *L)        \
  5.     {                                                    \
  6.         int rv;                                            \
  7.         if( !type##_get_##op(&rv) )                \
  8.             lua_pushnumber(L, rv);                        \
  9.         else                                            \
  10.             lua_pushnil(L);                                \
  11.         return 1;                                        \
  12.     }

  13. #define LUA_WRAP_STRING(type,op)                         \
  14.     static int usrstat_L_##type##_##op(lua_State *L)        \
  15.     {                                                    \
  16.         char rv[USRSTAT_BUFSIZE];                        \
  17.         memset(rv, 0, USRSTAT_BUFSIZE);                    \
  18.         if( !type##_get_##op(L, rv) )                \
  19.             lua_pushstring(L, rv);                        \
  20.         else                                            \
  21.             lua_pushnil(L);                                \
  22.         return 1;                                        \
  23.     }

  24. #define LUA_WRAP_STRUCT(type,op)                        \
  25.     static int usrstat_L_##type##_##op(lua_State *L)        \
  26.     {                                                    \
  27.         return usrstat_L_##op(L, type##_get_##op);        \
  28.     }
     LUA_REG,用来注册函数,LUA_WRAP_INT,LUA_WRAP_STRUCT和LUA_WRAP_STRING则用于声明函数。
     现在我们需要一个供lua调用的函数,这个函数的功能是在页面显示终端的基本信息,我们实现如下。

点击(此处)折叠或打开

  1. static int clients_get_getClients(lua_State *L, char *buf)
  2. {
  3.     getAllUserInfo(buf);
  4.     return 0;
  5. }
     通过LUA_WRAP_STRING宏可以知道,最终是通过lua_pushstring(L, rv);这一行来实现将字符串压入虚拟栈。而上面函数的buf指针就是预先分配好的一个数组,通过对这个数组赋值,并且最后通过lua_pushstring压栈,页面就能通过lua读取到c函数提供的信息。页面调用如下:

点击(此处)折叠或打开

  1. <%-
  2.     local usrstat = require "usrstat"
  3.     local clients = usrstat.clients.getClients()
  4. -%>

    htm页面中通过<%    %>分隔出语句块,通知解释器这个语句块中的语言要用lua解释,lua通过require “usrstat”获取所需的表,再通过usrstat.clients.getClients()函数调用获取所需信息。

三、lua带参数调用

    如果c库函数需要通过页面的某个值来决定返回值,就需要lua在调用c库函数的时候能将参数传下去,前面已经说过lua直接获取底层信息是通过c函数将值压入虚拟栈中提供给lua的,同理,lua将参数传给c函数也必须使用虚拟栈传递的方法。首先看页面的调用方式。

点击(此处)折叠或打开

  1. <%-
  2. local usrstat = require "usrstat"
  3. connection = usrstat.clients.getClientConnection(clientInfo)
  4. -%>
    可以看出与前面的区别在于调用函数的时候带入了参数,这个参数在lua调用c库函数时,就位于虚拟栈的顶端,然后我们再看c函数是怎么取出这个参数的。

点击(此处)折叠或打开

  1. static int clients_get_getClientConnection(lua_State *L, char *buf)
  2. {
  3.     const char *ip = NULL;

  4.     if (lua_gettop(L)==1 && lua_isstring(L, 1))
  5.     {
  6.         ip = lua_tostring(L, 1);
  7.         getClientConnection(ip, buf);
  8.         return 0;
  9.     }

  10.     return 1;
  11. }

    首先判断参数合法性,lua_gettop函数取出所有参数的个数,我们需要的是字符串类型的参数,所有通过lua_isstring函数进行判断。参数合法后,就可以通过lua_tostring函数取出位于栈顶的参数。


四、总结

    本文主要介绍了openwrt中luci页面调用c库函数的方法,无论是c库函数传值到页面还是页面传值到c函数,参数的传递都是通过lua提供的虚拟栈实现的。本文没有对lua的c api多做介绍,一方面是因为网络资源丰富,应该很容易就能找到相关的资料,另外因为lua语言还在发展,很多老版本的api在新版本中已经不存在。所以重点在于理解lua和c之间的调用方式。理解工作方式后再去结合api,相信就能解决多数问题。






阅读(3051) | 评论(0) | 转发(0) |
0

上一篇:通过gdb调试coredump

下一篇:没有了

给主人留下些什么吧!~~