三國殺(3):原始C/C++ lua 編譯 集成,及使用介紹

一. c++ 調用lua

 

1. lua文件

str = "I am learning lua!"
tbl = {name = "shun", id = 20200501}
function add(a,b)
    return a + b
end

//////////////////////////////////////////////////////////////////////////////////////////

2. main.cpp文件

 

#include <iostream>
#include <string.h>
using namespace std;

extern "C"
{
    #include "lua.h"
    #include "lauxlib.h"
    #include "lualib.h"
}

 

int main()
{
    //1.創建Lua狀態
    lua_State *L = luaL_newstate();
    if (L == NULL)
    {
        return -1;
    }

    //2.加載lua文件
    int bRet = luaL_loadfile(L,"scripts/testlua.lua");
    if(bRet)
    {
        cout<<"load file error"<<endl;
        return -1;
    }

    //3.運行lua文件
    bRet = lua_pcall(L,0,0,0);
    if(bRet)
    {
        cout<<"pcall error"<<endl;
        return -1;
    }

    //4.讀取變量
    lua_getglobal(L,"str");
    string str = lua_tostring(L,-1);
    cout<<"str = "<<str.c_str()<<endl;        //str = I am so cool~

    //5.讀取table
    lua_getglobal(L,"tbl");
    lua_getfield(L,-1,"name");
    str = lua_tostring(L,-1);
    cout<<"tbl:name = "<<str.c_str()<<endl; //tbl:name = shun

    //6.讀取函數
    lua_getglobal(L, "add");        // 獲取函數,壓入棧中
    lua_pushnumber(L, 10);            // 壓入第一個參數
    lua_pushnumber(L, 20);            // 壓入第二個參數
    int iRet= lua_pcall(L, 2, 1, 0);// 調用函數,調用完成以後,會將返回值壓入棧中,2表示參數個數,1表示返回結果個數。
    if (iRet)                        // 調用出錯
    {
        const char *pErrorMsg = lua_tostring(L, -1);
        cout << pErrorMsg << endl;
        lua_close(L);
        return -1;
    }
    if (lua_isnumber(L, -1))        //取值輸出
    {
        double fValue = lua_tonumber(L, -1);
        cout << "Result is " << fValue << endl;
    }

    //至此,棧中的情況是:
    //=================== 棧頂 ===================
    //  索引  類型      值
    //   4   int:      30
    //   3   string:   shun
    //     2     table:        tbl
    //   1   string:    I am so cool~
    //=================== 棧底 ===================

    //7.關閉state
    lua_close(L);

    return 0;

}

運行結果:

二.lua調用c++


主要兩個方法實現:

 

方法一:靜態註冊

大概順序就是:在c++中寫一個模塊函數,將函數註冊到lua解釋器中,然後由c++去執行我們的lua文件,然後在lua中調用剛剛註冊的函數。


1. 在目錄下新建一個testlua.lua如下:

    avg, sum = average(10, 20, 30, 40, 50)
    print("The average is ", avg)
    print("The sum is ", sum)


2.新建main.cpp如下:

#include <iostream>
#include <string.h>
using namespace std;

extern "C"
{
    #include "lua.h"
    #include "lauxlib.h"
    #include "lualib.h"
}


/* 指向Lua解釋器的指針 */
lua_State* L;
static int average(lua_State *L)
{
    /* 得到參數個數 */
    int n = lua_gettop(L);
    double sum = 0;
    int i;

    /* 循環求參數之和 */
    for (i = 1; i <= n; i++)
    {
        /* 求和 */
        sum += lua_tonumber(L, i);
    }
    /* 壓入平均值 */
    lua_pushnumber(L, sum / n);
    /* 壓入和 */
    lua_pushnumber(L, sum);
    /* 返回返回值的個數 */
    return 2;
}

int main ( int argc, char *argv[] )
{

    //1.創建Lua狀態
    lua_State *L = luaL_newstate();
    if (L == NULL)
    {
        return -1;
    }

    /* 載入Lua基本庫 */
    luaL_openlibs(L);
    /* 註冊函數 */
    lua_register(L, "average", average);
    /* 運行腳本 */
    bool bRet = luaL_dofile(L, "scripts/testlua.lua");
    if(bRet)
    {
        cout<<"load file error"<<endl;
        return -1;
    }
    /* 清除Lua */
    lua_close(L);

    /* 暫停 */
    system("pause");
    return 0;
}

執行一下,得到結果:

 


方法2:綁定C++對象

1. 建立工程

2. 封裝C++類

1)CMath.H

#ifndef CMATH_H
#define CMATH_H

extern "C"
{
    #include "lua.h"
    #include "lauxlib.h"
    #include "lualib.h"
}

class CMath
{
public:
    CMath();
    virtual ~CMath(){}
    void setNum(int x);
    int getNum();
    int Add(int x, int y);

    int Sub(int x, int y);
    int Mul(int x, int y);
    private:
        int num;

};

#ifdef __cplusplus
extern "C" {
#endif
    int luaopen_cmath(lua_State *L);
#ifdef __cplusplus
}
#endif
#endif // CMATH_H

 

2) CMath.CPP

#include "cmath.h"
#include "iostream"
using namespace std;


CMath::CMath()
{

}


void CMath::setNum(int x)
{
    num = x;
}
int CMath::getNum()
{
    return 0;
}
int CMath::Add(int x, int y)
{

    cout<<"%p Add="<<this<<",X="<<x<<",Y="<<y<<endl;
    return x + y;
};
int CMath::Sub(int x, int y)
{
    return x-y;
}
int CMath::Mul(int x, int y)
{
    return x*y;
}


////////////////////////////////////////////////////////////////////////////

