2019獨角獸企業重金招聘Python工程師標準>>>
????userdata類型是為了方便C/C++對Lua進行擴展,因為在用C/C++擴展時,我們經常會自定義數據類型,如:
typedef?struct?epoll_fd
{?int?epfd;size_t?size;struct?epoll_event?*events;
}epoll_fd_t;
????我們要想在Lua中使用此類型的對象,就必須使用userdata類型來表示。
? ? 對于Lua而言,所有C/C++自定義的類型都是userdata類型,無法區分,那么在實際的應用中userdata都會設置metatable,通過metattable來區分不同的userdata類型。
????metatable其實也是table,metatable中的方法則稱為“元方法”。對于userdata的metatable,當我們訪問userdata對象的成員時,lua會調用metatable中的__index()元方法,如果__index也是table,則從__index table中查找對應的成員。當lua要回收userdata對象時,會調用metatable中的__gc()元方法。還有很多元方法,可以查看文檔說明。
userdata常用的Lua C API
????1) 創建userdata for lua
void?*lua_newuserdata?(lua_State?*L,?size_t?size);This?function?allocates?a?new?block?of?memory?with?the?given?size,?pushes?onto?the?stack?a?new?full?userdata?with?the?block?address,?and?returns?this?address.Userdata?represent?C?values?in?Lua.?A?full?userdata?represents?a?block?of?memory.?It?is?an?object?(like?a?table):?you?must?create?it,?it?can?have?its?own?metatable,?and?you?can?detect?when?it?is?being?collected.?A?full?userdata?is?only?equal?to?itself?(under?raw?equality).When?Lua?collects?a?full?userdata?with?a?gc?metamethod,?Lua?calls?the?metamethod?and?marks?the?userdata?as?finalized.?When?this?userdata?is?collected?again?then?Lua?frees?its?corresponding?memory.
????從上面的說明,可以知道返回的就是內存地址,我們只需要強制轉換為我們的自定義類型即可使用。
? ?2) 創建metatable: ?
int?luaL_newmetatable?(lua_State?*L,?const?char?*tname);If?the?registry?already?has?the?key?tname,?returns?0.?Otherwise,?creates?a?new?table?to?be?used?as?a?metatable?for?userdata,?adds?it?to?the?registry?with?key?tname,?and?returns?1.
In?both?cases?pushes?onto?the?stack?the?final?value?associated?with?tname?in?the?registry.
? ? 新創建的metatable會存放于registry中,其實registry也是一個table.
????要從registry中獲取指定的metatable:
void?luaL_getmetatable?(lua_State?*L,?const?char?*tname);
Pushes?onto?the?stack?the?metatable?associated?with?name?tname?in?the?registry
????3) 設置metatable:?
int?lua_setmetatable?(lua_State?*L,?int?index);Pops?a?table?from?the?stack?and?sets?it?as?the?new?metatable?for?the?value?at?the?given?acceptable?index.
????metatable其實也是table,所以所有對table操作的方法,均可用在metatable上,包括為metatable設置元方法等。
????4) 從棧中獲取userdata:
void?*luaL_checkudata?(lua_State?*L,?int?narg,?const?char?*tname);
Checks?whether?the?function?argument?narg?is?a?userdata?of?the?type?tname.
It?will?throws?an?error?when?the?given?value?is?not?a?userdata?of?the?expected?type.
2. 對lua-epoll模塊進行簡單的修改,
????在上一篇博文的基礎上,對其源碼進行修改如下:
//?lua_f_epoll.c
#include?"lua_f_base.h"
#include?"epoll_fd.h"#define?EPOLL_METATABLE_NAME?"Epoll.MT"#define?check_epoll_fd(L)?luaL_checkudata(L,?1,?EPOLL_METATABLE_NAME);static?int?lua_f_epoll_close(lua_State?*L)
{epoll_fd_t?*self?=?(epoll_fd_t?*)check_epoll_fd(L);epoll_fd_release(self);return?0;
}static?int?lua_f_epoll_register(lua_State?*L)
{if(lua_gettop(L)?<?3){RETERR("invalid?args");}epoll_fd_t?*self?=?(epoll_fd_t?*)check_epoll_fd(L);int?fd?=?luaL_checkint(L,?2);int?event?=?luaL_checkint(L,?3);if(fd?<?0){RETERR("invalid?fd");}if(epoll_fd_add(self,?fd,?event)?<?0){RETERR("epoll_fd_add?fail");}lua_pushboolean(L,?1);return?1;
}static?int?lua_f_epoll_modify(lua_State?*L)
{if(lua_gettop(L)?<?3){RETERR("invalid?args");}epoll_fd_t?*self?=?(epoll_fd_t?*)check_epoll_fd(L);int?fd?=?luaL_checkint(L,?2);int?event?=?luaL_checkint(L,?3);if(fd?<?0){RETERR("invalid?fd");}if(epoll_fd_mod(self,?fd,?event)?<?0){RETERR("epoll_fd_mod?fail");}lua_pushboolean(L,?1);return?1;
}static?int?lua_f_epoll_unregister(lua_State?*L)
{if(lua_gettop(L)?<?2){RETERR("invalid?args");}epoll_fd_t?*self?=?(epoll_fd_t?*)check_epoll_fd(L);int?fd?=?luaL_checkint(L,?2);if(fd?<?0){RETERR("invalid?fd");}if(epoll_fd_del(self,?fd)?<?0){RETERR("epoll_fd_del?fail");}lua_pushboolean(L,?1);return?1;
}static?int?lua_f_epoll_wait(lua_State?*L)
{epoll_fd_t?*self?=?(epoll_fd_t?*)check_epoll_fd(L);int?timeout?=?luaL_optint(L,?2,?-1);int?n?=?epoll_fd_wait(self,?timeout);if(n?<?0){RETERR("epoll_fd_wait?fail");}lua_newtable(L);int?i;for(i?=?0;?i?<?n;?++i){lua_pushinteger(L,?self->events[i].events);lua_rawseti(L,?-2,?self->events[i].data.fd);}return?1;
}static?const?struct?luaL_Reg?lua_f_epoll_func[]?=?{{"close",?lua_f_epoll_close},{"register",?lua_f_epoll_register},{"modify",?lua_f_epoll_modify},{"unregister",?lua_f_epoll_unregister},{"wait",?lua_f_epoll_wait},{NULL,?NULL},
};static?int?lua_f_epoll_create(lua_State?*L)
{size_t?size?=?luaL_optint(L,?1,?EPOLL_DEFAULT_SIZE);int?epfd?=?epoll_create(size);if(epfd?<?0){RETERR("epoll_create?fail");}epoll_fd_t?*self?=?(epoll_fd_t*)lua_newuserdata(L,?sizeof(epoll_fd_t));if(epoll_fd_init(self,?epfd,?size)?<?0){RETERR("epoll_fd_init?fail");}luaL_getmetatable(L,?EPOLL_METATABLE_NAME);lua_setmetatable(L,?-2);return?1;
}static?int?lua_f_epoll_version(lua_State?*L)
{const?char?*ver?=?"Lua-Epoll?V0.0.1?by?wenhao.ye";lua_pushstring(L,?ver);return?1;
}static?const?struct?luaL_Reg?lua_f_epoll_mod[]?=?{{"create",?lua_f_epoll_create},{"version",?lua_f_epoll_version},{NULL,?NULL},
};/*mt?=?{__gc?=?lua_f_epoll_close,__index?=?{close?=?lua_f_epoll_close,register?=?lua_f_epoll_register,modify?=?lua_f_epoll_modify,unregister?=?lua_f_epoll_unregister,wait?=?lua_f_epoll_wait,},};epoll?=?{create?=?lua_f_epoll_create,version?=?lua_f_epoll_version,EPOLLIN?=?EPOLLIN,EPOLLPRI?=?EPOLLPRI,EPOLLOUT?=?EPOLLOUT,EPOLLRDNORM?=?EPOLLRDNORM,EPOLLRDBAND?=?EPOLLRDBAND,EPOLLWRNORM?=?EPOLLWRNORM,EPOLLWRBAND?=?EPOLLWRBAND,EPOLLMSG?=?EPOLLMSG,EPOLLERR?=?EPOLLERR,EPOLLHUP?=?EPOLLHUP,EPOLLRDHUP?=?EPOLLRDHUP,EPOLLONESHOT?=?EPOLLONESHOT,EPOLLET?=?EPOLLET,};*/
int?luaopen_epoll(lua_State?*L)
{luaL_newmetatable(L,?EPOLL_METATABLE_NAME);LTABLE_ADD_CFUNC(L,?-1,?"__gc",?lua_f_epoll_close);lua_newtable(L);luaL_register(L,?NULL,?lua_f_epoll_func);lua_setfield(L,?-2,?"__index");lua_pop(L,?1);luaL_register(L,?"epoll",?lua_f_epoll_mod);#define?SETCONST(EVENT)?\lua_pushnumber(L,?EVENT);?\lua_setfield(L,?-2,?#EVENT)//?push?const?values?into?epoll?table.SETCONST(EPOLLIN);SETCONST(EPOLLPRI);SETCONST(EPOLLOUT);SETCONST(EPOLLRDNORM);SETCONST(EPOLLRDBAND);SETCONST(EPOLLWRNORM);SETCONST(EPOLLWRBAND);SETCONST(EPOLLMSG);SETCONST(EPOLLERR);SETCONST(EPOLLHUP);SETCONST(EPOLLRDHUP);SETCONST(EPOLLONESHOT);SETCONST(EPOLLET);return?1;
}
????上述代碼用到的關于epoll的操作,我封裝在epoll_fd,h文件中:
//?epoll_fd.h
#ifndef?_EPOLL_FD_H_
#define?_EPOLL_FD_H_#ifdef?__cplusplus
extern?"C"{
#endif#include?"def.h"
#include?"io_tools.h"?//?for?fd_set_nonblock()
#include?<fcntl.h>
#include?<sys/epoll.h>
#include?<string.h>
#include?<errno.h>
#include?<unistd.h>#define?EPOLL_DEFAULT_SIZE?1024?typedef?struct?epoll_fd
{?int?epfd;size_t?size;struct?epoll_event?*events;
}epoll_fd_t;inline?int?epoll_fd_init(epoll_fd_t?*self,?int?epfd,?size_t?size)
{self->epfd?=?epfd;self->size?=?size;self->events?=?(struct?epoll_event?*)MALLOC(sizeof(struct?epoll_event)?*?size);if(self->events?==?NULL)return?-1;return?0;
}inline?int?epoll_fd_release(epoll_fd_t?*self)
{safe_close(self->epfd);safe_free(self->events);return?0;
}inline?int?epoll_fd_add(epoll_fd_t?*self,?int?fd,?int?event)
{if(fd_set_nonblock(fd)?<?0){ERR("fd_set_nonblock?fail");return?-1;}struct?epoll_event?ev;ev.data.fd?=?fd;ev.events?=?event;if(epoll_ctl(self->epfd,?EPOLL_CTL_ADD,?fd,?&ev)?<?0){ERR("epoll_ctl_add?fail");return?-1;}return?0;
}inline?int?epoll_fd_mod(epoll_fd_t?*self,?int?fd,?int?event)
{struct?epoll_event?ev;ev.data.fd?=?fd;ev.events?=?event;if(epoll_ctl(self->epfd,?EPOLL_CTL_MOD,?fd,?&ev)?<?0){ERR("epoll_ctl_mod?fail");return?-1;}return?0;
}inline?int?epoll_fd_del(epoll_fd_t?*self,?int?fd)
{if(epoll_ctl(self->epfd,?EPOLL_CTL_DEL,?fd,?NULL)?<?0){ERR("epoll_ctl_del?fail");return?-1;}return?0;
}inline?int?epoll_fd_wait(epoll_fd_t?*self,?int?timeout)
{return?epoll_wait(self->epfd,?self->events,?self->size,?timeout);
}#ifdef?__cplusplus
}
#endif#endif
????代碼很簡單,也就不過多說明了,上述代碼如果直接拿來用會編譯不過,因為這些代碼是在我的整個工程中,會用到其他的一些宏或者函數,工程代碼地址:?https://git.oschina.net/yewenhao/libutils
????如果不想拿整個工程,只需要把DBG(), ERR(), 換成簡單的打印函數printf即可,其他的函數如下:
#define?RETERR(_e)?\DBG("err:?%s",?(_e));\lua_pushnil(L);?\lua_pushfstring(L,?"%s:?%s",?strerror(errno),?(_e));?\return?2#define?LTABLE_ADD_CFUNC(L,?index,?name,?func)?\lua_pushcfunction(L,?(func));\lua_setfield(L,?(index)-1,?(name));int?fd_set_nonblock(int?fd)
{int?val;if((val?=?fcntl(fd,?F_GETFL,?0))?==?-1){perror("fcntl?get");return?-1;}val?|=?O_NONBLOCK;if(fcntl(fd,?F_SETFL,?val)?==?-1){perror("fcntl?set");return?-1;}return?0;
}
????修改后的epoll模塊的使用參考:
local?epoll?=?require('epoll')
print(epoll.version())local?epfd?=?epoll.create()
epfd:register(fd,?epoll.EPOLLIN)while?true?dolocal?events,?err=?epfd:wait()if?not?err?then--?handle?eventsend--?...
endepfd:unregister(fd)
epfd:close()
print('done')
轉載于:https://my.oschina.net/kaedehao/blog/513222
總結
以上是生活随笔為你收集整理的lua userdata的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。