开发者社区> jamesjxiao> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

Lua和C语言的交互——C API

简介:   Lua可作为扩展性语言(Lua可以作为程序库用来扩展应用的功能),同时也是个可扩展的语言(Lua程序中可以注册由其他语言实现的函数)。   C和Lua交互的部分称为C API。C API是一个C代码与Lua进行交互的函数集。
+关注继续查看

  Lua可作为扩展性语言(Lua可以作为程序库用来扩展应用的功能),同时也是个可扩展的语言(Lua程序中可以注册由其他语言实现的函数)。

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

  API中有些函数为了方便以宏的方式实现。

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

  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):从栈顶弹出元素值并将其设置到指定索引位置,没有任何移动操作。

  下面来看一段Lua和C++简单交互的代码:

#include <stdio.h>
#include <string.h>

extern "C" 
{  
    #include <lua.h>  
    #include <lauxlib.h>  
    #include <lualib.h>  
} 

int main (void)
{
    char buff[256];
    int error;
    lua_State *L = lua_open();  /* opens Lua */
    luaL_openlibs(L);

    while (fgets(buff, sizeof(buff), stdin) != NULL)
    {
       error = luaL_loadbuffer(L, buff, strlen(buff), "line") || lua_pcall(L, 0, 0, 0);
       if (error) 
       {
           fprintf(stderr, "%s", lua_tostring(L, -1));
           lua_pop(L, 1);/* pop error message from the stack */
       }
    }

    lua_close(L);
    return 0;
}

  上述代码在VS2010中正常运行,Lua使用5.1的版本,其中有几点需要注意的:

  (1)lua5.0之前初始化库的用法如下:

  luaopen_base(L); /* opens the basic library */

  luaopen_table(L); /* opens the table library */

  luaopen_io(L); /* opens the I/O library */

  luaopen_string(L); /* opens the string lib. */

  luaopen_math(L); /* opens the math lib. */

  但5.0之后只需要一句话即可:

luaL_openlibs(L);

  (2)需要添加依赖库:lua5.1.lib lua51.lib。

  下面这个例子可以有助于加深对C和Lua之间堆栈的理解:

// 从栈底到栈顶依次遍历整个堆栈
static void stackDump(lua_State* L)
{
    int i;
    int top = lua_gettop(L);
    for(i = 1; i <= top; ++i)
    {
        int t = lua_type(L, i);
        switch(t)
        {
        case LUA_TSTRING:
            printf("'%s'", lua_tostring(L, i));
            break;
        case LUA_TBOOLEAN:
            printf(lua_toboolean(L, i) ? "true": "false");
            break;
        case LUA_TNUMBER:
            printf("'%g'", lua_tonumber(L, i));
            break;
        default:
            printf("'%s'", lua_typename(L, t));
            break;
        }
        printf("    ");
    }
    printf("\n");
}

int main(void)
{
    lua_State* L = lua_open();
    luaL_openlibs(L);

    lua_pushboolean(L, 1);
    lua_pushnumber(L, 10);
    lua_pushnil(L);
    lua_pushstring(L, "hello");
    stackDump(L);

    lua_pushvalue(L, -4);
    stackDump(L);

    lua_replace(L, 3);
    stackDump(L);

    lua_settop(L, 6);
    stackDump(L);

    lua_remove(L, -3);
    stackDump(L);

    lua_settop(L, -5);
    stackDump(L);

    return 0;
}

  注意lua_replace首先会弹出栈顶元素,并且需要注意的是lua_replace(L, -1);语句会导致站顶元素弹出,其他元素不变。

   作为配置语言是LUA的一个重要应用,下面看一个简单的这方面的例子。有一个含有简单字段记录的lua文件如下:

width = 200
height = 300

  对该lua文件的解析大妈如下:

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern "C" 
{  
    #include <lua.h>  
    #include <lauxlib.h>  
    #include <lualib.h>  
} 

void error(lua_State* L, const char* fmt, ...)
{
    va_list argp;
    va_start(argp, fmt);
    vfprintf(stderr, fmt, argp);
    va_end(argp);
    lua_close(L);
    exit(EXIT_FAILURE);
}

void load(char* filename, int* width, int* height)
{
    lua_State* L = lua_open();
    luaL_openlibs(L);

    if(luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0))
    {
        error(L, "cannot run configuration file: %s", lua_tostring(L, -1));
    }

    lua_getglobal(L, "width");
    lua_getglobal(L, "height");

    if(!lua_isnumber(L, -2))
        error(L, "'width' should be a number\n");
    
    if(!lua_isnumber(L, -1))
        error(L, "'height' should be a number\n");

    *width = (int)lua_tonumber(L, -2);
    *height = (int)lua_tonumber(L, -1);

    lua_close(L);
}

