与Lua交换自定义数据

使用metatable提供面向对象调用方式

    上面的VCL代码库为Lua提供了GUI的支持,但是看那些Lua代码,还处于面向过程时期。如何能把VCL.show(edt)之类的代码改成edt: show()这样的形式呢?还是先看代码:

  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. extern "C" {
  4. #include "lua.h"
  5. #include "lualib.h"
  6. #include "lauxlib.h"
  7. }
  8.  
  9. #include <iostream>
  10. #pragma hdrstop
  11. //---------------------------------------------------------------------------
  12. #pragma argsused
  13.  
  14. typedef TWinControl* PWinControl;
  15. //创建窗体,输入父窗体(或nil),类型,标题
  16. //输出创建后的窗体
  17. int newCtrl(lua_State *L)
  18. {
  19.     //input:TWinControl *Parent, type(TForm,TButton,TEdit), text(optional)
  20.     TWinControl *Parent = NULL;
  21.  
  22.     if(lua_isuserdata(L,1))
  23.         Parent = *(PWinControl*)luaL_checkudata(L,1,"My_VCL");
  24.     String Type = UpperCase(luaL_checkstring(L, 2));
  25.     String Text = lua_tostring(L, 3);
  26.  
  27.     TWinControl *R = NULL;
  28.  
  29.     if(Type == "FORM")
  30.         R = new TForm(Application);
  31.     else if(Type == "BUTTON")
  32.         R = new TButton(Application);
  33.     else if(Type == "EDIT")
  34.         R = new TEdit(Application);
  35.     else
  36.         luaL_error(L, "unknow type!");
  37.  
  38.     if(Parent)
  39.         R->Parent = Parent;
  40.  
  41.     if(!Text.IsEmpty())
  42.         ::SetWindowText(R->Handle, Text.c_str());
  43.  
  44.     //output TWinControl*
  45.     PWinControl* pCtrl = (PWinControl*)lua_newuserdata(L,sizeof(PWinControl));
  46.     *pCtrl = R;
  47.     //关联metatable
  48.     luaL_getmetatable(L, "My_VCL");
  49.     lua_setmetatable(L, -2);
  50.     return 1;
  51. }
  52.  
  53. //显示窗体
  54. int showCtrl(lua_State *L)
  55. {
  56.     //input: TWinControl*, for TForm, use ShowModal
  57.     TWinControl* Ctrl = *(PWinControl*)luaL_checkudata(L,1,"My_VCL");
  58.     TForm *fm = dynamic_cast<TForm*>(Ctrl);
  59.     if(fm)
  60.         fm->ShowModal();
  61.     else
  62.         Ctrl->Show();
  63.     return 0;
  64. }
  65.  
  66. //定位窗体,输入窗体,左,上,右,下
  67. int posCtrl(lua_State *L)
  68. {
  69.     //input: TWinControl*, Left, Top, Right, Bottom
  70.     TWinControl* Ctrl = *(PWinControl*)luaL_checkudata(L,1,"My_VCL");
  71.     Ctrl->BoundsRect = TRect(
  72.         luaL_checkint(L, 2),
  73.         luaL_checkint(L, 3),
  74.         luaL_checkint(L, 4),
  75.         luaL_checkint(L, 5));
  76.  
  77.     return 0;
  78. }
  79.  
  80. //删除窗体
  81. int delCtrl(lua_State *L)
  82. {
  83.     //input: TWinControl*
  84.     TWinControl* Ctrl = *(PWinControl*)luaL_checkudata(L,1,"My_VCL");
  85.     delete Ctrl;
  86.     return 0;
  87. }
  88.  
  89. //把这些函数作为VCL函数库提供给Lua
  90. static const struct luaL_reg lib_VCL [] = {
  91.     {"new", newCtrl},
  92.     {"del", delCtrl},
  93.     {"pos", posCtrl},
  94.     {"show", showCtrl},
  95.     {NULL, NULL}
  96. };
  97.  
  98. int luaopen_VCL (lua_State *L) {
  99.     //建立metatable
  100.     luaL_newmetatable(L, "My_VCL");
  101.  
  102.     //查找索引,把它指向metatable自身(因为稍后我们会在metatable里加入一些成员)
  103.     lua_pushvalue(L, -1);
  104.     lua_setfield(L,-2,"__index");
  105.  
  106.     //pos方法
  107.     lua_pushcfunction(L, posCtrl);
  108.     lua_setfield(L,-2,"pos");
  109.  
  110.     //show方法
  111.     lua_pushcfunction(L, showCtrl);
  112.     lua_setfield(L,-2,"show");
  113.  
  114.     //析构,如果表里有__gc,Lua的垃圾回收机制会调用它。
  115.     lua_pushcfunction(L, delCtrl);
  116.     lua_setfield(L,-2,"__gc");
  117.  
  118.     luaL_register(L, "VCL", lib_VCL);
  119.     return 1;
  120. }
  121.  
  122. int main(int argc, char* argv[])
  123. {
  124.     char* szLua_code=
  125.         "local fm = VCL.new(nil,'Form','Lua Demo'); "    //新建主窗体fm
  126.         "fm:pos(200, 200, 500, 300); "        //定位
  127.         "local edt = VCL.new(fm, 'Edit', 'Hello World'); "  //在fm上建立一个编辑框edt
  128.         "edt:pos(5, 5, 280, 28); "
  129.         "local btn = VCL.new(fm, 'Button', 'Haha'); "    //在fm上建立一个按钮btn
  130.         "btn:pos(100, 40, 150, 63); "
  131.         "edt:show(); "
  132.         "btn:show(); "
  133.         "fm:show(); ";                       //显示
  134.         //"VCL.del(fm);";   //不再需要删除了,Lua的垃圾回收在回收userdata地会调用metatable.__gc。
  135.  
  136.     lua_State *L = luaL_newstate();
  137.     luaL_openlibs(L);
  138.     luaopen_VCL(L);
  139.  
  140.     bool err = luaL_loadstring(L, szLua_code) || lua_pcall(L, 0, 0, 0);
  141.     if(err)
  142.     {
  143.         std::cerr << lua_tostring(L, -1);
  144.         lua_pop(L, 1);
  145.     }   
  146.  
  147.     lua_close(L);
  148.     return 0;
  149. }
  150. //---------------------------------------------------------------------------


