第一次接触Lua是由于Unity游戏中须要热更,可是一直没搞懂Lua是怎么嵌入到别的语言中执行的,如何互相调用的。此次打算好好了解一下C跟lua是如何交互的html
lua是c语言编写的,并且开源。能够在https://www.lua.org官网上下载Lua的源码,而后尝试编译它!是否是跟我同样好激动,一直用集成环境,写上层语言,今天竟然要碰编译了!!~ 可怎么编译呢?前端
让咱们召唤出编译神器:gcc!【GNU编译器套件(GNU Compiler Collection)包括C、C++、Objective-C、Fortran、Java、Ada和Go语言的前端,也包括了这些语言的库(如libstdc++、libgcj等等)。】c++
若是你安装了Homebrew的话,只要一行就能够了。macos
brew install gcc
装完后用curl
brew info gcc 或者 gcc -v
看一下是否是成功了
函数
当你安装好了编译器后,编译lua就变得很是简单了
Lua官网的文档里有说编译方式, 但MakeFile里默认的是编译成静态连接库,被这个坑了,后面再说性能
建议安装在/opt目录下ui
sudo su cd /opt curl -R -O http://www.lua.org/ftp/lua-5.3.4.tar.gz tar zxf lua-5.3.4.tar.gz cd lua-5.3.4 make macosx test make macosx install
安装好后用lua -v查看下若是有信息, 恭喜你,Lua编译好了!~lua
下面正式开干了~url
add.c内容
//你须要include这几个lua头文件 #include <stdio.h> #include "lua.h" #include "lualib.h" #include "lauxlib.h" lua_State* L; int luaadd(int x, int y) { int sum; /*函数名*/ lua_getglobal(L,"add"); /*参数入栈*/ lua_pushnumber(L, x); /*参数入栈*/ lua_pushnumber(L, y); /*开始调用函数,有2个参数,1个返回值*/ lua_call(L, 2, 1); /*取出返回值*/ sum = (int)lua_tonumber(L, -1); /*清除返回值的栈*/ lua_pop(L,1); return sum; } int main(int argc, char *argv[]) { int sum; L = luaL_newstate(); /* 建立lua状态机 */ luaL_openlibs(L); /* 打开Lua状态机中全部Lua标准库 */ /*加载lua脚本*/ luaL_dofile(L, "add.lua"); /*调用C函数,这个里面会调用lua函数*/ sum = luaadd(99, 10); printf("The sum is %d \n",sum); /*清除Lua*/ lua_close(L); return 0; }
add.lua放到与C同级的目录下,里面写一个简单的函数,让C调用
function add(x,y) return x + y end
好了,终于到了用GCC编译的阶段了,直接gcc add.c一下看看行不行。
果真报错了!
这是由于没有把add.c里面的函数连接到咱们前面编译出来的lua库里致使的。怎么让他指定连接哪一个库呢?看GCC的文档得知-l参数能够指定要连接的库
-l参数和-L参数
-l参数就是用来指定程序要连接的库,-l参数紧接着就是库名,那么库名跟真正的库文
件名有什么关系呢?
就拿数学库来讲,他的库名是m,他的库文件名是libm.so,很容易看出,把库文件名的
头lib和尾.so去掉就是库名了
那咱们再试一下,gcc add.c -llua,此次编译出来了: a.out
执行成功!
Lua调用C,我了解到的有3种方式
1.经过在C中注册函数给lua调用
2.封装成c动态连接库,在lua中require
3.在LuaJIT里面可使用ffi高性能的调用C(可是IOS上不支持LuaJIT。。)
1.在C中注册函数给Lua
lua提供了lua_register函数注册C函数给lua端调用
hello.c
#include <stdio.h> #include <string.h> #include "lua.h" #include "lualib.h" #include "lauxlib.h" static int l_SayHello(lua_State *L) { const char *d = luaL_checkstring(L, 1);//获取参数,字符串类型 int len = strlen(d); char str[100] = "hello "; strcat(str, d); lua_pushstring(L, str); /* 返回给lua的值压栈 */ return 1; } int main(int argc, char *argv[]) { lua_State *L = luaL_newstate(); /* 建立lua状态机 */ luaL_openlibs(L); /* 打开Lua状态机中全部Lua标准库 */ lua_register(L, "SayHello", l_SayHello);//注册C函数到lua const char* testfunc = "print(SayHello('lijia'))";//lua中调用c函数 if(luaL_dostring(L, testfunc)) // 执行Lua命令。 printf("Failed to invoke.\n"); /*清除Lua*/ lua_close(L); return 0; }
gcc -o hello hello.c -llua编译执行
2.调用C动态连接库
建立一个mylib.c的文件,而后咱们把它编译成动态连接库
#include <stdio.h> #include <math.h> #include <stdarg.h> #include <stdlib.h> #include <lua.h> #include <lauxlib.h> #include <lualib.h> /* 全部注册给Lua的C函数具备 * "typedef int (*lua_CFunction) (lua_State *L);"的原型。 */ static int l_sin(lua_State *L) { // 若是给定虚拟栈中索引处的元素能够转换为数字,则返回转换后的数字,不然报错。 double d = luaL_checknumber(L, 1); lua_pushnumber(L, sin(d)); /* push result */ /* 这里能够看出,C能够返回给Lua多个结果, * 经过屡次调用lua_push*(),以后return返回结果的数量。 */ return 1; /* number of results */ } /* 须要一个"luaL_Reg"类型的结构体,其中每个元素对应一个提供给Lua的函数。 * 每个元素中包含此函数在Lua中的名字,以及该函数在C库中的函数指针。 * 最后一个元素为“哨兵元素”(两个"NULL"),用于告诉Lua没有其余的函数须要注册。 */ static const struct luaL_Reg mylib[] = { {"mysin", l_sin}, {NULL, NULL} }; /* 此函数为C库中的“特殊函数”。 * 经过调用它注册全部C库中的函数,并将它们存储在适当的位置。 * 此函数的命名规则应遵循: * 一、使用"luaopen_"做为前缀。 * 二、前缀以后的名字将做为"require"的参数。 */ extern int luaopen_mylib(lua_State* L) { /* void luaL_newlib (lua_State *L, const luaL_Reg l[]); * 建立一个新的"table",并将"l"中所列出的函数注册为"table"的域。 */ luaL_newlib(L, mylib); return 1; }
使用gcc -o mylib.so -fPIC -shared mylib.c -llua -ldl编译成so
而后建立一个lua文件,把咱们编译出来的c库引入进来
--[[ 这里"require"的参数对应C库中"luaopen_mylib()"中的"mylib"。 C库就放在"a.lua"的同级目录,"require"能够找到。]] local mylib = require "mylib" -- 结果与上面的例子中相同,可是这里是经过调用C库中的函数实现。 print(mylib.mysin(3.14 / 2)) --> 0.99999968293183
执行a.lua文件,后报错,说Lua存在多个虚拟机!
lua: multiple Lua VMs detected
为何呢?查了一些资料发现由于lua默认编译的是静态连接库,这样会致使连接多个VM冲突。
那么咱们本身再编译个lua解释器动态连接一下。
mylua.c
#include <stdio.h> #include "lua.h" #include "lualib.h" #include "lauxlib.h" int main() { lua_State *L = luaL_newstate(); luaL_openlibs(L); if (luaL_loadfile(L, "a.lua") || lua_pcall(L, 0, 0, 0)) { printf("%s", lua_tostring(L, -1)); } }
gcc -o mylua mylua.c -llua -ldl -lm -Wall
这样就能编译出mylua可执行文件
在命令行./mylua执行,成功打印出0.99999968293183
gcc命令,编译lua,编译C动态连接库这些以前都接触的比较少。因此也爬了很多坑,哈哈哈。接下来要好好研究下怎么在c中解析二进制协议给lua调用,在c中怎么封装好luatable
参考资料:
https://www.cnblogs.com/pied/archive/2012/10/26/2741601.html
http://blog.csdn.net/vermilliontear/article/details/50947379
http://blog.csdn.net/casularm/article/details/316149