static int CreateCMath(lua_State* L)
{
    // 創建一個元表爲CTest的Table——Lua對象
    *(CMath**)lua_newuserdata(L, sizeof(CMath*)) = new CMath();
    luaL_getmetatable(L, "CMath");
    lua_setmetatable(L, -2);
    return 1;
}

static int DestoryCTest(lua_State* L)
{
    // 釋放對象
    delete *(CMath**)lua_topointer(L, 1);
    return 0;
}

static int CallAdd(lua_State* L)
{
    int  cnt = lua_gettop (L);

    // 調用C++類方法的跳板函數。
    CMath* pT = *(CMath**)lua_topointer(L, 1);
    lua_pushnumber(L, pT->Add(lua_tonumber(L, 2), lua_tonumber(L, 3)));
    return 1;
}
static int CallSub(lua_State* L)
{
    int  cnt = lua_gettop (L);

    // 調用C++類方法的跳板函數。
    CMath* pT = *(CMath**)lua_topointer(L, 1);
    lua_pushnumber(L, pT->Sub(lua_tonumber(L, 2), lua_tonumber(L, 3)));
    return 1;
}
static int CallMul(lua_State* L)
{
    int  cnt = lua_gettop (L);

    // 調用C++類方法的跳板函數。
    CMath* pT = *(CMath**)lua_topointer(L, 1);
    lua_pushnumber(L, pT->Mul(lua_tonumber(L, 2), lua_tonumber(L, 3)));
    return 1;
}
static int CallgetNum(lua_State* L)
{
    int  cnt = lua_gettop (L);
    // 調用C++類方法的跳板函數。
    CMath* pT = *(CMath**)lua_topointer(L, 1);
    lua_pushnumber(L, pT->getNum());
    return 1;
}
static int CallsetNum(lua_State* L)
{
    int  cnt = lua_gettop (L);

    // 調用C++類方法的跳板函數。
    CMath* pT = *(CMath**)lua_topointer(L, 1);
    pT->setNum(lua_tonumber(L, 2));
    return 0;
}

static luaL_Reg arraylib_m [] = {
    {"Add", CallAdd},
    {"Sub", CallSub},
    {"Mul", CallMul},
    {"setNum", CallsetNum},
    {"getNum", CallgetNum}, //print(a)時Lua會調用該元方法。
    {NULL, NULL}
};

int luaopen_cmath(lua_State *L){
    // 往lua中註冊類
    lua_pushcfunction(L, CreateCMath);    // 註冊用於創建類的全局函數
    lua_setglobal(L,  "CMath");

    luaL_newmetatable(L, "CMath");           // 創建一個元表
    lua_pushvalue(L,-1);

    lua_pushstring(L, "__gc");                    // 垃圾回收
    lua_pushcfunction(L, DestoryCTest);
    lua_settable(L, -3);                               // 公共函數調用的實現就在此啊

    lua_pushstring(L, "__index");
    lua_pushvalue(L, -2);                           // 注意這一句,其實是將__index設置成元表自己
    lua_settable(L, -3);

//    lua_pushstring(L, "Add");                     // 放元表中增加一個函數。這樣所有基於該元表的Table就都有Add方法了
//    lua_pushcfunction(L, CallAdd);
//    lua_settable(L, -3);
//
//    lua_pushstring(L, "Sub");                     // 放元表中增加一個函數。這樣所有基於該元表的Table就都有Add方法了
//    lua_pushcfunction(L, CallSub);
//    lua_settable(L, -3);
//
//    lua_pushstring(L, "Mul");                     // 放元表中增加一個函數。這樣所有基於該元表的Table就都有Add方法了
//    lua_pushcfunction(L, CallMul);
//    lua_settable(L, -3);
//
//    lua_pushstring(L, "setNum");                     // 放元表中增加一個函數。這樣所有基於該元表的Table就都有Add方法了
//    lua_pushcfunction(L, CallsetNum);
//    lua_settable(L, -3);
//
//    lua_pushstring(L, "getNum");                     // 放元表中增加一個函數。這樣所有基於該元表的Table就都有Add方法了
//    lua_pushcfunction(L, CallgetNum);
//    lua_settable(L, -3);
//    lua_register(L, "getNum", CallgetNum);

//    lua_pop(L,1);
    luaL_setfuncs(L,arraylib_m,0);
    return 0;
}


3) main.cpp測試函數

#include <iostream>
#include <string.h>
#include <cmath.h>

using namespace std;


int main (  )
{

    lua_State *L = luaL_newstate();
    if (L == nullptr)
    {
        return -1;
    }

    luaL_openlibs(L);

    luaopen_cmath(L); //打開封裝的C++lib庫

    int bRet = luaL_dofile(L, "scripts/testlua.lua");
    if (bRet)
    {
        cout << "call testlua.lua file failed" << endl;
        return -1;
    }


    lua_close(L);
    // waiting for console
    cin.get();
    return 0;
}

4) testlua.lua調用C++方法

--第四步:在lua中調用 cpp_add,cpp_sub直接操作
--testlua. lua代碼
c = CMath()
d = CMath()
print("c.Add(1, 2) ==> " .. c:Add(1, 2));
print("d.Sub(104, 5) ==> " .. d:Sub(104, 5));
print("d.Mul(104, 5) ==> " .. d:Mul(104, 5));
--c::setNum(23);
print(c:getNum());

--print("d.Add(4, 5) ==> " .. d:Add(4, 5));

5)運行結果

三.總結

lua和c++是通過一個虛擬棧來交互的。

c++調用lua實際上是:由c++先把數據放入棧中,由lua去棧中取數據,然後返回數據對應的值到棧頂,再由棧頂返回c++。

lua調c++也一樣:先編寫自己的c模塊,然後註冊函數到lua解釋器中,然後由lua去調用這個模塊的函數。

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