lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


于 2012-6-20 23:16, Stephen Virgo 写道:
Excellent, this worked and is in fact a much more full solution than I need
In actual fact it didn't quite work when I tried it fully in the wild,
my apologies. I was able to re-arrange your solution to the following
however, which fulfills my needs:-



game_wrapper = {
	this_bool = false,
	that_bool = true,
	some_value = 7
};


-- allow game_wrapper to inherit from game
function game_wrapper:__index(key)
  	local field = rawget(getmetatable(self), key);
	local retval = nil;
	
	if type(field) == "nil" then
		-- key doesn't exist in self, look for it in the prototype object
		local proto = rawget(self, "game");
		field = proto and proto[key];
				
		if type(field) == "function" then
			-- key exists as a function on the prototype object
			retval = function(obj, ...)
				return field(proto, ...);
			end;
		else
			-- return whatever this key refers to on the prototype object
			retval = field;
		end;
	else
		-- key exists in self
		if type(field) == "function" then
			-- key exists as a function on the self object
			retval = function(obj, ...)
				return field(self, ...);
			end;
		else
			-- return whatever this key refers to on the self object
			retval = field;
		end;
	end;
	
	return retval;
end;


function game_wrapper:some_function()
	return self.some_value;
end;


function game_wrapper:quit()
	self:out("A call to game:quit() would usually end the game, but it
won't now that it's overridden!");
end;


function game_wrapper:new(new_game)
	gw = {
		game = new_game
	};
	
	setmetatable(gw, game_wrapper);

	return gw;
end;



my_game = game_wrapper:new(game);

my_game:out("This prints as if I'd used game:out!");
my_game:out("my_game.that_bool is " .. tostring(my_game.that_bool));
my_game:out("my_game:some_function() returns " ..
tostring(my_game:some_function()));
my_game:quit();


--[[ Output would be..

This prints as if I'd used game:out!
my_game.that_bool is true
my_game:some_function() returns 7
A call to game:quit() would usually end the game, but it won't now
that it's overridden!

]]

if you set your overriden field into the actual wrapper object from the new function,
instead of setting it into a `class' object (game_wrapper in your case),
then it won't trigger the __index meta method at all when you call it. it is truelly overriden.

the code should be like this:

------------------------------------- code begin ----------------------------------------
local game_wrapper = {}
function game_wrapper:__index (key)
-- omitted...
-- see previous post
end
function game_wrapper:new ()
local gw = {
game = new_game,
this_bool = true,
that_bool = false,
some_value = 7
}
function gw:some_function ()
return self.some_value
end
function gw:quit ()
self:out ("overriden!")
end
return setmetatable(gw, self)
end
-------------------------------------- code end ------------------------------------------

I guess many new comers to Lua might mix up the meta-table and the __index meta-method.
when a key lookup is missed in a table, it is looked up in the __index meta-method(or table),
not in the meta-table itself.
although it is quite common and convenient to set the __index to the meta-table itself,
it is not necessary, and you should not mingle the two concepts.

in this use case, my sugguested way is to create wrapper object to wrap the real
game object directly. you don't need to create a first level wrapper (I mean, the
game_wrapper, which you might considered as a `class') to wrap the game object,
and then create the second level wrapper to wrap the first level object.

Your style is absolutely OK, I just suggest my personal opinion.

best regards.