我们这儿用到的辅助函数有:

int   luaL_newmetatable (lua_State *L, const char *tname);
    创建一个新表(用于metatable),将新表放到栈顶并在注册表中建立一个类型名与之联系。
void  luaL_getmetatable (lua_State *L, const char *tname);
    获取注册表中tname对应的metatable。
int   lua_setmetatable  (lua_State *L, int objindex);
    把一个table弹出堆栈,并将其设为给定索引处的值的 metatable。
void *<span class="KSFIND_CLASS" id="4KSFindDIV" style="text-align: center; -webkit-box-shadow: rgba(0, 0, 0, 0.74902) 0px 1px 3px; bottom: 0px !important; left: 0px !important; right: 0px !important; top: 0px !important; height: inherit !important; width: inherit !important; float: none !important; color: black !important; padding: 2px 0px 1px !important; display: inline !important; position: relative !important; z-index: 3000000 !important; background: none 0px 0px repeat scroll rgb(255, 244, 151) !important;">luaL_checkudata</span> (lua_State *L, int index, const char *tname);
    检查在栈中指定位置的对象是否为带有给定名字的metatable的userdata。

    
    我们只改动了luaopen_VCLnewCtrl函数。
    在luaopen_VCL里,我们建立了一个metatable,然后让它的__index成员指向自身,并加入了pos,show函数成员和__gc函 数成员。
    在newCtrl里,我们把luaopen_VCL里建立的metatable和新建的userdata关联,于是:

  • 对userdata的索引操作就会转向metatable.__index
  • 因为metatable.__index是metatable自身,所以就在这个metatable里查找
  • 这样,对userdata的pos、show索引转到metatable里的pos和show上,它们指向的是我们的C函数posCtrl和 posShow。
  • 最后,当Lua回收这些userdata前,会调用metatable.__gc(如果有的话),我们已经把metatable.__gc指向了C函数 delCtrl。

    加入metatable后,我们还得到了额外的好处:可以区分不同的userdata以保证类型安全,我们把所以的lua_touserdata改成了luaL_checkudata
    关于metatable的知识已超出本文讨论范围,请参考Lua官方手册。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值