天天看點

Lua 與C/C++ 互動系列: Lua調用C/C++函數(4-1)

 1、本文将繼續講解在Lua Code中調用注冊的C函數.偶在學習本文知識點時,由于知識點的遺漏,在這個上面浪費了大量時間和精力。一直都沒有沒明白,Lua 通過面向對象的方式是如果調用注冊的C函數。在Programming In Lua一書,有對這個方面的講解。但是當時看書,就是不了解。因為在前面的章節中,有一個重要的知識點被遺漏。在Lua 元方法中,有兩個特别重要的.__index 和__newindex被我遺漏。這兩個元方法特别重要,對于定義和拓展Lua的機制,基本依靠這兩個。

2、本文首先由Lua Code中對__index 和__nexindex 的講解引入,來對通過Lua Code 面向對象調用注冊的C函數。

     2.1 在Lua Code中講解metatable中__index和__newindex兩個重要的元方法

     2.2 注冊C子產品

     2.3 通過Lua Code面向對象調用注冊的C函數。

3、本文作為在Lua 中實作面向對象程式設計和在Lua中綁定C++對象的一個鋪墊。在Lua的演化中,作者并沒有把面向對象特點加入的Lua的語言中,但是,一個語言的發展深入當時環境的影響,Lua在發展時,由使用者的原因,被迫滿足在Lua中提供面向對象的需求。在Lua中實作面向對象隻要依靠Table和metatable兩個機制,來實作面向對象的形式。

4、本文不是詳細講解Lua 編寫語言的知識點,但是會總結關鍵知識點。對于Lua本身的學習,主要Programming In Lua 和Lua  Reference Manual 足以.

5.1 下面LuaCode将講解元方法和Lua Code中實作面試對象調用:

object ={}
methodTab ={}

setmetatable(object, methodTab)

methodTab.__tostring=function()
    print("object tostring...")
    return "object"
end
methodTab.__call=function() 
   print("object call...")
end
methodTab["method"]=function()
   print("object method...")
   return "object method"
end
object() --對Table進行函數調用,對于__call 元函數
tostring(object)
print(object)
<span style="color:#ff0000;">methodTab.__index =methodTab  --__index元方法表示對Object Table的進行定義.當調用Object["method"]擷取Object指定鍵操作時,如果Object中不存在指定鍵,</span>
           
<span style="color:#ff0000;">則在查詢Object是否存在metatable,如果存在metatable則在metatable中查詢指定鍵元素。</span>
local m =object["method"]

print(m)
m()
           
<pre name="code" class="plain">object["method"]()
           

由于在忽悠__index 和__nexindex知識點,在後來的學習中,總是了解不清楚,浪費了大量的時間。在此特别羅列出知識點,希望能夠減少讀者在錯誤的道路上浪費的時間。

5.2  下面例子代碼來自于Programming In Lua的章節中。來講述注冊C子產品和通過Lua面向對象調用注冊的C函數。在Lua中面向對象調用,實際是Lua作者不想把面向對象機制引入Lua語言本身,導緻語言複雜性,而引入一種文法糖來實作面向對象調用。在Lua作者的文章中多次提及Lua語言本身不提供面向對象機制,但是提供對面向對象的支援。

後面的文章會繼續設計Lua的面向對象東東,包括對Lua作者面向對象觀點的翻譯。

extern "C" 
{
	#include <lua.h> 
	#include <lualib.h>
	#include <lauxlib.h>
}
#include <stdio.h>
#define CnExampleStr "example"
//在C語言中的結構體,綁定到Lua語言中
typedef struct 
{
  int Val;
  int Open;
} ExampleType, * ExamplePtrType;
static ExamplePtrType LclExamplePtrGet(lua_State *L,int StkPos)
{
  ExamplePtrType ExamplePtr = (ExamplePtrType)luaL_checkudata(L, StkPos, CnExampleStr);
  if (! ExamplePtr->Open)
    luaL_error(L, "attempt to use a closed " CnExampleStr);
  return ExamplePtr;
}

