Lua利用一个虚拟的堆栈来给C传递值或从C获取值。每当Lua调用C函数,都会获得一个新的堆栈,该堆栈初始包含所有的调用C函数所需要的参数值(Lua传给C函数的调用实参),并且C函数执行完毕后,会把返回值压入这个栈(Lua从中拿到C函数调用结果)。
于此相关的C API有几个比较重要的定义如下:
(1)typedef struct lua_State lua_State;
lua虚拟机(或叫解释器),可以理解为一个thread,和一个完整的Lua虚拟环境的执行状态。
(2)typedef int (*lua_CFunction) (lua_State *L);
能够被Lua调用的C函数都必须是这种规则。函数的返回的int值表示C函数返回值的个数。
(3)void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);
将一个C闭包压栈;
首先将upvalues依次压栈,然后调用该函数,将c函数压栈,并将n个upvalues出栈;
参数fn:C函数指针
参数n:函数关联的upvalue的个数。
(4)void lua_pushcfunction (lua_State *L, lua_CFunction f);
将C函数压栈;
接收一个C函数的指针参数,然后将一个Lua.function类型的对象压栈。
(5)void lua_register (lua_State *L, const char *name, lua_CFunction f);
注册C函数为一个全局变量;
#define lua_register(L,n,f) (lua_pushcfunction(L, f), lua_setglobal(L, n))
(6)void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup);
注册函数到栈顶的表中;
参数l:luaL_Reg列表,记录了要注册的函数信息,注意,该列表以{NULL, NULL}结尾;
nup参数:upvalue的个数,如果不为0,则注册的所有函数都共享这些upvalues;
先将table压栈,然后将upvalues依次压栈,然后调用该函数进行函数注册。注册完毕后upvalues会出栈。
注意:luaL_register函数已经不再使用,取而代之的是luaL_setfuncs,因为该函数不会创建全局变量。
typedef struct luaL_Reg { const char *name; lua_CFunction func; } luaL_Reg;
Lua可以调用C函数的能力将极大的提高Lua的可扩展性和可用性。对于有些和操作系统相关的功能,或者是对效率要求较高的模块,我们完全可以通过C函数来实现,之后再通过Lua调用指定的C函数。对于那些可被Lua调用的C函数而言,其接口必须遵循Lua要求的形式,即typedef int (*lua_CFunction)(lua_State* L)。简单说明一下,该函数类型仅仅包含一个表示Lua环境的指针作为其唯一的参数,实现者可以通过该指针进一步获取Lua代码中实际传入的参数。返回值是整型,表示该C函数将返回给Lua代码的返回值数量,如果没有返回值,则return 0即可。需要说明的是,C函数无法直接将真正的返回值返回给Lua代码,而是通过虚拟栈来传递Lua代码和C函数之间的调用参数和返回值的。这里我们将介绍两种Lua调用C函数的规则。
1. C函数作为应用程序的一部分
#include <stdio.h>
#include <string.h>
#include <lua.hpp>
#include <lauxlib.h>
#include <lualib.h> //待Lua调用的C注册函数。
static int add2(lua_State* L)
{
//检查栈中的参数是否合法,1表示Lua调用时的第一个参数(从左到右),依此类推。
//如果Lua代码在调用时传递的参数不为number,该函数将报错并终止程序的执行。
double op1 = luaL_checknumber(L,);
double op2 = luaL_checknumber(L,);
//将函数的结果压入栈中。如果有多个返回值,可以在这里多次压入栈中。
lua_pushnumber(L,op1 + op2);
//返回值用于提示该C函数的返回值数量,即压入栈中的返回值数量。
return ;
} //另一个待Lua调用的C注册函数。
static int sub2(lua_State* L)
{
double op1 = luaL_checknumber(L,);
double op2 = luaL_checknumber(L,);
lua_pushnumber(L,op1 - op2);
return ;
} const char* testfunc = "print(add2(1.0,2.0)) print(sub2(20.1,19))"; int main()
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);
//将指定的函数注册为Lua的全局函数变量,其中第一个字符串参数为Lua代码
//在调用C函数时使用的全局函数名,第二个参数为实际C函数的指针。
lua_register(L, "add2", add2);
lua_register(L, "sub2", sub2);
//在注册完所有的C函数之后,即可在Lua的代码块中使用这些已经注册的C函数了。
if (luaL_dostring(L,testfunc))
printf("Failed to invoke.\n");
lua_close(L);
return ;
}
2. C函数库成为Lua的模块
将包含C函数的代码生成库文件,如Linux的so,或Windows的DLL,同时拷贝到Lua代码所在的当前目录,或者是LUA_CPATH环境变量所指向的目录,以便于Lua解析器可以正确定位到他们。在我当前的Windows系统中,我将其copy到"C:\Program Files\Lua\5.1\clibs\",这里包含了所有Lua可调用的C库。见如下C语言代码和关键性注释:
#include <stdio.h>
#include <string.h>
#include <lua.hpp>
#include <lauxlib.h>
#include <lualib.h> //待注册的C函数,该函数的声明形式在上面的例子中已经给出。
//需要说明的是,该函数必须以C的形式被导出,因此extern "C"是必须的。
//函数代码和上例相同,这里不再赘述。
extern "C" int add(lua_State* L)
{
double op1 = luaL_checknumber(L,);
double op2 = luaL_checknumber(L,);
lua_pushnumber(L,op1 + op2);
return ;
} extern "C" int sub(lua_State* L)
{
double op1 = luaL_checknumber(L,);
double op2 = luaL_checknumber(L,);
lua_pushnumber(L,op1 - op2);
return ;
} //luaL_Reg结构体的第一个字段为字符串,在注册时用于通知Lua该函数的名字。
//第一个字段为C函数指针。
//结构体数组中的最后一个元素的两个字段均为NULL,用于提示Lua注册函数已经到达数组的末尾。
static luaL_Reg mylibs[] = {
{"add", add},
{"sub", sub},
{NULL, NULL}
}; //该C库的唯一入口函数。其函数签名等同于上面的注册函数。见如下几点说明:
//1. 我们可以将该函数简单的理解为模块的工厂函数。
//2. 其函数名必须为luaopen_xxx,其中xxx表示library名称。Lua代码require "xxx"需要与之对应。
//3. 在luaL_register的调用中,其第一个字符串参数为模块名"xxx",第二个参数为待注册函数的数组。
//4. 需要强调的是,所有需要用到"xxx"的代码,不论C还是Lua,都必须保持一致,这是Lua的约定,
// 否则将无法调用。
extern "C" __declspec(dllexport)
int luaopen_mytestlib(lua_State* L)
{
const char* libName = "mytestlib";
luaL_register(L,libName,mylibs);
return ;
}
见如下Lua代码:
require "mytestlib" --指定包名称 --在调用时,必须是package.function
print(mytestlib.add(1.0,2.0))
print(mytestlib.sub(20.1,))
参考:http://www.cnblogs.com/stephen-liu74/archive/2012/07/23/2469902.html
Lua中调用C函数的更多相关文章
-
lua中调用C++函数
lua中调用C++函数 我们产品中提供了很多lua-C API给用户在lua中调用,之前一直没用深究其实现原理,只是根据已有的代码在编码.显然这不是一个好的习惯,没用达到知其所以然的目的. 一.基本原 ...
-
Lua中调用C函数(lua-5.2.3)
Lua能够调用C函数的能力将极大的提高Lua的可扩展性和可用性. 对于有些和操作系统相关的功能,或者是对效率要求较高的模块,我们全然能够通过C函数来实现,之后再通过Lua调用指定的C函数. 对于那些可 ...
-
lua如何调用C++函数
第一步是定义函数.所有在Lua中被调用的C/C++函数将使用下面一类指针进行调用: typedef int (*lua_CFunction) (lua_State *L); 换句话说,函数必须要以Lu ...
-
cocos2d-x 2.2.0 如何在lua中注册回调函数给C++
cocos2d-x内部使用tolua进行lua绑定,但是引擎并没有提供一个通用的接口让我们可以把一个lua函数注册给C++层面的回调事件.翻看引擎的lua绑定代码,我们可以仿照引擎中的方法来做.值得吐 ...
-
Lua中调用C++方法
目前项目,使用了Lua脚本,至于使用Lua的好处不再赘述了.于是对Tolua做了一些小小的学习,总结一下吧. 主要说一下如何在Lua中调用C++方法. Lua调用C++的桥梁,是tolua.tolua ...
-
EC笔记,第二部分:9.不在构造、析构函数中调用虚函数
9.不在构造.析构函数中调用虚函数 1.在构造函数和析构函数中调用虚函数会产生什么结果呢? #; } 上述程序会产生什么样的输出呢? 你一定会以为会输出: cls2 make cls2 delete ...
-
关于在C#中构造函数中调用虚函数的问题
在C#中如果存在类的继承关系,应避免在构造函数中调用虚函数.这是由于C#的运行机制造成的,原因如下: 新建一个类实例时,C#会先初始化该类(对类变量赋值,并将函数记在函数表中),然后再初始化父类.构造 ...
-
如何在C语言中调用Swift函数
在Apple官方的<Using Swift with Cocoa and Objectgive-C>一书中详细地介绍了如何在Objective-C中使用Swift的类以及如何在Swift中 ...
-
C++ 构造函数中调用虚函数
我们知道:C++中的多态使得可以根据对象的真实类型(动态类型)调用不同的虚函数.这种调用都是对象已经构建完成的情况.那如果在构造函数中调用虚函数,会怎么样呢? 有这么一段代码: class A { p ...
随机推荐
-
深入浅出设计模式——中介者模式(Mediator Pattern)
模式动机 在用户与用户直接聊天的设计方案中,用户对象之间存在很强的关联性,将导致系统出现如下问题: 系统结构复杂:对象之间存在大量的相互关联和调用,若有一个对象发生变化,则需要跟踪和该对象关联的其他 ...
-
c++标准库中几个常见的数据结构的区别和应用规则
转载自http://www.lifecrunch.biz/archives/202 vector和built-in数组类似,它拥有一段连续的内存空间,并且起始地址不变,因此它能非常好的支持随即存取,即 ...
-
goldengate 12c对teradata的支持
OGG12c在2014.4.24发布了对TD的最新支持版本,软件可在此下载:https://edelivery.oracle.com.新版本特性如下: 当前支持TD 14.1的捕获和交付,仍然基于Te ...
-
Android工程目录及其作用简介
1. src:存放所有的*.java源程序. 2. gen:为ADT插件自动生成的代码文件保存路径,里面的R.java将保存所有的资源ID. 3. assets:可以存放项目一些较大的资源文件,例如: ...
-
我用Emacs,后来转向Vim——Vim学习之Vim键盘图(绝对值得珍藏)
Emacs本来就比较臃肿,麻烦.当我发现Vim键盘图时,我就渐渐转向Vim,追随Unix/Linux哲学去了.. 我用了Emacs三个月,因为它的学习曲线没Vim陡,这点吸引了,我使用Linux才7. ...
-
HTML知识点摘记
HTML HTML:(Hype Text Markup Language)超文本标记语言,是最基本的网页语言.代码由标签组成,不区分大小写. 由<html>开始,</html> ...
-
stm32 DMA数据搬运 [操作寄存器+库函数](转)
源:stm32 DMA数据搬运 [操作寄存器+库函数] DMA(Direct Memory Access)常译为“存储器直接存取”.早在Intel的8086平台上就有了DMA应用了. ...
-
Python操作MongoDB和Redis
1. python对mongo的常见CURD的操作 1.1 mongo简介 mongodb是一个nosql数据库,无结构化.和去中心化. 那为什么要用mongo来存呢? 1. 首先.数据关系复杂,没有 ...
-
Windows10远程桌面Ubuntu16.04
一.Ubuntu16.04端软件安装(管理员权限) 1.安装xrdp sudo apt-get install xrdp 2.安装vnc4server sudo apt-get install vnc ...
-
PBN飞越转弯Flyover衔接DF航段保护区组图
PBN飞越转弯Flyover衔接DF航段,是飞越转弯中最常用的形态. Flyover-DF保护区叠加图: Flyover-DF保护区分解图: