lua-users home
lua-l archive

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


John Hind wrote:
I am trying to write an object oriented library in which you can have objects for which the following is valid:

for v in obj do ... end

Using the __call metamethod you can easily get to:

for v in obj() do ... end

But the redundant parenthesis rankles.

In most cases you can simply eliminate the need of the parenthesis by double indexing the container object. I noticed this usefull concept when reengineering the Lua API for our project two years ago. I implemented a set like container called "group" supporting joins, intersections, etc. The for loop is as simple as:

for obj in group do obj:toggle() end

The implemention is very simple and does not require any Lua change at all.

In OO you can rely on objects having a unique id. You just need to store the objects twice in your container - once at their regular index and once at their id used as index. The "group" is in fact Lua table based and stores the objects at standard numerical indices, which makes the concepts implementation very straight forward.

The __call has no need to store the iteration state, as the last object with its unique id fulfills this purpose:

static int iteratorGroup(lua_State *L) {
    // generic for loop iterator function
// var_1 = _f(_s, _var) with _var == nil on first access, var_1 == nil on end
    // on stack: group, _s, _var
    if (!(is_group(L, 1)))
        throwLuaError(L, "Group: iterator first arg not a group");
    lua_getmetatable(L, 1);
    int size = lua_objlen(L, -1);
    if (lua_isnil(L, 3)) {   // first iterator loop access
        if (size == 0) {     // an empty group
            return 1;
        } else {
            lua_rawgeti(L, -1, 1);  // get the first object
            return 1;
    } else {
        lua_pushvalue(L, 3);   // copy last object as key
        lua_rawget(L, -2);     // get last index
        int i = lua_tointeger(L, -1);
        lua_rawgeti(L, -2, ++i);  // get next object
        return 1;

Be aware that the implementation depends on the containers index usage and the object id's nature. But the concept can easily been modified for other index and id types.

This implementation is up and running and is meanwhile being used by multiple ten thousand lines of code.

you are
wrongfully introducing object oriented concepts in a language that is
not OO.

I was about to write this, but you were faster. Thanks,

I agree that in most cases it is preferable not to reveal underlying OO technology in the Lua part to its full extend. A simple object based API that hides complex concepts like inheritance can be more suitable.