Lua與C++交互初探之Lua調用C++


Lua與C++交互初探之Lua調用C++

上一篇我們已經成功將Lua的運行環境搭建了起來,也成功在C++里調用了Lua函數。今天我來講解一下如何在Lua里調用C++函數。

Lua作為一個輕量級腳本語言,他只包含了一些必要的系統庫函數,當有需要時還得自己去寫。有一次我要做一個兩數異或的操作發現函數庫里居然沒有異或運算。不得不非常苦逼的自己去寫。后來接觸Lua深了之后才知道將這種"缺陷"可以由C函數來彌補。但要做到這一點對於一個對C只知道if else的學生來說確實還是有不少難度。

在學習調用的時候我也確實遇到了不少問題。一開始當然是看不懂代碼,各種指針以及莫名其妙的函數,各種頭大啊有木有。后來試着靜下心來慢慢理了一下Hello World程序,覺得是一知半解了就把代碼拷到編譯器里運行一下。是的,各種報錯。錯得我都沒信心再看下去了。

好了,上面都是一些沒用的,現在開始步入正題。

 

Lua調用C++有兩種方法:

  1. 將C函數作為應用程序的一部分
  2. 將C函數作為Lua的一個模塊

這里解釋一下,將C函數作為程序的一部分,就是說源文件有main方法,函數在main方法里注冊,使得c函數成為Lua里的全局函數,供lua直接調用;將C函數作為Lua的一個模塊。本人比較喜歡這種方式。其實是將C函數注冊后封裝到一個模塊中,在windows系統中以dll的文件形式存在。在使用時我們只需要將他require進來就行了,另外dll文件也非常方便復制和轉移,也保護了源碼。所以下面將主要講解如何將C函數注冊成為Lua的模塊庫。記住:我之所以說是C函數,是因為函數必須以C的形式導出,因此在C++里在函數名前必須加"extern "C"";這一點下面你將會看到

下面來講講步驟:

 

    1. 在VS里新建一個VC++的win32項目(控制台應用亦可)
    2. 點擊下一步選擇dll(以前我們為了省事直接點了finish,這里我們得改一下后面的設置)
    3. 新建完成后配置好lua在VS里的運行環境,不會的可以看一下我的上一篇文章
    4. 輸入下面代碼

       

      // Mydll.cpp : 定義 DLL 應用程序的導出函數。
      //
      
      #include "stdafx.h"
      #include <stdio.h>
      #include <string.h>
      #include <lua.hpp>
      #include <lauxlib.h>
      #include <lualib.h>
      
      //待注冊的C函數
      //需要說明的是,該函數必須以C的形式被導出,因此extern "C"是必須的。
      //定義一個求取平均數的函數
      extern "C" int average(lua_State* L)
      {
          double sum=0;
          int num = lua_gettop(L);//獲取參數的個數
          for (int i = 1; i <= num;i++)
              sum+= lua_tonumber(L, i);
          //依次獲取所有參數值,相加
          lua_pushnumber(L, sum/num);//將平均數壓如棧,供lua獲取
          
          return 1;//返回返回值個數,通知lua應該在棧里取幾個值作為返回結果
      }
      
      extern "C" int Communicate(lua_State* L)
      {
          const char *name = lua_tostring(L, 1);//獲取字符串
          printf("Hello %s\n", name);
          printf("I`m in C,I send a message to you");
          lua_pushstring(L, "This message from C");
          return 1;
      }
      //luaL_Reg結構體的第一個字段為字符串,在注冊時用於通知Lua該函數的名字。
      //第一個字段為C函數指針。
      //結構體數組中的最后一個元素的兩個字段均為NULL,用於提示Lua注冊函數已經到達數組的末尾。
      static luaL_Reg cMethods[] = {
          { "average", average },
          { "Communicate", Communicate },
          { NULL, NULL }
      };
      
      
      //該C庫的唯一入口函數。其函數簽名等同於上面的注冊函數。見如下幾點說明:
      //1. 我們可以將該函數簡單的理解為模塊的工廠函數。
      //2. 其函數名必須為luaopen_xxx,其中xxx表示library名稱。Lua代碼require "xxx"需要與之對應。
      //3. 在luaL_register的調用中,其第一個字符串參數為模塊名"xxx",第二個參數為待注冊函數的數組。
      //4. 需要強調的是,所有需要用到"xxx"的代碼,不論C還是Lua,都必須保持一致,這是Lua的約定,
      //   否則將無法調用。
      extern "C" __declspec(dllexport)
      int luaopen_Mydll(lua_State* L)
      {
          const char* libName = "Mydll";
          luaL_register(L, libName, cMethods);
          return 1;
      }

       

       

    5. 點擊運行后我們把編譯好的dll文件復制到lua解析器能找到的位置(lua文件同目錄或者LUA_CPATH指向的路徑一般在lua安裝目錄的clibs目錄下)
    6. 下面編寫lua測試代碼

       

      require "Mydll"  --引入包
      
       --在調用時,必須是package.function
      
      print(Mydll.average(1.0,2.0,3.0,4));
      print(Mydll.Communicate("Zack"));

       

       

    7. 測試運行

    注意他的打印順序,因為我在Communicate函數里只向棧里壓了"This message from C"這一個字符串,所以在Lua端他只向棧里取了一個元素作為返回值。還有就是注意一下在C++里面的輸出,一般情況我們使用C++處理邏輯不會有輸出操作,不過他的順序沒有反。下面有個很有意思的東西

    把Communicate函數提到average函數前面,結果更意外:

                       

  C++的所有輸出都放在了最后面


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM