Hey,
Out of curiosity, I wanted to implement Aspect Oriented Programming into metatables used as objects.
I'm having trouble making the __index
meta-method to work correctly.
My goal is whenever the functions update
or draw
are executed, I also execute the functions called pre_update
, pre_draw
and post_update
and post_draw
if they exist in the same object.
I can't make the code below to work. What am I doing wrong?
function aop_mt(tbl) tbl = tbl or {} tbl.__index = function(tbl,key) local ogfunc = rawget(tbl, key) if (one_of(key, "update", "draw") and is_fun(ogfunc)) then local prefunc = rawget(tbl,"pre_"..key) local postfunc = rawget(tbl,"post_"..key) return function(self,...) if(prefunc) prefunc(self,...) local ret = ogfunc(self,...) if(postfunc) postfunc(self,...) return ret end end return ogfunc end return tbl end --creating an object using aop_mt object = { items = { {name="potion", qty=100, price=120, sell=100}, {name="medicine", qty=100, price=100, sell=80}, {name="revive", qty=10, price=300, sell=180}, {name="attack+", qty=10, price=400, sell=250}, }, --when I call this function, I also want 'pre_update' to run before, and 'post_update' to run after. update=function(self) -- update logic end, pre_update=function(self) -- pre-update logic end, post_update=function(self) -- post-update logic end, } setmetatable(shop,aop_mt()) --utils used above for reference function is_fun(f) return (f!=nil and type(f)=="function") end function one_of(value, ...) if(select("#",...)==0) return false for arg in all({...})do if(value==arg) return true end return false end |
Okay so there are a couple obvious problems:
You're calling setmetatable
on shop
but there's no shop
object in your code. I assume object
should be called shop
or vice versa?
Also your update
, pre_update
and post_update
functions all have their terminating end
commented out.
The problem you're having with __index
is that its purpose is to find a value to return when there isn't one defined on the object itself. In other words, shop:update()
will only trigger a call to __index
if shop.update == nil
. Since shop.update
isn't nil, there's no call to __index
.
What you can do instead is use __newindex
to replace the user defined update
function with the one you want when it's defined.
Here's a sketch of a possible solution:
function aop() return setmetatable( {}, { __newindex=function(self, key, value) -- do a similar thing for draw if key == 'update' then local function _real_update(obj) if obj.pre_update then obj:pre_update() end local ret_val = value(obj) -- value is the user defined update function if obj.post_update then obj:post_update() end return ret_val end rawset(self, key, _real_update) return end -- anything other than update or draw just set it as is. rawset(self, key, value) end } ) end shop = aop() function shop:update() print('update') end function shop:pre_update() print('pre_update') end function shop:post_update() print('post_update') end shop:update() |
The above prints
pre_update update post_update |
as intended.
[Please log in to post a comment]