lua-users home
lua-l archive

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



On 23-Sep-04, at 11:30 AM, David Given wrote:

However, this approach to implementing objects would make it rather hard to implement subclassing properly. You'd end up having to have seperate closures for each level in the inheritence chain and some foul hackery to make them
all share the same dispatch table.

Surely it is just a question of setting the __index of the dispatch table?
Although subclassing is not always necessary for useful objects.

Certainly possible, but I'm not sure if
it's worth the effort... what sort of speed improvement are we looking at
between table lookups and locals?

I don't believe it is so much that as implementing objects in C (personally)

Consider the following (as yet untested):
(Note that it is not necessary to test for object type, and that the object
can reference Lua objects directly, so garbage collection works.)

/* Functional object support library */

static int l_methodical (lua_State *L) {
  luaL_reg *entry;
  lua_pushvalue(L, 1); /* get the method name */
  lua_gettable(L, lua_upvalueindex(1));  /* look it up */
  if (lua_isnil(L, -1)) {
    return luaL_error(L, "Unknown method %s", lua_tostring(L, 1));
  }
luaL_reg *entry = lua_touserdata(L, -1); /* get the actual function */
  return entry->func(L);
}

/* Create a constructor function */

int register (lua_State *L, lua_CFunction ctr, luaL_reg *methods) {
  /* make a table of luaL_reg pointers */
  /* Portability: void* cannot be a function pointer */
  lua_newtable(L);
  for (; method->name; ++method) {
    lua_pushstring(L, method->name);
    lua_pushlightuserdata(L, method);
    lua_settable(L, -3);
  }
  /* bind the method table to the constructor */
  lua_pushcclosure(L, ctr, 1);
  return 1;
}

/* Constructor function should call this to create the closure */

int construct (lua_State *L, int nvar) {
  lua_pushvalue(L, lua_upvalueindex(L, 1));  /* method table */
  lua_insert(L, 1);  /* get it into the right place */
  lua_pushcclosure(L, l_methodical, nvar + 1);
  return 1;
}

/* Just for convenience */

#define FIELD(f) lua_upvalueindex((f) + 1)

/*********** end of support library ***********/

/* Ok, kind of dumb example, proof of concept */

enum NamedPointVals {
  NAME = 1,
  X,
  Y,
};

static int l_namedpointctr (lua_State *L) {
  luaL_checkstring(L, 1);
  luaL_checknumber(L, 2);
  luaL_checknumber(L, 3);
  lua_settop(L, 3);
  return construct(L, 3);
}

/* Accessors */

static int l_name (lua_State *L) {
  lua_pushvalue(L, FIELD(NAME);
  return 1;
}

static int l_x (lua_State *L) {
  lua_pushvalue(L, FIELD(X));
  return 1;
}

static int l_y (lua_State *L) {
  lua_pushvalue(L, FIELD(Y));
  return 1;
}

/* pretty printer */

static int l_tostring (lua_State *L) {
  lua_pushfstring("%s<%f, %f>", lua_tostring(L, FIELD(NAME)),
                                lua_tonumber(L, FIELD(X)),
                                lua_tonumber(L, FIELD(Y)));
  return 1;
}

/* Mutator */

static int l_shift (lua_State *L) {
  double dx = luaL_checknumber(L, 2);
  double dy = luaL_checknumber(L, 3);
  lua_pushnumber(L, lua_tonumber(L, FIELD(X)) + dx);
  lua_replace(L, FIELD(X));
  lua_pushnumber(L, lua_tonumber(L, FIELD(Y)) + dy);
  lua_replace(L, FIELD(Y));
  return 0;
}

/* Method table */

static const luaL_reg namedpointmethods[] = {
  {"name", l_name},
  {"x", l_x},
  {"y", l_y},
  {"tostring", l_tostring},
  {"shift", l_shift},
  {NULL, NULL}
};

/* make the constructor */
int luaopen_namedpoint (lua_State *L) {
  register(L, l_namedpointctr, namedpointmethods);
  lua_setglobal(L, "NamedPoint");
  return 0;
}