static int LclExampleStr(lua_State *L)
{
  ExamplePtrType  ExamplePtr = (ExamplePtrType)luaL_checkudata(L, 1, CnExampleStr);
  if (ExamplePtr->Open)
    lua_pushfstring(L, CnExampleStr " (%d)", ExamplePtr->Val);
  else lua_pushfstring(L, CnExampleStr " (%d, closed)", ExamplePtr->Val);
  return 1;
}
//擷取c結構體資料
static int LclExampleGet(lua_State *L)
{ 
  ExamplePtrType ExamplePtr = LclExamplePtrGet(L, 1);
  lua_pushnumber(L, ExamplePtr->Val);
  printf("Retrieving value of " CnExampleStr " (%d)\n", ExamplePtr->Val);
  return 1;
} 
//設定c結構體資料
static int LclExampleSet(lua_State *L)
{ 

  ExamplePtrType ExamplePtr = LclExamplePtrGet(L, 1);
  int  Val = luaL_checkint(L, 2);
  printf("Setting value of " CnExampleStr " from %d to %d\n",
    ExamplePtr->Val, Val);
  lua_pushnumber(L, ExamplePtr->Val);
  ExamplePtr->Val = Val;
  return 1;
}
//關閉結構體
static int LclExampleClose(lua_State *L)
{
  ExamplePtrType ExamplePtr = LclExamplePtrGet(L, 1);
  printf("Closing " CnExampleStr " (%d) explicitly\n", ExamplePtr->Val);
  ExamplePtr->Open = 0;
  return 0;
}
//通過Lua Code構造C語言結構體
static int LclExampleOpen(lua_State *L)
{ 
  //接受LuaCode傳遞參數
  int Val = luaL_checkint(L, 1);
  //申請由lua GC管理下的記憶體
  ExamplePtrType  ExamplePtr =(ExamplePtrType)lua_newuserdata(L, sizeof(ExampleType));
  printf("Opening " CnExampleStr " (%d)\n", Val);
  //設定資料
  ExamplePtr->Val = Val;
  ExamplePtr->Open = 1;
  在系統資料庫中查詢注冊的C函數
  luaL_getmetatable(L, CnExampleStr);
  //設定userdata的CnExampleStr的metatable
  lua_setmetatable(L, -2);
  return 1;
}
void luaopen_register(lua_State *L)
{
 static const luaL_reg regMethod[] = 
 {
	{"open", LclExampleOpen},
    {"close", LclExampleClose},
    {"get", LclExampleGet},
    {"set", LclExampleSet},
    {"tostring", LclExampleStr},
    {NULL, NULL}
  }; 
  //建立一個metatable用于辨別userdata唯一性
  luaL_newmetatable(L, CnExampleStr);
  luaL_register(L,"ud_example",regMethod);
}
int main(int argc, char **argv)
{
	/* initialize Lua */
	lua_State* L = lua_open();
	luaL_openlibs(L);
	//luaopen_ud_example(L);
	luaopen_register(L);
	luaL_dofile(L, "sample_6.lua");
	/* cleanup Lua */
	lua_close(L);
	return 1;
}
           

sample_6.lua  測試代碼如下:

print("sample_6.lua")

local ud1 =ud_example.open(1)
print(ud1)
print(ud_example.tostring(ud1))

local a =ud_example.get(ud1)
print("a:"..a)

local b =ud_example.set(ud1,2)
print("b="..b)
           

整個注冊子產品和Lua Code調用示範子產品已經完成。至于Lua Code如何管理C/C++對象的生命周期,會在後面陸續介紹。在這個例子中沒有涉及到Lua GC垃圾收集器。

到目前為止隻要記住,Lua為了提高使用者自定義資料類型,提供了userdata資料類型。userdata資料類型的生命周期由Lua GC管理。當userdata管理的C/C++對象時,當GC把userdata當做垃圾收集時,會調用__gc  userdata的元方法。假如,C/C++對象生命周期由Lua GC管理,那麼此時是釋放C/C++對象的唯一機會。

本文中涉及到環境和userdata的如何使用?可以參考Lua  Reference Manual  .在本系列文章中,将會不斷的講解userdata的由來和使用方式方法。

本來本文應該完成第三部分,通過Lua Code面向對象文法糖調用注冊的C函數,無奈,這内容比較多,涉及知識點較多。和老婆約好了,11點去同僚家完,不想本文草草了事情,是以第三部以及涉及的知識點,将在下一張詳細講述。

繼續閱讀