程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> lua(8)

lua(8)

編輯:關於C++

上一篇我們提到許多c的api,這一篇我們就來看看如何實現基本的C++與lua的交互。

(1)基礎示例

首先我們打開VS,新建一個c++控制台程序lua1,在我電腦上,這個新建的c++項目路徑是F:\VSProject\lua1。

然後在lua的安裝目錄下找到include和lib文件夾

\

將include和lib文件夾拷貝至新建的c++項目中,拷貝到和.sln解決方案文件同一目錄

\

拷貝完畢後,在vs中右鍵解決方案,找到屬性

\

在C/C++中的“附加包含目錄”加上../include

\

在鏈接器中的“附加庫目錄”加上../lib

\

附加包含目錄和附加庫目錄添加完畢後,就可以在程序中通過#include來加載lua的頭文件了。

lua1.cpp:

 

// lua1.cpp : 定義控制台應用程序的入口點。
//
 
#include "stdafx.h"
#include "iostream"
#include "stdio.h"
#include "string.h"
//lua.h和lualib.h作為C代碼運行,在C++文件中使用lua.h和lualib.h,需使用extern命令實現混合編程,否則會提示無法解析外部函數
extern "C"
{
    #include "lua.h"
    #include "lauxlib.h"
    #include "lualib.h"
}
using namespace std;
#pragma comment(lib,"lua5.1.lib")
 
int _tmain(int argc, _TCHAR* argv[])
{
    char buff[256];
    int error;
    //創建一個lua狀態機(虛擬棧),這個狀態機沒有包含預定義的lua庫函數
    lua_State* L = luaL_newstate();
    //打開標准庫,這樣狀態機可以使用lua庫函數,可以解釋lua腳本
    luaL_openlibs(L);
    char fileName[] = "F:/LuaFile/lua1.lua";
    //加載lua文件,解釋器將lua文件的內容讀取出來,動態編譯為代碼塊
    luaL_loadfile(L,fileName);
    //執行編譯好的代碼塊,參數1為棧指針,參數2為傳給待調用函數的參數數量,
    //參數3為期望返回的結果的數量,參數4為錯誤處理函數的索引(這個索引為壓入棧的函數索引,0表示沒有錯誤處理函數)
    int result = lua_pcall(L,0,LUA_MULTRET,0);
    //如果運行沒有錯誤,lua_pcall將返回0
    if(!result)
    {
        printf_s("lua腳本運行成功\n");
    }
    lua_close(L);
    return 0;
}

lua1.cpp新建完畢後,我們在F:/LuaFile/路徑下新建一個lua1.lua文件,然後簡單的寫上一句打印

\

運行程序,看看結果

\

 

上面的程序使用了luaL_newstate、luaL_openlibs、luaL_loadfile、lua_pcall、lua_close這幾個c api,其中luaL_newstate用於在宿主程序中創建lua的虛擬機(棧);剛創建好的虛擬機是不具備lua的庫環境的,因此需要luaL_openlibs函數打開lua的標准庫;虛擬機的環境初始化完畢後,我們使用luaL_loadfile把lua1.lua文件的內容讀取出來,讀取後將內容視作代碼進行編譯,如果編譯成功,將編譯後的代碼塊壓入虛擬機中;壓入虛擬機中的代碼塊是可以被執行的,因此我們通過lua_pcall來執行這些代碼塊,壓入的代碼塊正是lua1.lua中的print("I am lua1"),因此控制台中輸出"Iam lua1";執行結束後,關閉虛擬機,至此,一個簡單的c api示例程序運行完畢。

 

(2)數據輸入

我們修改一下main函數裡面的內容

 

int _tmain(int argc, _TCHAR* argv[])
{
    char buff[256];
    int loadError;
    int callError;
    //創建一個lua狀態機(虛擬棧),這個狀態機沒有包含預定義的lua庫函數
    lua_State* L = luaL_newstate();
    //打開標准庫,這樣狀態機可以使用lua庫函數,可以解釋lua腳本
    luaL_openlibs(L);
    //使用fgets輸入流輸入數據,如果使用scanf_s會受到空格的影響
    while(fgets(buff,sizeof(buff),stdin) != NULL)
    {
        //接收用戶輸入的數據,編譯為程序塊並壓入棧中,如果沒有錯誤,返回0;如果有錯誤,壓入一行錯誤信息的字符串
        loadError =luaL_loadbuffer(L,buff,strlen(buff),"line");
        if(loadError)
        {
            //打印這條錯誤信息,同時將錯誤信息壓桟
            printf_s("%s\n",lua_tostring(L,-1));
            //彈出這條錯誤信息,第二個參數是從棧頂彈出元素的個數
            lua_pop(L,1);
        }
       //執行代碼塊,並將代碼塊彈出棧
       callError = lua_pcall(L,0,0,0);
       if(callError)
       {
           printf_s("%s\n",lua_tostring(L,-1));
           lua_pop(L,1);
       }
    }
    lua_close(L);
    return 0;
}

 

運行結果

\

 

上述程序將每一行的輸入都動態編譯成了lua的代碼塊,再通過lua_pcall函數來執行這些代碼塊。

通過luaL_loadbuffer函數,上述代碼還可以寫成這樣子。

 

int _tmain(int argc, _TCHAR* argv[])
{
    char buff[256];
    int loadError;
    int callError;
 
    //c++長字符串在每行後面用"\"接續
    string luaBuff = "print('hello world');" \
                     "local a = 123;" \
                     "print(type(a));" \
                     "local b = 'ABC';" \
                     "print(type(b));" \
                     "function d(...)" \
                     "local str = 'abc^%&(';" \
                     "print(string.gsub(str,'%w',''));" \
                     "end;" \
                     "d();";
 
    //創建一個lua狀態機(虛擬棧),這個狀態機沒有包含預定義的lua庫函數
    lua_State* L = luaL_newstate();
    //打開標准庫,這樣狀態機可以使用lua庫函數,可以解釋lua腳本
    luaL_openlibs(L);
 
    //接收用戶輸入的數據,編譯為程序塊並壓入棧中,如果沒有錯誤,返回0;如果有錯誤,壓入一行錯誤信息的字符串
    loadError =luaL_loadbuffer(L,luaBuff.c_str(),strlen(luaBuff.c_str()),"line");
    if(loadError)
    {
        //打印這條錯誤信息,同時將錯誤信息壓桟
        printf_s("%s\n",lua_tostring(L,-1));
        //彈出這條錯誤信息,第二個參數是從棧頂彈出元素的個數
        lua_pop(L,1);
    }
    //執行代碼塊,並將代碼塊彈出棧
    callError = lua_pcall(L,0,0,0);
    if(callError)
    {
        printf_s("%s\n",lua_tostring(L,-1));
        lua_pop(L,1);
    }
    lua_close(L);
    return 0;
}

運行結果

\

 

可以看到,luaBuff這個字符串的內容也被當成是lua代碼塊來執行了。

(3)C++與lua間的通信

上述的例子都是直接加在的一個代碼塊去執行它,如果c++代碼中有函數fun1和fun2,lua的代碼中有函數fun3和fun4,現在需要在fun1中調用fun3,在fun4中調用fun2,那麼程序可以寫成這樣子。

lua1.cpp:

 

// lua1.cpp : 定義控制台應用程序的入口點。
//
 
#include "stdafx.h"
#include "iostream"
#include "stdio.h"
#include "string.h"
//lua.h和lualib.h作為C代碼運行,在C++文件中使用lua.h和lualib.h,需使用extern命令實現混合編程,否則會提示無法解析外部函數
extern "C"
{
    #include "lua.h"
    #include "lauxlib.h"
    #include "lualib.h"
}
using namespace std;
#pragma comment(lib,"lua5.1.lib")
 
void fun1(lua_State* L)
{
    printf_s("I am fun1,I'll call fun3\n");
    //在全局范圍內獲得"fun3"的這個元素,並將這個元素的內容壓入棧中
    lua_getglobal(L,"fun3");
    int callError;
    callError = lua_pcall(L,0,0,0);
    if(callError)
    {
        printf_s("%s\n",lua_tostring(L,-1));
        printf_s("call fun3 error.");
        lua_pop(L,1);
    }
}
 
int fun2(lua_State* L)
{
    printf_s("I am fun2~~~~~~~~~\n");
    //返回值的個數為0
    return 0;
}
 
int _tmain(int argc, _TCHAR* argv[])
{
    char buff[256];
    int error;
    //創建一個lua狀態機(虛擬棧),這個狀態機沒有包含預定義的lua庫函數
    lua_State* L = luaL_newstate();
    //打開標准庫,這樣狀態機可以使用lua庫函數,可以解釋lua腳本
    luaL_openlibs(L);
    char fileName[] = "F:/LuaFile/lua1.lua";
    //加載lua文件,解釋器將lua文件的內容讀取出來,動態編譯為代碼塊
    int loadFileError = luaL_loadfile(L,fileName);
    //打印錯誤信息
    if(loadFileError)
    {
        printf_s("%s\n",lua_tostring(L,-1));
        lua_pop(L,1);
    }
 
    //向棧中壓入c++的fun2
    lua_pushcfunction(L,fun2);
    //在棧中給fun2命名為"fun2",這樣其他地方就能根據"fun2"這個字段索引到fun2的內容
    lua_setglobal(L,"fun2");
 
    //執行棧中的代碼塊
    int callError = lua_pcall(L,0,0,0);
    if(callError)
    {
        printf_s("%s\n",lua_tostring(L,-1));
        lua_pop(L,1);
    }
 
    fun1(L);
    lua_close(L);
    return 0;
}

 

然後是lua1.lua:

\

執行結果

\

可以看到,c++中的fun1調用了lua中的fun3,而lua中的fun4調用了fun2,這樣就完成了最基本的交互。需要注意的是,lua_setglobal和lua_getglobal等方法需要在代碼塊被執行後(lua_pcall)才能生效。也就是說,如果只是把代碼塊加載進了棧中,但是不執行這些代碼塊,那麼是獲取不到棧中的這些全局變量的。

 

(4)函數的傳參、返回值

我們對(3)中的lua1.cpp修改一下

lua1.cpp:

 

// lua1.cpp : 定義控制台應用程序的入口點。
//
 
#include "stdafx.h"
#include "iostream"
#include "stdio.h"
#include "string.h"
//lua.h和lualib.h作為C代碼運行,在C++文件中使用lua.h和lualib.h,需使用extern命令實現混合編程,否則會提示無法解析外部函數
extern "C"
{
    #include "lua.h"
    #include "lauxlib.h"
    #include "lualib.h"
}
using namespace std;
#pragma comment(lib,"lua5.1.lib")
 
void fun1(lua_State* L)
{
    printf_s("I am fun1,I'll call fun3\n");
    //在全局范圍內獲得"fun3"的這個元素,並將這個元素的內容壓入棧中
    lua_getglobal(L,"fun3");
    //給fun3函數傳參
    lua_pushnumber(L,2);
    lua_pushnumber(L,5);
    int callError;
    //由於有fun3有兩個參數,同時有一個返回值,因此lua_pcall寫成lua_pcall(L,2,1,0)
    //lua_pcall會執行fun3方法,同時把fun3和處於棧頂的兩個參數一起彈出棧,然後將fun3的返回值壓桟
    callError = lua_pcall(L,2,1,0);
    if(callError)
    {
        printf_s("%s\n",lua_tostring(L,-1));
        printf_s("call fun3 error.");
        lua_pop(L,1);
    }
    else
    {
        //讀出fun3的返回值,在棧頂
        int returnNum = lua_tonumber(L,-1);
        printf_s("fun3 return num:%d\n",returnNum);
    }
}
 
int fun2(lua_State* L)
{
    printf_s("I am fun2~~~~~~~~~\n");
 
    //獲得當前棧中被調用的函數的第一個參數,也就是"the param from fun4"
    const char* paramStr = luaL_checkstring(L,1);
    printf_s("I get the str:");
    printf_s(paramStr);
    printf_s("\n");
 
    //把這個參數彈出棧
    lua_pop(L,1);
    //把返回值壓入棧中
    lua_pushstring(L,"fun2 getted the param.");
 
    //返回值的個數為1
    return 1;
}
 
int _tmain(int argc, _TCHAR* argv[])
{
    char buff[256];
    int error;
    //創建一個lua狀態機(虛擬棧),這個狀態機沒有包含預定義的lua庫函數
    lua_State* L = luaL_newstate();
    //打開標准庫,這樣狀態機可以使用lua庫函數,可以解釋lua腳本
    luaL_openlibs(L);
    char fileName[] = "F:/LuaFile/lua1.lua";
    //加載lua文件,解釋器將lua文件的內容讀取出來,動態編譯為代碼塊
    int loadFileError = luaL_loadfile(L,fileName);
    //打印錯誤信息
    if(loadFileError)
    {
        printf_s("%s\n",lua_tostring(L,-1));
        lua_pop(L,1);
    }
 
    //向棧中壓入c++的fun2
    lua_pushcfunction(L,fun2);
    //在棧中給fun2命名為"fun2",這樣其他地方就能根據"fun2"這個字段索引到fun2的內容
    lua_setglobal(L,"fun2");
 
    //執行棧中的代碼塊
    int callError = lua_pcall(L,0,0,0);
    if(callError)
    {
        printf_s("%s\n",lua_tostring(L,-1));
        lua_pop(L,1);
    }
 
    fun1(L);
    lua_close(L);
    return 0;
}

 

然後修改一下lua1.lua的內容

\

執行程序,看看結果

\

可以看到c++的代碼和lua的代碼已經實現了相互的傳參和獲取返回值。

除了上述這些交互部分,還有許多種常見的數據交互,如c++中類、lua中的table等,這些部分我們在後面的篇章中再進行敘述,這篇文章就先這樣了,嗯 = =。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved