C语言与Lua之间的相互调用详解

 更新时间:2020年4月25日 17:28  点击:2326

前言

第一次接触Lua是因为Unity游戏中需要热更,但是一直没搞懂Lua是怎么嵌入到别的语言中执行的,如何互相调用的。

lua是扩展性非常良好的语言,虽然核心非常精简,但是用户可以依靠lua库来实现大部分工作。除此之外,lua还可以通过与C函数相互调用来扩展程序功能。在C中嵌入lua脚本既可以让用户在不重新编译代码的情况下修改lua代码更新程序,也可以给用户提供一个自由定制的接口,这种方法遵循了机制与策略分离的原则。在lua中调用C函数可以提高程序的运行效率。lua与C的相互调用在工程中相当实用,本文就来讲解lua与C相互调用的方法。这次打算好好了解一下C跟lua是如何交互的

那么如何使用Lua语言?

lua是c语言编写的,而且开源。可以在https://www.lua.org官网上下载Lua的源码,然后尝试编译它!是不是跟我一样好激动,一直用集成环境,写上层语言,今天居然要碰编译了!!~ 可怎么编译呢?

让我们召唤出编译神器:gcc!【GNU编译器套件(GNU Compiler Collection)包括C、C++、Objective-C、Fortran、Java、Ada和Go语言的前端,也包括了这些语言的库(如libstdc++、libgcj等等)。】

在Mac上安装GCC

如果你安装了Homebrew的话,只要一行就可以了。

brew install gcc

装完后用

brew info gcc

或者

gcc -v

看一下是不是成功了


编译Lua

当你安装好了编译器后,编译lua就变得非常简单了

Lua官网的文档里有说编译方式, 但MakeFile里默认的是编译成静态链接库,被这个坑了,后面再说

建议安装在/opt目录下

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编译好了!~

下面正式开干了~

写一个C调用Lua的Demo编译运行

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?

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库中的函数,并将它们存储在适当的位置。
 * 此函数的命名规则应遵循:
 * 1、使用"luaopen_"作为前缀。
 * 2、前缀之后的名字将作为"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

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对猪先飞的支持。

[!--infotagslink--]

相关文章

  • C语言实现放烟花的程序

    这篇文章主要为大家详细介绍了C语言实现放烟花的程序,有音乐播放,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-02-23
  • C语言中的字符(char)详细讲解

    本篇文章主要介绍C语言中char的知识,并附有代码实例,以便大家在学习的时候更好的理解,有需要的可以看一下...2020-04-25
  • 详解如何将c语言文件打包成exe可执行程序

    这篇文章主要介绍了详解如何将c语言文件打包成exe可执行程序,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-25
  • Lua语言新手简单入门教程

    这篇文章主要给大家介绍的是关于Lua语言新手入门的简单教程,文中通过示例代码一步步介绍的非常详细,对各位新手们的入门提供了一个很方便的教程,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧。...2020-06-30
  • lua读取redis数据的null判断示例代码

    最近在工作中遇到了一个问题,通过查找相关资料才得知原因是因为返回结果的问题,下面这篇文章主要给大家介绍了关于lua读取redis数据的null判断的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下...2020-06-30
  • C语言中free函数的使用详解

    free函数是释放之前某一次malloc函数申请的空间,而且只是释放空间,并不改变指针的值。下面我们就来详细探讨下...2020-04-25
  • C语言中计算正弦的相关函数总结

    这篇文章主要介绍了C语言中计算正弦的相关函数总结,包括正弦和双曲线正弦以及反正弦的函数,需要的朋友可以参考下...2020-04-25
  • 详解C语言中的rename()函数和remove()函数的使用方法

    这篇文章主要介绍了详解C语言中的rename()函数和remove()函数的使用方法,是C语言入门学习中的基础知识,需要的朋友可以参考下...2020-04-25
  • C语言中求和、计算平均值、方差和标准差的实例

    这篇文章主要介绍了C语言中求和、计算平均值、方差和标准差的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-12-10
  • C语言的基本语法详解

    本篇文章主要讲解C语言 基本语法,这里提供简单的示例和代码来详细讲解C语言的基本语法,开始学习C语言的朋友可以看一下,希望能够给你带来帮助...2021-09-18
  • C语言中send()函数和sendto()函数的使用方法

    这篇文章主要介绍了C语言中send()函数和sendto()函数的使用方法,是C语言入门学习中的基础知识,需要的朋友可以参考下...2020-04-25
  • C语言实现从文件读入一个3*3数组,并计算每行的平均值

    今天小编就为大家分享一篇C语言实现从文件读入一个3*3数组,并计算每行的平均值,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-04-25
  • 使用C语言操作文件的基本函数整理

    这篇文章主要介绍了使用C语言操作文件的基本函数整理,包括创建和打开以及关闭文件的操作方法,需要的朋友可以参考下...2020-04-25
  • C语言中memcpy 函数的用法详解

    这篇文章主要介绍了C语言中memcpy 函数的用法详解的相关资料,需要的朋友可以参考下...2020-04-25
  • C语言中查找字符在字符串中出现的位置的方法

    这篇文章主要介绍了C语言中查找字符在字符串中出现的位置的方法,分别是strchr()函数和strrchr()函数的使用,需要的朋友可以参考下...2020-04-25
  • C语言菜鸟基础教程之a++与++a

    很多同学在学习c语言的时候是不是会碰到a++和++a都有甚么作用啊。今天我们就来探讨下...2020-04-25
  • Lua判断变量是否为数字、字符串是否可以转换为数字等

    这篇文章主要介绍了Lua判断变量是否为数字、字符串是否可以转换为数字等,本文讲解了Lua 判断是字符还是数字的方法、Lua判断数字的方法、判断可否转换为数字的方法、判断并且准备一个初值的方法,需要的朋友可以参考下...2020-06-30
  • C语言实现时间戳转日期的算法(推荐)

    下面小编就为大家带来一篇C语言实现时间戳转日期的算法(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-04-25
  • 详解C语言中const关键字的用法

    这篇文章主要对C语言中const关键字的用法进行了详细的分析介绍,需要的朋友可以参考下...2020-04-25
  • C语言之整数划分问题(递归法)实例代码

    这篇文章主要介绍了C语言之整数划分问题(递归法)实例代码的相关资料,需要的朋友可以参考下...2020-04-25