• lua 与 c 的相互调用


      Lua是一个嵌入式的语言,意味着Lua不仅可以是一个独立运行的程序包也可以是一个用来嵌入其他应用的程序库。

      Lua可以作为程序库用来扩展应用的功能,也就是Lua可以作为扩展性语言的原因所在。同时,Lua程序中可以注册有其他语言实现的函数,这些函数可能由C语言(或其他语言)实现,可以增加一些不容易由Lua实现的功能。这使得Lua是可扩展的。与上面两种观点(Lua作为扩展性语言和可扩展的语言)对应的C和Lua中间有两种交互方式。第一种,C作为应用程序语言,Lua作为一个库使用;第二种,反过来,Lua作为程序语言,C作为库使用。这两种方式,C语言都使用相同的API与Lua通信,因此C和Lua交互这部分称为C API。

      C API是一个C代码与Lua进行交互的函数集。他由以下部分组成:读写Lua全局变量的函数、调用Lua函数的函数、运行Lua代码片断的函数、注册C函数然后可以在Lua中被调用的函数等。

      当在Lua和C之间交换数据时我们面临着两个问题:动态与静态类型系统的不匹配和自动与手动内存管理的不一致。解决办法是在C和Lua之间通信关键在于一 个虚拟的栈。几乎所有的API调用都是对栈上的值进行操作,所有C与Lua之间的数据交换也都通过这个栈来完成。因为栈是由Lua来管理的,垃圾回收器知 道那个值正在被C使用。

      C API中的大部分函数并不检查他们参数的正确性;你需要在调用函数之前负责确保参数是有效的。

    Lua以一个严格的LIFO规则(后进先出;也就是说,始终存取栈顶)来操作栈:
      (1)当你调用Lua时,它只会改变栈顶部分。
      (2)你的C代码却有更多的自由;更明确的来讲,你可以查询栈上的任何元素,甚至是在任何一个位置插入和删除元素。

    在调用C API时有几个重要的头文件:

      (1)lua.h:Lua基础函数库,lua_前缀
      (2)lauxlib.h:辅助库,luaL_前缀,利用lua.h实现的更高层的抽象
       (3)lualib.h:为了保持Lua的苗条,所有的标准库以单独的包提供,所以如果你不需要就不会强求你使用它们。头文件lualib.h定义了打 开这些库的函数。例如,调用luaopen_io,以创建io table并注册I/O函数(io.read,io.write等等)到Lua环境中。

    API用索引来访问栈中的元素。栈底为1,依次往上增加,也可用负数索引,-1表示栈顶元素。下面列出一些常用的C API函数:

    lua_push*:压入栈元素
      void lua_pushnil (lua_State *L);
      void lua_pushboolean (lua_State *L, int bool);
      void lua_pushnumber (lua_State *L, double n);
      void lua_pushlstring (lua_State *L, const char *s, size_t length);
      void lua_pushstring (lua_State *L, const char *s);

    lua_to*:从栈中获得值。即使给定的元素类型不正确,调用这些函数也没问题。
      int lua_toboolean (lua_State *L, int index);
      double lua_tonumber (lua_State *L, int index);
      const char * lua_tostring (lua_State *L, int index);

    Lua_tostring函数返回一个指向字符串的内部拷贝的指针。你不能修改它(使你想起那里有一个const)。只要这个指针对应的值还在栈内,Lua会保证这个指针一直有效。当一个C函数返回后,Lua会清理他的栈,所以,有一个原则:永远不要将指向Lua字符串的指针保存到访问他们的外部函数中。

      size_t lua_strlen (lua_State *L, int index):返回字符串的实际长度。

      int lua_checkstack(lua_State *L, int sz):检查栈空间。默认有20个空闲的记录,lua.h中的LUA_MINSTACK宏定义了这个常量。

      int lua_is... (lua_State *L, int index):检查一个元素能否被转换成指定的类型。

      int lua_type (lua_State *L, int idx):返回栈中元素的类型;

      const char* lua_typename(lua_State *L, int tp):返回type对应的名字字符串,第二个参数为lua_type返回的类型

      void luaL_checktype (lua_State *L, int arg, int t):返回参数arg是否是类型t,第三个参数为lua_type的取值。

      在lua.h头文件中,每种类型都被定义为一个常量:LUA_TNIL、LUA_TBOOLEAN、LUA_TNUMBER、LUA_TSTRING、LUA_TTABLE、LUA_TFUNCTION、LUA_TUSERDATA以及LUA_TTHREAD。

      int lua_gettop (lua_State *L):返回栈中元素个数,它也是栈顶元素的索引。

      void lua_settop (lua_State *L, int index):设置栈顶元素的索引,相当于设置栈的大小。如果开始的栈顶高于新的栈顶,顶部的值被丢弃。否则,为了得到指定的大小这个函数压入相应个数的 空值(nil)到栈上。lua_settop(L,0):清空堆栈。

      #define lua_pop(L,n) lua_settop(L, -(n)-1):宏定义,弹出n个元素。

      void lua_pushvalue (lua_State *L, int index):压入堆栈上指定索引的一个抟贝到栈顶,等于拷贝index处的元素,然后添加到栈顶。

      void lua_remove (lua_State *L, int index):移除指定索引的元素,并将其上面所有的元素下移来填补这个位置的空白。

      void lua_insert (lua_State *L, int index):移动栈顶元素到指定索引的位置,并将这个索引位置上面的元素全部上移至栈顶被移动留下的空隔。

      void lua_replace (lua_State *L, int index):从栈顶弹出元素值并将其设置到指定索引位置,没有任何移动操作。

    实践部分:

    1.lua 环境安装

    到http://www.lua.org/versions.html下载lua安装包(此处为5.1.5.tar.gz)

    解压后执行make linux

    2.lua之小试牛刀

    编辑first.lua

    print("lua program")

    编译first.lua

    lua first.lua

    -->lua program

    3. c 调用lua

    编写first.c调用first.lua

    /---first.c---/

    int main(int argc, char *argv[])
    {
            
            /*initialize Lua*/
            L = lua_open();

            /*load Lua base libraries*/
            luaL_openlibs(L);

            /*load the script*/
            luaL_dofile(L, "first.lua");
            
            /*cleanup Lua*/
            lua_close(L);

            return 0;
    }

    在lua.h里有这样的定义:typedef int (*lua_CFunction) (lua_State *L);所以lua_State * 的类型就是int (*lua_CFunction)

    编译

    gcc -o first first.c -I/usr/include/lua5.1 -llua5.1

    (-I和-l要正确指定,否则编译出错)

    执行

    wss@localhost:~/lua/train/test_01$ ./first
    lua program

    4.lua调用c(有两种方式)

    (1)c作为lua的一部分

    /-----lua_call_c.c-----/

    #include <lua.h>
    #include <lauxlib.h>
    #include <lualib.h>
    static int l_plus(lua_State* L)
    {
         lua_Integer a = luaL_checkinteger(L, 1);
         lua_Integer b = luaL_checkinteger(L, 2);
         lua_pushinteger(L, a+b);
         return 1;
    } int main()
    {
         lua_State *L = luaL_newstate();
         luaL_openlibs(L);
         lua_pushcfunction(L, l_plus);
         lua_setglobal(L, "myplus");
         if (luaL_dostring(L, "print(myplus(2,2))")) {
              lua_close(L);
              error("Failed to invoke");
         }
         lua_close(L);
         return 0;
    }

    #>gcc -o lua_call_c lua_call_c.c -I/usr/include/lua5.1 -llua5.1

    #>./lua_call_c
    4

    (2)c函数以库的形式被lua调用

    /***mytestlib.c**/

    #include <stdio.h>
    #include <string.h>
    #include<lua.h>
    #include <lauxlib.h>
    #include <lualib.h>

    int add(lua_State* L)
    {
        double op1 = luaL_checknumber(L,1);
        double op2 = luaL_checknumber(L,2);
        lua_pushnumber(L,op1 + op2);
        return 1;
    }

    int sub(lua_State* L)
    {
        double op1 = luaL_checknumber(L,1);
        double op2 = luaL_checknumber(L,2);
        lua_pushnumber(L,op1 - op2);
        return 1;
    }
    int myprint(lua_State* L)
    {
            lua_pushstring(L,"lua call c");
            return 1;

    }
    static luaL_Reg mylibs[] = {
        {"add", add},
        {"sub", sub},
        {"myprint",myprint},
        {NULL, NULL}
    };

    int luaopen_mytestlib(lua_State* L)
    {
        const char* libName = "mytestlib";
        luaL_register(L,libName,mylibs);
        return 1;
    }

    编译生成.so 库

    gcc -fpic -shared -o mytestlib.so mytestlib.c

    编写test.lua测试脚本,在test.lua里面调用mytestlib.so库

    /*******test.lua***********/

    require "mytestlib"
    print(mytestlib.myprint())
    print(mytestlib.add(1.0,23.0))
    print(mytestlib.sub(20.1,19))
    lua里调用c函数库时,需要以包名.函数名的形式进行调用

    wss@localhost:~/lua/train/lua-call_c/2$ lua test.lua
    lua call c
    24
    1.1                     

  • 相关阅读:
    druid-1.0.13 数据库配置文件密码加密
    PostConstruct注解
    easyui formatter 返回easyui组件
    小师妹问 easyUI mergeCells 行合并后表头和内容对不齐
    Java Split以竖线作为分隔符
    Integer比较值的时候小心使用
    js 关键字 in
    Asp.net中防止用户多次登录的方法
    C#取得站点跟目录
    解读支付宝接口实现步骤
  • 原文地址:https://www.cnblogs.com/thinkinglife/p/5228974.html
Copyright © 2020-2023  润新知