lua-users home
lua-l archive

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


On Wed, Aug 30, 2006 at 11:56:42AM -0700, Sam Roberts wrote:
> I was following through PILv1 online, but it abruptly ended before
> discussing how to have both methods like a:size() AND array
> indexors like a[2] = 5 or a.some_field = "value" for user data.

I rewrote in C, and added both implementations to

  http://lua-users.org/wiki/ObjectProperties

where it might be useful to others trying to have what this page calls
"properties" for userdata.

Usage is as:

  o = array:new(3)

  print(o:size())

  o[1] = 1
  o[2] = 2
  o[3] = 3

  print(o:get(2))
  o:set(3, -1)
  print(o[3])

Cheers,
Sam

/*
Rewrite in C of array example from http://www.lua.org/pil/28.4.html that
implements both array and OO access.

Lacks bounds checking, its not pertinent to this example.
*/
#include "lauxlib.h"
#include "lua.h"

#include <assert.h>
#include <stdint.h>
#include <string.h>

#define ARRAY_REGID  "22d3fa81-aef3-4335-be43-6ff037daf78e"
#define ARRAY_CLASS  "array"

struct array {
  lua_Integer size;
  lua_Number data[1];
};

typedef struct array* array;

static array array_check(lua_State* L, int index) 
{
  void* userdata = luaL_checkudata(L,index,ARRAY_REGID);
  assert(userdata);
  return  userdata;
}

int array_new(lua_State* L) 
{
  // Ignoring [1], the "array" global table.
  int size = luaL_checkinteger(L, 2);
  array self = (array) lua_newuserdata(L,sizeof(*self) + (size-1) * sizeof(self->data));

  self->size = size;

  for(size = 0; size < self->size; size++)
    self->data[size] = 0;

  luaL_getmetatable(L, ARRAY_REGID);
  lua_setmetatable(L, -2);

  return 1;
}

int array_size(lua_State* L) 
{
  array self = array_check(L, 1);

  lua_pushinteger(L, self->size);

  return 1;
}

int array_get(lua_State* L) 
{
  array self = array_check(L, 1);
  lua_Integer i = luaL_checkinteger(L, 2);

  // TODO bounds checking on i

  lua_pushnumber(L, self->data[i-1]);

  return 1;
}

int array_set(lua_State* L) 
{
  array self = array_check(L, 1);
  lua_Integer i = luaL_checkinteger(L, 2);
  lua_Number v = luaL_checknumber(L, 3);

  // TODO bounds checking on i

  self->data[i-1] = v;

  return 0;
}

int array_index(lua_State* L) 
{
  const char* key = luaL_checkstring(L, 2);

  lua_getmetatable(L, 1);
  lua_getfield(L, -1, key);

     // Either key is name of a method in the metatable
  if(!lua_isnil(L, -1))
    return 1;

  // ... or its a field access, so recall as self.get(self, value).
  lua_settop(L, 2);

  return array_get(L);
}

static const struct luaL_reg array_class_methods[] = {
  { "new",            array_new },
  { NULL, NULL }
};

static const struct luaL_reg array_instance_methods[] = {
  { "get",             array_get },
  { "set",             array_set },
  { "size",            array_size },
  { "__index",         array_index },
  { "__newindex",      array_set },
  { NULL, NULL }
};

int array_open(lua_State* L) 
{
  luaL_newmetatable(L, ARRAY_REGID);

  luaL_openlib(L, NULL, array_instance_methods, 0);

  luaL_openlib(L, ARRAY_CLASS, array_class_methods, 0);

  return 1;
}