上一篇我們搭建好了整個的項目環境,現在,我們一起探索一下如何將lua寄宿到C++中。
宿主的實現
我們在LuaWithCPPTest項目下,查看Source.cpp代碼如下:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
extern "C"
{
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
};
void TestLua();
int main()
{
TestLua();
return 0;
}
void TestLua()
{
lua_State *L = luaL_newstate();
luaopen_base(L); //
luaopen_table(L); //
luaopen_package(L); //
luaopen_io(L); //
luaopen_string(L); //
luaL_openlibs(L); //打開以上所有的lib
string str;
while (true)
{
cout << "請輸入Lua代碼:" << endl;
getline(cin, str, '\n');
if (luaL_loadstring(L, str.c_str())
|| lua_pcall(L, 0, 0, 0) )
{
const char * error = lua_tostring(L, -1) ;
cout << string(error) << endl;
}
}
lua_close(L);
}
其中,被extern "C"包起來的是lua的主要函數的聲明。在C++中,每個嵌入的lua的生命周期與各自的lua_State對象一一對應。通過luaL_newstate()方法,我們便創建了一個lua解釋器。隨后的幾個luaopen_*方法,都是獲取相應lua庫的使用權,最后通過luaL_openlibs打開所有的有使用權的lua標准庫。一切准備就緒后,我們開始接收輸入。
我們通過luaL_loadstring,將所有代碼讀入lua,並且檢查代碼是否有語法錯誤。然后通過lua_pcall,運行代碼,將所有的全局變量保存在_G中。通過讀取、運行這兩步,我們就建立起一個自己的lua解釋器了。
將lua作為配置文件
從文件讀取lua代碼,流程與之前的示例一樣,僅是將luaL_loadstring()換成luaL_loadfile()即可。代碼如下:
string str;
while (true)
{
cout << "輸入lua文件路徑:" << endl;
getline(cin, str, '\n');
if (luaL_loadfile(L, str.c_str())
|| lua_pcall(L, 0, 0, 0) )
{
const char * error = lua_tostring(L, -1) ;
cout << string(error) << endl;
return;
}
}
現在,我們在lua中定義變量,並且賦值。然后在c++中取值,運算出結果。在lua文件中,內容如下:
在c++中,我們獲取a,b兩個變量的值,然后相加,算出結果:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
extern "C"
{
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
};
void TestLua();
int main()
{
TestLua();
return 0;
}
void TestLua()
{
lua_State *L = luaL_newstate();
luaopen_base(L); //
luaopen_table(L); //
luaopen_package(L); //
luaopen_io(L); //
luaopen_string(L); //
luaL_openlibs(L); //打開以上所有的lib
string str;
while (true)
{
cout << "輸入lua文件路徑:" << endl;
getline(cin, str, '\n');
if (luaL_loadfile(L, str.c_str())
|| lua_pcall(L, 0, 0, 0) )
{
const char * error = lua_tostring(L, -1) ;
cout << string(error) << endl;
return;
}
else
{
break;
}
}
int a = 0;
int b = 0;
// 獲取a的值
lua_getglobal(L, "a");
if (!lua_isnumber(L, -1))
{
cout << "-2 error" << lua_isnumber(L, -1) << lua_isnumber(L, -1) << endl;
return ;
}
a = lua_tonumber(L, -1);
// 獲取b的值
lua_getglobal(L, "b");
if (!lua_isnumber(L, -1))
{
cout << "-1 error" << endl;
return ;
}
b = lua_tonumber(L, -1);
cout << "a = " << a << " b = " << b << endl;
cout << "a + b = " << a + b << endl;
lua_close(L);
}
最后的得到結果如下:
將文件載入lua后,我們通過lua_getglobal()方法,將想要獲得的lua值放入棧頂(棧為何物,下篇分析),然后用lua_isnumber(L, –1)來檢測棧頂的元素是否是number,若果是,調用lua_tonumber(L,-1)來將棧頂的值傳遞給c++變量,整個流程為:1、將欲獲得的lua值放入棧中;2、檢測棧中的值是否ok;3、取出棧中的值。lua有一系列的lua_is*()來檢測棧中的值是否是相應的*類型,相應的,也有一系列的lua_to*()來取值:
總結
寫這篇的時候,我內心比較糾結,在內容的深度上把握不准,最終我的定位是:這系列只描述兩者之間交互的步驟、方式,不深入去描述原理。原因很簡單,因為我的水平有限。在自己水平不足的情況下,去描述c++與lua內在的交互原理,會誤導人。我目前的大綱是:第一篇搭建環境;第二篇描述C++調用lua的一些數據;第三篇講解C++與lua之間的棧,主要還是偏重C++代碼調用lua代碼。第四篇描述lua如何調用c++代碼。第五篇介紹工具tolua++。以上。