【Lua】Lua與C交互

上一篇文章簡單介紹了在Lua中如何調用C,其中的原理還是需要稍微深究一下。

文章參考自:Lua和C交互的簡易教程(HansChen的博客)


C/C++與Lua交互的基礎源於虛擬棧在Lua中,Lua堆棧就是一個struct,堆棧索引的方式可是是正數也可以是負數,區別是:正數索引1永遠表示棧底,負數索引-1永遠表示棧頂

這裏寫圖片描述

一個簡單的例子:

#include <lua.h>
#include <stdio.h>
#include "lauxlib.h"

int main()
{
	lua_State *L = luaL_newstate();

	lua_pushstring(L, "mick");
	lua_pushnumber(L, 20);

	printf("stack size: %d\n", lua_gettop(L));
	const char *str = luaL_checklstring(L, 1, NULL);
	int data = luaL_checknumber(L, 2);
	printf("%s	%d\n", str, data);

	return 0;
}

不同於上次編譯動態庫,這次直接編譯可執行文件,缺少一些編譯選項就會報錯。我遇到的:

1、少 liblua.a 時:

/tmp/ccM31DcG.o: In function `main':
lua_test.c:(.text+0x9): undefined reference to `luaL_newstate'
lua_test.c:(.text+0x1e): undefined reference to `lua_pushstring'
lua_test.c:(.text+0x3d): undefined reference to `lua_pushnumber'
lua_test.c:(.text+0x53): undefined reference to `luaL_checklstring'
lua_test.c:(.text+0x68): undefined reference to `luaL_checknumber'
collect2: error: ld returned 1 exit status


2、少 -lm 時:

object.c:(.text+0xeb): undefined reference to `fmod'
lobject.c:(.text+0x111): undefined reference to `pow'
lobject.c:(.text+0x125): undefined reference to `floor'
/usr/local/lib/liblua.a(ltable.o): In function `luaH_get':
ltable.c:(.text+0x623): undefined reference to `floor'
/usr/local/lib/liblua.a(ltable.o): In function `luaH_newkey':
ltable.c:(.text+0xcc9): undefined reference to `floor'
/usr/local/lib/liblua.a(lvm.o): In function `tointeger_aux':
lvm.c:(.text+0x7a): undefined reference to `floor'
/usr/local/lib/liblua.a(lvm.o): In function `luaV_execute':
lvm.c:(.text+0x1a2e): undefined reference to `floor'
lvm.c:(.text+0x1ead): undefined reference to `pow'
lvm.c:(.text+0x2000): undefined reference to `fmod'
collect2: error: ld returned 1 exit status


3、少 -ldl 時:

/usr/local/lib/liblua.a(loadlib.o): In function `lookforfunc':
loadlib.c:(.text+0x502): undefined reference to `dlsym'
loadlib.c:(.text+0x549): undefined reference to `dlerror'
loadlib.c:(.text+0x576): undefined reference to `dlopen'
loadlib.c:(.text+0x5ed): undefined reference to `dlerror'
/usr/local/lib/liblua.a(loadlib.o): In function `gctm':
loadlib.c:(.text+0x781): undefined reference to `dlclose'
collect2: error: ld returned 1 exit status

所以可行的編譯爲:

gcc source.c -o target /usr/local/lib/liblua.a -lm -ldl


返回如下:

stack size: 2
mick	20




Lua調用C動態庫


對棧有了一定了解後,我們再次開始Lua調用C的例子,這回可以有返回值。

注意一點:所有的函數必須接收一個lua_State作爲參數,同時返回一個整數值,該整數值表示函數的返回值。這個函數使用Lua棧作爲參數,它可以從棧裏面讀取任意數量和任意類型的參數。當函數開始的時候, lua_gettop(L) 可以返回函數收到的參數個數。 第一個參數(如果有的話)在索引 1 的地方, 而最後一個參數在索引 lua_gettop(L) 處。 當需要向 Lua 返回值的時候, C 函數只需要把它們以正序壓到堆棧上(第一個返回值最先壓入), 然後返回這些返回值的個數。 在這些返回值之下的,堆棧上的東西都會被 Lua 丟掉。


例子如下:

#include <lua.h>
#include <lauxlib.h>
#include <stdio.h>

static int test(lua_State *L)
{
	int num = luaL_checkinteger(L, 1);
	const char* str = luaL_checklstring(L, 2, NULL);
	printf("come from test: num = %d	str = %s\n", num, str);
	lua_pushnumber(L, 10);
	lua_pushstring(L, "mick");
	return 2;
}

int luaopen_myLib(lua_State *L)
{
	luaL_Reg l[] = 
	{
		{"test", test},
		{NULL, NULL}
	};

	luaL_newlib(L, l);
	return 1;
}

調用如下:

local reta, retb = myLib.test(666, "cxl")
print("ret val:", reta, retb)

