@(语言)

Lua和C 函数间的使用,都是通过栈来交互,并且基于遵守一定的规则,按照这个规则来就可以了。

1. 调用Lua函数

调用Lua方法过程

  1. 将被调用的函数入栈;
  2. 依次将所有参数入栈;
  3. 使用 lua_pcall 调用函数;
  4. 从栈中获取函数执行返回的结果。

** lua_pcall** 函数

lua_pcall (lua_State *L, int nargs, int nresults, int errfunc);

Calls a function in protected mode.

Both nargs and nresults have the same meaning as in lua_call. If there are no errors during the call, lua_pcall behaves exactly like lua_call. However, if there is any error, lua_pcall catches it, pushes a single value on the stack (the error message), and returns an error code. Like lua_call, lua_pcall always removes the function and its arguments from the stack.

If errfunc is 0, then the error message returned on the stack is exactly the original error message. Otherwise, errfunc is the stack index of an error handler function. (In the current implementation, this index cannot be a pseudo-index.) In case of runtime errors, this function will be called with the error message and its return value will be the message returned on the stack by lua_pcall.

Typically, the error handler function is used to add more debug information to the error message, such as a stack traceback. Such information cannot be gathered after the return of lua_pcall, since by then the stack has unwound.

The lua_pcall function returns 0 in case of success or one of the following error codes (defined in lua.h):

LUA_ERRRUN: a runtime error.

LUA_ERRMEM: memory allocation error. For such errors, Lua does not call the error handler function.

LUA_ERRERR: error while running the error handler function.

lua_pcall 时指定参数的个数和返回结果的个数,第四个参数可以指定一个错误处理函数。

在将结果入栈之前, lua_pcall 会将栈内的函数和参数移除.

如果 lua_pcall 运行时出现错误, lua_pcall 会返回一个非 0 的结果。另外,他将错误信息入栈(仍然会先将函数和参数从栈中移除)。在将错误信息入栈之前,如果指定了错误处理函数, lua_pcall会用错误处理函数。

lua_call 函数

void lua_call (lua_State *L, int nargs, int nresults);

功能与lua_pcall相同,错误时直接抛出错误,而不是返回错误码

案例:

Lua方法:

function f (x, y)
return (x^2 * math.sin(y))/(1 - x)
end

C中调用:

/* call a function `f' defined in Lua */
double f (double x, double y) {
double z;
/* push functions and arguments */
lua_getglobal(L, "f"); /* function to be called */
lua_pushnumber(L, x); /* push 1st argument */
lua_pushnumber(L, y); /* push 2nd argument */
/* do the call (2 arguments, 1 result) */
if (lua_pcall(L, 2, 1, 0) != 0)
error(L, "error running function `f': %s",
lua_tostring(L, -1));
/* retrieve result */
if (!lua_isnumber(L, -1))
error(L, "function `f' must return a number");
z = lua_tonumber(L, -1);
lua_pop(L, 1); /* pop returned value */
return z;
}

2. 调用C函数

当 Lua 调用 C 函数的时候,使用和 C 调用 Lua 相同类型的栈来交互。 C 函数从栈中获取她的参数,调用结束后将返回结果放到栈中。需要注意:

  1. 每个 C 函数还会返回结果的个数( the function returns (in C) the number of results it isleaving on the stack.)
  2. Lua调用 C 函数我们必须注册函数,也就是把 C 函数的地址以一个适当的方式传递给 Lua 解释器。
  3. 用来交互的栈不是全局变量,每一个函数都有他自己的私有栈。当 Lua 调用 C 函数的时候,第一个参数总是在这个私有栈的index=1 的位置。甚至当一个 C 函数调用 Lua 代码( Lua 代码调用同一个 C 函数或者其他的 C 函数),每一个 C 函数都有自己的独立的私有栈,并且第一个参数在 index=1 的位置

C 函数

C函数规范:

typedef int (*lua_CFunction) (lua_State *L);

一个 C 函数接受单一的参数 Lua state,返回一个表示返回值个数的数字。所以,函数在将返回值入栈之前不需要清理栈,函数返回之后, Lua 自动的清除栈中返回结果下面的所有内容。

注册接口

void lua_pushcfunction (lua_State *L, lua_CFunction f);
//其定义
#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0)
//方便接口:
#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))

案例

//定义
static int l_sin (lua_State *L)
{
double d = luaL_checknumber(L, 1);
lua_pushnumber(L, sin(d));
return 1; /* number of results */
}
//注册
lua_pushcfunction(l, l_sin);
lua_setglobal(l, "mysin");

C函数库

Lua 通过注册,就可以看到库中的 C 函数。一旦一个 C 函数被注册之后并保存到 Lua 中,在 Lua 程序中就可以直接引用他的地址(当我们注册这个函数的时候传递给 Lua 的地址)来访问这个函数了。luaL_openlib 函数接受一个 C 函数的列表和他们对应的函数名,并且作为一个库在一个 table 中注册所有这些函数。大致步骤如下:

//1. 定义函数
static int l_dir (lua_State *L)
{
... /* as before */
}
//2.导出声明数组
static const struct luaL_reg mylib [] =
{
{ "dir", l_dir},
{NULL, NULL} /* sentinel */
}; //3. luaL_openlib 声明,注意数组中最后一对必须是{NULL,NULL},用来表示结束
int luaopen_mylib (lua_State *L)
{
luaL_openlib(L, "mylib", mylib, 0);
return 1;
} //4. lua中加载
mylib = loadlib("fullname-of-your-library", "luaopen_mylib")

字符串操作

当 C 函数接受一个来自 lua 的字符串作为参数时,有两个规则必须遵守:

  1. 当字符串正在被访问的时候不要将其出栈;
  2. 永远不要修改字符串。

当 C 函数需要创建一个字符串返回给 lua 的时候:

字符串操作中需要注意的其他:

  1. 永远不要将指向 Lua 字符串的指针保存到访问他们的外部函数中,因为lua_tostring 返回的是指向内部字符串的内部拷贝指针2. lua_tonumber 返回的字符串结尾总会有一个字符结束标志 0,但是字符串中间也可能包含 0, lua_strlen 返回字符串的实际长度

结束语

函数操作过程中需要注意栈的容量,及时进行清除。函数操作过程中操作更多的是对参数的处理,比如说参数是C定义的类,下篇文章介绍

