C/C++與lua之間能過實現交互,它的原理是:
1.lua裏面的API都是用C寫的。
2.lua跟C/C++的交互是通過一個虛擬stack來進行數據的溝通的。在VS工程中,我們需要加入C API的頭文件lua.h, luaconfig.h , lualib.h, luaxlib.h 。這幾個頭文件都是lua源代碼,可以直接下載使用。lua.h提供原子級別的API,對棧的基本操作都在裏面實現,裏面的API都是lua_開頭。luaxlib.h 定義了輔助庫提供的函數,它的所有定義都以luaL_開頭,輔助庫是一個使用lua.h中的API編寫出的一個較高的抽象層。下面看下完整源代碼:
#include "stdafx.h"
#include "stdio.h"
extern "C"
{
#include "lua.h" //提供原子級別的API
#include "lualib.h"
#include "lauxlib.h" //定義了輔助哭提供的函數
}; //lua頭文件中的API都是用C寫的
#pragma comment(lib,"lua5.1.lib") //讓程序知道調用的lua裏面的API可以直接在這個庫裏面找
//這個函數,我們實現將壓進stack裏面的所有數據,從base到top,根據不同類型打印出來
void stackDump(lua_State *L)
{
int i;
int top = lua_gettop(L); //獲取stack大小
for ( i = 1;i <= top;i++ )
{
int type = lua_type(L, i);
switch(type)
{
case LUA_TSTRING:
{
printf("%s",lua_tostring(L, i)); //lua庫中的函數,添加了頭文件,可以直接使用
break;
}
case LUA_TBOOLEAN:
{
printf(lua_toboolean(L, i)?"true":"false");
break;
}
case LUA_TNUMBER:
{
printf("%g",lua_tonumber(L, i));
break;
}
default:
{
printf("%s",lua_typename(L ,i));
break;
}
}
printf(" ");
}
printf("\n");
}
int _tmain(int argc, _TCHAR* argv[])
{
lua_State *L; //創建一個lua_State實例
L = luaL_newstate(); //必須要創建lua_State環境,stack存在於這個環境中
luaL_openlibs(L); //加載lua靜態鏈接庫lua5.1.lib
lua_pushboolean(L,true);
lua_pushinteger(L,2);
lua_pushnumber(L,2.333);
lua_pushstring(L,"then"); //分別壓入了四個不同類型的元素,現在stack裏面,從base到top依次爲 true , 2 , 2.333 ,then
stackDump(L); //打印結果 從base到top爲 true , 2 , 2.333 ,then
printf("stay here");
return 0;
}
C API. Lua既是一種擴展語言,也是一種可擴展語言;說它是擴展語言,意思是C/C++可以用lua進行擴展,這時C/C++擁有控制權,lua是一個庫,這種形式的C/C++代碼稱爲“應用程序代碼”;說它是可擴展語言,意思是lua自身也可以通過在lua環境中註冊用C語言(或其他語言)實現的函數,然後lua可以直接調用這些函數,這時lua擁有控制權,C是一個庫,這種形式的C代碼稱爲“庫代碼”。這就是C跟lua交互的兩種形式。應用程序代碼與庫代碼都使用相同的API來與lua通信,這些API就是C API
C API
Lua腳本實現交互提供了一系列的C API,常用API有:
luaL_newstate函數用於初始化一個lua_State實例
luaL_openlibs函數用於打開Lua中的所有標準庫,如io庫、string庫等。
luaL_loadbuffer編譯了buff中的Lua代碼,如果沒有錯誤,則返回0,同時將編譯後的程序塊壓入虛擬棧中。
lua_pcall函數會將程序塊從棧中彈出,並在保護模式下運行該程序塊。執行成功返回0,否則將錯誤信息壓入棧中。
lua_tostring函數中的-1,表示棧頂的索引值,棧底的索引值爲1,以此類推。該函數將返回棧頂的錯誤信息,但是不會將其從棧中彈出。
lua_pop是一個宏,用於從虛擬棧中彈出指定數量的元素,這裏的1表示僅彈出棧頂的元素。
lua_close用於釋放狀態指針所引用的資源
Lua針對每種C類型,都有一個C API函數與之對應,如:
void lua_pushnil(lua_State* L); --nil值
void lua_pushboolean(lua_State* L, int b); --布爾值
void lua_pushnumber(lua_State* L, lua_Number n); --浮點數
void lua_pushinteger(lua_State* L, lua_Integer n); --整型
void lua_pushlstring(lua_State* L, const char* s, size_t len); --指定長度的內存數據
void lua_pushstring(lua_State* L, const char* s); --以零結尾的字符串,其長度可由strlen得出
Lua提供了一組特定的函數用於檢查返回元素的類型,如:
int lua_isboolean (lua_State *L, int index);
int lua_iscfunction (lua_State *L, int index);
int lua_isfunction (lua_State *L, int index);
int lua_isnil (lua_State *L, int index);
int lua_islightuserdata (lua_State *L, int index);
int lua_isnumber (lua_State *L, int index);
int lua_isstring (lua_State *L, int index);
int lua_istable (lua_State *L, int index);
int lua_isuserdata (lua_State *L, int index);
以上函數,成功返回1,否則返回0。需要特別指出的是,對於lua_isnumber而言,不會檢查值是否爲數字類型,而是檢查值是否能轉換爲數字類型
C/C++與lua交互的總結:
1.引入lua的庫函數
extern "C"
{
#include "lua.h" //提供原子級別的API
#include "lualib.h"
#include "lauxlib.h" //定義了輔助哭提供的函數
};//C++代碼中,用extern "C" 區別c代碼,因爲lua頭文件中的API都是用C寫的
2.創建一個lua_State對象
lua_State *L; //創建一個lua_State實例,lua_State主要是管理一個lua虛擬機的執行環境, 一個lua虛擬機可以有多個執行環境。Lua虛擬機通過維護這樣一個虛擬棧來實現兩種之間的通信
//C/C++與lua的數據通信
//Lua虛擬機提供Lua_State這樣一種數據結構。任何一種數據從C\C++傳入Lua虛擬機中,Lua都會將這類數據轉換爲一種通用的結構lua_TValue,並且將數據複製一份,將其壓入虛擬棧中
3.初始化實例
L = luaL_newstate(); //必須要創建lua_State環境,stack存在於這個環境中
4.打開Lua中的所有標準庫
luaL_openlibs(L); //加載lua靜態鏈接庫lua5.1.lib
5.向stack中添加數據
lua_pushboolean(L,true);
lua_pushinteger(L,2);
lua_pushnumber(L,2.333);
lua_pushstring(L,"then"); //類似於棧,第一個數據放在棧底,API使用“索引”來引用棧中的元素,第一個壓入棧的爲1,第二個爲2,依此類推。我們也可以使用負數作爲索引值,其中-1表示爲棧頂元素,-2爲棧頂下面的元素,同樣依此類推
6.將stack中的數據讀出來
stackDump(L)