打印如下:

rom test: num = 666	str = cxl
ret val:	10.0	mick



C調用Lua


事先準備lua文件:

str = "my lua lib"
person = {name = "cxl", age = 26}

function add(x, y)
	return x + y
end

function addName(person, newName)
	person.name = newName
	return person
end

function display()
	for k, v in pairs(person) do
		print(k .. ":	" .. v)
	end
end


然後是 c 文件:

#include <lua.h>
#include <lauxlib.h>
#include <stdio.h>

lua_State* load_lua(char *luaPath) {
	lua_State *L = luaL_newstate();
	luaL_openlibs(L);

	//加載腳本並運行
	if (luaL_loadfile(L, luaPath) || lua_pcall(L, 0, 0, 0)) {
		printf("load Lua failed: %s\n", lua_tostring(L, -1));	//發生錯誤時,有關錯誤的提示信息會被壓入棧頂
		return NULL;
	}
	return L;
}

int main() {
	char *luaFile = "mylib.lua";
	lua_State *L = load_lua(luaFile);
	if (NULL == L) {
		return -1;
	}
	
	printf("stack size: %d\n", lua_gettop(L));
	lua_getglobal(L, "str");
	printf("%s\n", luaL_checklstring(L, -1, NULL));

	lua_getglobal(L, "person");
	lua_getfield(L, -1, "name");
	printf("name: %s\n", luaL_checklstring(L, -1, NULL));
	lua_getfield(L, -2, "age");			// 由於將 "name" 對應的值壓棧,table的索引變成了-2
	printf("age: %d\n", luaL_checkinteger(L, -1));

	//=================== 棧頂 ===================
    	//  索引  類型      值
    	//   4   int:      26
    	//   3   string:   cxl
    	//   2   table:     person
    	//   1   string:    cxl
    	//=================== 棧底 ===================   

	lua_getglobal(L, "add");
	lua_pushnumber(L, 3);
	lua_pushnumber(L, 4);
	printf("stack size: %d\n", lua_gettop(L));
	if (lua_pcall(L, 2, 1, 0)) {			// 函數調用完畢,函數與參數出棧
		printf("lua call failed: %s\n", luaL_checklstring(L, -1, NULL));
		return -1;
	}
	
	int result = luaL_checkinteger(L, -1);
	printf("result: %d\n", result);
	printf("stack size: %d\n", lua_gettop(L));

	lua_pushnumber(L, 165);
	lua_setfield(L, 2, "height");		// 棧頂元素被設置到table的"height",同時出棧
	printf("stack size: %d\n", lua_gettop(L));

	lua_getglobal(L, "display");
	if (lua_pcall(L, 0, 0, 0)) {
		printf("lua call failed: %s\n", luaL_checklstring(L, -1, NULL));
		return -1;
	}
	
	lua_getglobal(L, "addName");
	lua_newtable(L);					// 建立一個新表
	lua_pushstring(L, "mzh");
	if (lua_pcall(L, 2, 1, 0)) {
		printf("lua call failed: %s\n", luaL_checklstring(L, -1, NULL));
		return -1;
	}
	lua_getfield(L, -1, "name");
	printf("name: %s\n", luaL_checklstring(L, -1, NULL));

	lua_close(L);
	return 0;
}



輸出顯示爲:

stack size: 0
my lua lib
name: cxl
age: 26
stack size: 7
result: 7
stack size: 5
stack size: 5
name:	cxl
age:	26
height:	165.0
name: mzh


需要注意的是:棧操作是基於棧頂的,默認情況下它只會去操作棧頂的值。 
舉個例子,函數調用流程是先將函數入棧,參數入棧,然後用lua_pcall調用函數,此時棧頂爲參數,棧底爲函數,所以棧過程大致會是:參數出棧->保存參數->函數出棧->調用函數->返回值入棧
類似的還有lua_setfield,設置一個表的值,首先將值出棧,保存,再去給表賦值。



有了以上基礎,我們嘗試在lua調用c,並將table傳入c,由c代碼處理。
c代碼:

#include <lua.h>
#include <lauxlib.h>
#include <stdio.h>


static int test(lua_State *L)
{
	lua_pushnumber(L, 26);
	lua_setfield(L, 1, "age");
	return 1;
}


int luaopen_myLib(lua_State *L)
{
	luaL_Reg l[] = 
	{
		{"test", test},
		{NULL, NULL}
	};


	luaL_newlib(L, l);
	return 1;
}




lua調用:
package.cpath = "./?.so"
local myLib = require "myLib"  


person = {name = "cxl"}
person = myLib.test(person)  


for k, v in pairs(person) do
	print(k .. ":	" .. v)
end


結果如下:
age:	26.0
name:	cxl




更多更詳細的函數介紹見:Lua5.3參考手冊

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章