lua編程之lua與C相互調用

lua是擴展性很是良好的語言,雖然核心很是精簡,可是用戶能夠依靠lua庫來實現大部分工做。除此以外,lua還能夠經過與C函數相互調用來擴展程序功能。在C中嵌入lua腳本既可讓用戶在不從新編譯代碼的狀況下修改lua代碼更新程序,也能夠給用戶提供一個自由定製的接口,這種方法遵循了機制與策略分離的原則。在lua中調用C函數能夠提升程序的運行效率。lua與C的相互調用在工程中至關實用,本文就來說解lua與C相互調用的方法。html

Lua與C相互調用的首要問題是如何交換數據,lua API使用了一個抽象的棧與C語言交換數據,提供了壓入元素,查詢元素和彈出元素等功能的API操做棧,這裏能夠查看lua5.2中每一個函數的詳細文檔,棧中的元素能夠經過索引訪問,從棧底向上是從1開始遞增的正整數,從棧頂向下是從-1開始遞減的負整數,棧的元素按照FIFO的規則進出。數組

1、 C調用lua函數

先經過一個簡單的例子瞭解C是怎麼調用lua的,ui

//test.lua
width = 10
height = 20

//test.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, "test.lua") || lua_pcall(L, 0,0,0)){
    printf("error %s\n", lua_tostring(L,-1));
    return -1;
  }
  lua_getglobal(L,"width");
  lua_getglobal(L,"length");
  printf("width = %d\n", lua_tointeger(L,-2));
  printf("length = %d\n", lua_tointeger(L,-1));
  lua_close(L);
  return 0;
}

luaL_newstate建立一個新的lua_State,C和lua的全部操做都要依賴這個lua環境, luaL_openlibs將lualib.h中定義的lua標準庫加載到進lua_State。lua

luaL_loadfile從文件中加載lua代碼並編譯,編譯成功後的程序塊被壓入棧中,spa

lua_pcall會將程序塊彈出並在保護模式下解釋執行。代碼中調用lua_pcall就在lua_State中定義了 width和 length兩個全局變量。code

lua_getglobal將全局變量的值壓入棧中,width先入棧,在-2的位置,length在棧頂。htm

除了變量,C代碼還能夠直接調用lua中定義的函數blog

//test.lua
function add(x, y)
  return x+y
end
//test.c
#include <stdio.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <math.h>

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

  if(luaL_loadfile(L, "test.lua") || lua_pcall(L, 0,0,0)){
    printf("error %s\n", lua_tostring(L,-1));
    return -1;
  }
  lua_getglobal(L,"add");
  lua_pushnumber(L, 10);
  lua_pushnumber(L, 20);
  if(lua_pcall(L, 2, 1, 0) != 0){
    printf("error %s\n", lua_tostring(L,-1));
    return -1;
  }
  double z = lua_tonumber(L, -1);
  printf("z = %f \n", z);
  lua_pop(L, 1);
  lua_close(L);
  return 0;
}

lua_pcall(L, 2, 1, 0)表示,傳入兩個參數,指望獲得一個返回值,0表示錯誤處理函數在棧中的索引值,壓入結果前會彈出函數和參數,因此z的索引是-1.索引

2、 lua調用C

lua能夠將C函數註冊到lua中,C函數必須遵循統一的原型,這個原型定義在lua.h中,

typedef int (*) (lua_State *)

用C函數擴展lua時,通常將全部的C函數編譯成一個獨立的模塊,方便增長新的函數。

//mylib.c
#include <stdio.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <math.h>

static int myadd(lua_State *L){
                int a = luaL_checknumber(L, 1);
                int b = luaL_checknumber(L, 2);
                lua_pushnumber(L, a+b);
                return 1;
}

static const struct luaL_Reg mylib [] = {
        {"add", myadd}, 
        {NULL, NULL}
};

int luaopen_mylib(lua_State *L){
  luaL_newlib(L, mylib);
  return 1;
}

//call.lua
#!/usr/local/bin/lua

lib=require "mylib"
print(lib.add(1, 2))

每一個被lua調用的C函數都有本身的私有棧,壓入參數的索引從1開始遞增,結果值也是直接壓入棧中,函數返回時會將壓入的參數所有刪除,只留下結果值。mylib[]聲明瞭模塊中全部C函數列表,每一項映射了C函數在lua中的命名,好比上面代碼中myadd函數在lua中用add表示,列表必須用{NULL, NULL}結束。 luaL_newlib在棧中建立一個table,將mylib數組中的C函數註冊進這個table中。 luaopen_mylib將這個table中的函數加載進lua環境中。

先將C代碼編譯成動態連接庫,

gcc -shared -fPIC -o mylib.so mylib.c -llua -lm -ldl

lua代碼中,require會查找 mylib.so,並調用該連接庫中的 luaopen_mylib,luaopen_的後綴必須與動態連接庫名字同樣,這是由require查找函數方式決定的。

相關文章
相關標籤/搜索