C語言與Lua之間的相互調(diào)用詳解
前言
第一次接觸Lua是因為Unity游戲中需要熱更,但是一直沒搞懂Lua是怎么嵌入到別的語言中執(zhí)行的,如何互相調(diào)用的。
lua是擴展性非常良好的語言,雖然核心非常精簡,但是用戶可以依靠lua庫來實現(xiàn)大部分工作。除此之外,lua還可以通過與C函數(shù)相互調(diào)用來擴展程序功能。在C中嵌入lua腳本既可以讓用戶在不重新編譯代碼的情況下修改lua代碼更新程序,也可以給用戶提供一個自由定制的接口,這種方法遵循了機制與策略分離的原則。在lua中調(diào)用C函數(shù)可以提高程序的運行效率。lua與C的相互調(diào)用在工程中相當實用,本文就來講解lua與C相互調(diào)用的方法。這次打算好好了解一下C跟lua是如何交互的
那么如何使用Lua語言?
lua是c語言編寫的,而且開源??梢栽趆ttps://www.lua.org官網(wǎng)上下載Lua的源碼,然后嘗試編譯它!是不是跟我一樣好激動,一直用集成環(huán)境,寫上層語言,今天居然要碰編譯了??!~ 可怎么編譯呢?
讓我們召喚出編譯神器:gcc!【GNU編譯器套件(GNU Compiler Collection)包括C、C++、Objective-C、Fortran、Java、Ada和Go語言的前端,也包括了這些語言的庫(如libstdc++、libgcj等等)?!?/p>
在Mac上安裝GCC
如果你安裝了Homebrew的話,只要一行就可以了。
brew install gcc
裝完后用
brew info gcc
或者
gcc -v
看一下是不是成功了
編譯Lua
當你安裝好了編譯器后,編譯lua就變得非常簡單了
Lua官網(wǎng)的文檔里有說編譯方式, 但MakeFile里默認的是編譯成靜態(tài)鏈接庫,被這個坑了,后面再說
建議安裝在/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調(diào)用Lua的Demo編譯運行
add.c內(nèi)容
//你需要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; /*函數(shù)名*/ lua_getglobal(L,"add"); /*參數(shù)入棧*/ lua_pushnumber(L, x); /*參數(shù)入棧*/ lua_pushnumber(L, y); /*開始調(diào)用函數(shù),有2個參數(shù),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(); /* 創(chuàng)建lua狀態(tài)機 */ luaL_openlibs(L); /* 打開Lua狀態(tài)機中所有Lua標準庫 */ /*加載lua腳本*/ luaL_dofile(L, "add.lua"); /*調(diào)用C函數(shù),這個里面會調(diào)用lua函數(shù)*/ sum = luaadd(99, 10); printf("The sum is %d \n",sum); /*清除Lua*/ lua_close(L); return 0; }
add.lua放到與C同級的目錄下,里面寫一個簡單的函數(shù),讓C調(diào)用
function add(x,y) return x + y end
好了,終于到了用GCC編譯的階段了,直接gcc add.c一下看看行不行。
果然報錯了!
這是因為沒有把add.c里面的函數(shù)鏈接到我們前面編譯出來的lua庫里導致的。怎么讓他指定鏈接哪個庫呢?看GCC的文檔得知-l參數(shù)可以指定要鏈接的庫
-l參數(shù)和-L參數(shù)
-l參數(shù)就是用來指定程序要鏈接的庫,-l參數(shù)緊接著就是庫名,那么庫名跟真正的庫文件名有什么關系呢?
就拿數(shù)學庫來說,他的庫名是m,他的庫文件名是libm.so,很容易看出,把庫文件名的頭lib和尾.so去掉就是庫名了
那我們再試一下,gcc add.c -llua,這次編譯出來了: a.out
執(zhí)行成功!
如何讓Lua調(diào)用C?
Lua調(diào)用C,我了解到的有3種方式
1.通過在C中注冊函數(shù)給lua調(diào)用
2.封裝成c動態(tài)鏈接庫,在lua中require
3.在LuaJIT里面可以使用ffi高性能的調(diào)用C(但是IOS上不支持LuaJIT。。)
1.在C中注冊函數(shù)給Lua
lua提供了lua_register函數(shù)注冊C函數(shù)給lua端調(diào)用
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);//獲取參數(shù),字符串類型 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(); /* 創(chuàng)建lua狀態(tài)機 */ luaL_openlibs(L); /* 打開Lua狀態(tài)機中所有Lua標準庫 */ lua_register(L, "SayHello", l_SayHello);//注冊C函數(shù)到lua const char* testfunc = "print(SayHello('lijia'))";//lua中調(diào)用c函數(shù) if(luaL_dostring(L, testfunc)) // 執(zhí)行Lua命令。 printf("Failed to invoke.\n"); /*清除Lua*/ lua_close(L); return 0; }
gcc -o hello hello.c -llua編譯執(zhí)行
2.調(diào)用C動態(tài)鏈接庫
創(chuàng)建一個mylib.c的文件,然后我們把它編譯成動態(tài)鏈接庫
#include <stdio.h> #include <math.h> #include <stdarg.h> #include <stdlib.h> #include <lua.h> #include <lauxlib.h> #include <lualib.h> /* 所有注冊給Lua的C函數(shù)具有 * "typedef int (*lua_CFunction) (lua_State *L);"的原型。 */ static int l_sin(lua_State *L) { // 如果給定虛擬棧中索引處的元素可以轉(zhuǎn)換為數(shù)字,則返回轉(zhuǎn)換后的數(shù)字,否則報錯。 double d = luaL_checknumber(L, 1); lua_pushnumber(L, sin(d)); /* push result */ /* 這里可以看出,C可以返回給Lua多個結(jié)果, * 通過多次調(diào)用lua_push*(),之后return返回結(jié)果的數(shù)量。 */ return 1; /* number of results */ } /* 需要一個"luaL_Reg"類型的結(jié)構(gòu)體,其中每一個元素對應一個提供給Lua的函數(shù)。 * 每一個元素中包含此函數(shù)在Lua中的名字,以及該函數(shù)在C庫中的函數(shù)指針。 * 最后一個元素為“哨兵元素”(兩個"NULL"),用于告訴Lua沒有其他的函數(shù)需要注冊。 */ static const struct luaL_Reg mylib[] = { {"mysin", l_sin}, {NULL, NULL} }; /* 此函數(shù)為C庫中的“特殊函數(shù)”。 * 通過調(diào)用它注冊所有C庫中的函數(shù),并將它們存儲在適當?shù)奈恢谩? * 此函數(shù)的命名規(guī)則應遵循: * 1、使用"luaopen_"作為前綴。 * 2、前綴之后的名字將作為"require"的參數(shù)。 */ extern int luaopen_mylib(lua_State* L) { /* void luaL_newlib (lua_State *L, const luaL_Reg l[]); * 創(chuàng)建一個新的"table",并將"l"中所列出的函數(shù)注冊為"table"的域。 */ luaL_newlib(L, mylib); return 1; }
使用gcc -o mylib.so -fPIC -shared mylib.c -llua -ldl編譯成so
然后創(chuàng)建一個lua文件,把我們編譯出來的c庫引入進來
--[[ 這里"require"的參數(shù)對應C庫中"luaopen_mylib()"中的"mylib"。 C庫就放在"a.lua"的同級目錄,"require"可以找到。]] local mylib = require "mylib" -- 結(jié)果與上面的例子中相同,但是這里是通過調(diào)用C庫中的函數(shù)實現(xiàn)。 print(mylib.mysin(3.14 / 2)) --> 0.99999968293183
執(zhí)行a.lua文件,后報錯,說Lua存在多個虛擬機!
lua: multiple Lua VMs detected
為什么呢?查了一些資料發(fā)現(xiàn)因為lua默認編譯的是靜態(tài)鏈接庫,這樣會導致鏈接多個VM沖突。
那么我們自己再編譯個lua解釋器動態(tài)鏈接一下。
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可執(zhí)行文件
在命令行./mylua執(zhí)行,成功打印出0.99999968293183
總結(jié)
gcc命令,編譯lua,編譯C動態(tài)鏈接庫這些之前都接觸的比較少。所以也爬了不少坑,哈哈哈。接下來要好好研究下怎么在c中解析二進制協(xié)議給lua調(diào)用,在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
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關文章
C/C++中數(shù)據(jù)類型轉(zhuǎn)換詳解及其作用介紹
這篇文章主要介紹了C/C++中數(shù)據(jù)類型轉(zhuǎn)換詳解及其作用,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09