Lua 与 C 交互值 函数调用(2)的更多相关文章

  1. Lua和C++交互 学习记录之三:全局值交互

    主要内容转载自:子龙山人博客(强烈建议去子龙山人博客完全学习一遍) 部分内容查阅自:<Lua 5.3  参考手册>中文版 译者 云风 制作 Kavcc vs2013+lua-5.3.3 1 ...

  2. Lua和C++交互详细总结

    转自:http://cn.cocos2d-x.org/tutorial/show?id=1474 一.Lua堆栈 要理解Lua和C++交互,首先要理解Lua堆栈. 简单来说,Lua和C/C++语言通信 ...

  3. 用好lua+unity,让性能飞起来——lua与c#交互篇

    前言 在看了uwa之前发布的<Unity项目常见Lua解决方案性能比较>,决定动手写一篇关于lua+unity方案的性能优化文. 整合lua是目前最强大的unity热更新方案,毕竟这是唯一 ...

  4. [转载]Lua和C++交互详细总结

    原文请看:Lua和C++交互详细总结 转自:http://cn.cocos2d-x.org/tutorial/show?id=1474 一.Lua堆栈 要理解Lua和C++交互,首先要理解Lua堆栈. ...

  5. Lua和C交互的简易教程

    转载请标明出处:http://blog.csdn.net/shensky711/article/details/52458051 本文出自: [HansChen的博客] Lua栈 要理解Lua和C++ ...

  6. Lua与C++交互初探之Lua调用C++

    Lua与C++交互初探之Lua调用C++ 上一篇我们已经成功将Lua的运行环境搭建了起来,也成功在C++里调用了Lua函数.今天我来讲解一下如何在Lua里调用C++函数. Lua作为一个轻量级脚本语言 ...

  7. Lua 和 C 交互中虚拟栈的操作

    Lua 和 C 交互中虚拟栈的操作 /* int lua_pcall(lua_State *L, int nargs, int nresults, int msgh) * 以保护模式调用具有" ...

  8. Lua和C++交互 学习记录之九:在Lua中以面向对象的方式使用C++注册的类

    主要内容转载自:子龙山人博客(强烈建议去子龙山人博客完全学习一遍) 部分内容查阅自:<Lua 5.3  参考手册>中文版 译者 云风 制作 Kavcc vs2013+lua-5.3.3 在 ...

  9. Lua和C++交互 学习记录之八:C++类注册为Lua模块

    主要内容转载自:子龙山人博客(强烈建议去子龙山人博客完全学习一遍) 部分内容查阅自:<Lua 5.3  参考手册>中文版 译者 云风 制作 Kavcc vs2013+lua-5.3.3 1 ...

随机推荐

  1. Django models 常用数据类型

    CharField class CharField(max_length=None[, **options]) # 字符串(存储从小到大各种长度) # 如果是巨大的文本类型,可以用 TextField ...

  2. C#基础知识回顾-- 属性与字段

    今天在公交车上,突然想属性和字段到底有什么区别?很多字段属性都存在 get{}和set{} 和普通的变量没什么区别(可读可写) 我就感觉属性就是给字段一个多的选择方式,有的字段是不允许更改的.. 刚写 ...

  3. WebFrom 小程序【条件查询】

    实现按照各种条件对数据库进行综合查询 基本功能:可以根据用户需要灵活查询 重难点:各种条件的可能.限制. public List<users> selectA( string str,Ha ...

  4. 使用Nexus2.x为Maven3.x搭建私服构件仓库

    前言 在笔者的前一篇博文<Use Maven3.x>中,笔者详细的讲解了如何通过使用Maven3.x来构建及管理你的项目.其中笔者提到过些关于私服的概念,但却没有明确的对私服做出详细的阐述 ...

  5. Install/Remove of the Service Denied!

    在windos 的cmd下安装mysql 在mysql的bin目录下面执行: mysqld --install 报错: 信息如下一: Install/Remove of the Service Den ...

  6. 【协议】4、http状态码

    10.4 客户错误 4xx 状态代码4xx类是专门使用在客户看上去错误的情形下的.除非当应答一个HEAD请求时,服务器因该有一个包括错误情形的解释的实体,以及它是否一个临时或者终了的条件.这些状态编码 ...

  7. UDP服务器/客户端代码示例

    UDP服务器代码: #include <errno.h> #include <string.h> #include <stdlib.h> #include < ...

  8. DotNetCore 结合 Nginx 将网站部署到阿里云

    基础环境配置 域名和服务器请先自行购买 基于 云服务器ECS 创建一个应用实例,选择系统镜像为 Ubuntu 16.04,在本机通过 SSH 进行远程连接,并进行相关配置 ssh root@http: ...

  9. nginx重启服务

    修改完nginx配置后,需要使用 nginx -s reload使修改的配置生效,配置生效是平滑的,不会对访问产生任何影响reload后会启动新的进程接受新请求,对于未处理完的请求还是用老的配置,直到 ...

  10. React 入门学习笔记整理(四)—— 事件

    1.事件定义 React事件绑定属性的命名采用驼峰式写法,而不是小写. 如果采用 JSX 的语法你需要传入一个函数作为事件处理函数,而不是一个字符串(DOM元素的写法) 在类组件中定义函数,通过thi ...