int main(void)
{
    int width, height;
    load("c:\\luatest\\cfg.lua", &width, &height);
    printf("width = %d height = %d \n", width, height);
    return 0;
}

 

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
《C语言及程序设计》程序阅读——洗牌
返回:贺老师课程教学链接 //随机数发牌游戏的C语言实现: #include<stdio.h> #include<stdlib.h> #include<time.h> void initial(int* p)//初始化一付牌 { int i; for(i=0; i<52; i++) p[i]=(i/13+3)*
932 0
《C语言及程序设计》实践参考——M$pszi$y是嘛意思
返回:贺老师课程教学链接  实践要求 【项目1-M$pszi$y是嘛意思?】背景:小明让同学传纸条给小丽。小丽接到会心一笑,大家却不知所云。纸条上写着M$pszi$y,两人暗中约定是,真实字符为实际字符前面的第4个!M$pszi$y是神马意思?推算一下,或从ASCII码表中查一下,自然是I love u。(1)小明请你写一个程序,在给小丽写情书时,再不用费功夫自己“翻译”,原信中每一个字符加
1153 0
《C语言及程序设计》实践参考——字符串复制
返回:贺老师课程教学链接  实践要求 【项目3-字符串复制】下面的程序,将str1中除空格外的所有字符,复制到了str2中。#include &lt;stdio.h&gt; int main() { char str1[100]="I am a happy boy\'s daddy.",str2[100]; int i=0,j=0; while(str1[i]!='\
711 0
《C语言及程序设计》实践参考——字符串处理函数
返回:贺老师课程教学链接  实践要求 【项目4-字符串处理函数】指针是神奇的,指向整型的指针int *p1,可以操作整型数组int a[];指向字符型的指针char *p2,可以操作字符数组(字符串)char str[];更灵活的是,在函数的传递中,指针、数组名在一定程度上可以互换。请编制函数,对字符串的进行各种操作。 序 功能 用数组名作形参 用指针作形参 1 字符串str1和str
1309 0
《C语言及程序设计》实践参考——编程处理C源代码
返回:贺老师课程教学链接  实践要求 【项目5-编程处理C源代码】在CodeBlocks等IDE中都提供了代码格式整理的功能。我们可以编写程序,处理的数据就是用C写的源代码文件。C源文件是一种文本文件,可以通过程序进行操作。(1)读入一个C程序,判断其中是否只有一个main()函数,输出“暂时没有发现问题”,或者“没有main()函数”,或者“不能定义多个main()函数”;提示1:简单处
1015 0
《C语言及程序设计》程序阅读——二维数组
返回:贺老师课程教学链接 阅读下面的程序,写出输出结果。在阅读中,建议“画”出二维数组,其中有不少精彩的处理手法等待你去发现。(1)#include <stdio.h> int main( ) { int a[6][6],i,j ; for (i=1; i<6 ; i++) for (j=1 ; j<6 ; j++)
707 0
《C语言及程序设计》程序填空——二维数组
返回:贺老师课程教学链接 1、下面的程序,要输出行、列号之和为3的数组元素,请将程序补充完整。#include &lt;stdio.h&gt; int main( ) { char ss[4][3]= {'A','a','f','c','B','d','e','b', 'C','g','f','D'}; int x,y,z; for (x=0; ______(1)__
1351 0
《C语言及程序设计》实践项目——二维数组
返回:贺老师课程教学链接 【项目1-折腾二维数组】创建一个5行4列的二维整型数组,通过初始化,为数组中的前两列的10个元素赋初值,然后: 通过键盘输入,使后两列的10个元素获得值; 按行序优先输出数组元素; 将所有元素值乘以3后保存在数组中; 按列序优先输出(输出的第一行是数组中的第一列……,其实输出的就是“转置”); 将数组“倒”着输出(即最后一行最后一列的最先输出,第0行第0列的最后输出
1294 0
《C语言及程序设计》实践参考——折腾二维数组
返回:贺老师课程教学链接  【项目1-折腾二维数组】创建一个5行4列的二维整型数组,通过初始化,为数组中的前两列的10个元素赋初值,然后: 通过键盘输入,使后两列的10个元素获得值; 按行序优先输出数组元素; 将所有元素值乘以3后保存在数组中; 按列序优先输出(输出的第一行是数组中的第一列……,其实输出的就是“转置”); 将数组“倒”着输出(即最后一行最后一列的最先输出,第0行第0列的最后
1090 0
《C语言及程序设计》实践参考——矩阵运算
返回:贺老师课程教学链接  【项目2-矩阵运算】  在数学中,一个矩阵由若干行和若干列数据组成,可以直接存储为一个二维数组。(1)矩阵相加  两个矩阵相加,要求其行、列数均相等。运算规则为:一个n行m列的矩阵A加上另一个n行m列的矩阵,得到的结果是一个n行m列的矩阵C,C中的第i行第j列位置上的数等于A和B矩阵第i行第j列上数相加的和。例如:  请编程序,实现两个矩阵的加法。 [参考解答]
1241 0
+关注
jamesjxiao
游戏领域耕耘者~
文章
问答
文章排行榜
最热
最新
相关电子书
更多
React Native项目实战优化之路
立即下载
API 平台的安全实践
立即下载
ACE 区域技术发展峰会:Flink Python Table API入门及实践
立即下载