User-Defined Types in C作为《Programming in Lua third edition》的第29章,引入了一个新的数据类型userdata,并向我们介绍如何在Lua中实现C语言的数组。除此之外,其余的内容都在总结过往的章节,比如:原表(metatable)、原函数的使用。同样,首先给出文章的结果:
array = require("array") --[[连接一个自己封装的动态库array.dll]]
a = array.new(1000) --[[开辟1000 bits大小的空间]]
a[5] = true --[[第5/3/20bit的位置赋值为true]]
a[3] = true;
a[20] =true;
print(a) --[[输出这个数组对象:array(1000): range[0 - 20] = 00101000000000000001]]
下面介绍array.dll的代码,没有什么特殊之处,对着书本一个个字符的敲。源码下载
#include <Windows.h>
extern "C"{
#include <lua.h>
#include <lauxlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <lualib.h>
#include <math.h>
#include <limits.h>
}
#define BITS_PER_WORD (CHAR_BIT*sizeof(unsigned int))
#define I_WORD(i) ((unsigned int)(i)/BITS_PER_WORD)
#define I_BIT(i) (1 << ((unsigned int)(i) % BITS_PER_WORD))
#define checkarray(L) (NumArray *)luaL_checkudata(L, 1, "LuaBook.array")
int maxIndex = 0;
int tempMaxIndex = -1;
typedef struct NumArray
{
int size;
unsigned int values[1];
}NumArray;
--[[error函数:输出错误信息并退出程序]]
void error(lua_State *L, const char *fmt, ...)
{
va_list argp;
va_start(argp, fmt);
vfprintf(stderr, fmt, argp);
va_end(argp);
lua_close(L);
exit(EXIT_FAILURE);
}
static int newarray(lua_State *L)
{
int i, n;
size_t nbytes;
NumArray *a;
n = luaL_checkint(L, 1);--[[在lua中调用C函数的时候,第一个参数的位置总是1,因为此时每个函数都有一个局部的栈。但是如果在其他C函数中调用此函数,就不成立了。]]
luaL_argcheck(L, n >= 1, 1, "invalid size");
nbytes = sizeof(NumArray) + I_WORD(n - 1) * sizeof(unsigned int);
a = (NumArray *)lua_newuserdata(L, nbytes);
a->size = n;
for (i = 0; i <= I_WORD(n-1); i ++)
a->values[i] = 0;
luaL_getmetatable(L, "LuaBook.array");
lua_setmetatable(L, -2);
return 1;
}
static unsigned int * getindex(lua_State *L, unsigned int *mask)
{
NumArray *a = checkarray(L);
int index = luaL_checkint(L, 2) - 1;
tempMaxIndex = index+1;
luaL_argcheck(L, 0 <= index && index < a->size, 2, "index out of range");
/*return element address */
*mask = I_BIT(index);
return &a->values[I_WORD(index)];
}
static int setarray(lua_State *L)
{
unsigned int mask;
unsigned int *entry = getindex(L, &mask);
luaL_checkany(L, 3); --[[必须要有第三个参数,否则error]]
if(!lua_isboolean(L, 3)) --[[课后习题要求检查是否为boolean类型,如果不是就结束程序。]]
error(L, "A boolean number is required!\n");
if (lua_toboolean(L, 3))
{
*entry |= mask;
if (tempMaxIndex != -1 && tempMaxIndex > maxIndex)
{
maxIndex = tempMaxIndex;
tempMaxIndex = -1;
}
}
else
*entry &= ~mask;
return 0;
}
static int getarray(lua_State *L)
{
unsigned int mask;
unsigned int *entry = getindex(L, &mask);
tempMaxIndex = -1;
lua_pushboolean(L, *entry & mask);
return 1;
}
static int getsize (lua_State *L)
{
NumArray * a = checkarray(L);
luaL_argcheck(L, a != NULL, 1, "'array' expected");
lua_pushinteger(L, a->size);
return 1;
}
int array2string(lua_State *L)
{
NumArray *a = checkarray(L);
char *str = (char *)malloc(maxIndex+1);
for (int i = 0; i < maxIndex; i ++)
{
bool k = (a->values[I_WORD(i)] & I_BIT(i))? 1:0;
str[i] = k + 48;
}
str[maxIndex] = 0;
lua_pushfstring(L, "array(%d): range[0 - %d] = %s", a->size, maxIndex, str);
free(str);
return 1;
}
static const struct luaL_Reg arraylib_f[] =
{
{"new", newarray},
{NULL, NULL}
};
static const struct luaL_Reg arraylib_m[] =
{
{"__tostring", array2string},
{"__newindex", setarray},
{"__index", getarray},
{"__len", getsize},
{NULL, NULL}
};
extern "C" int __declspec(dllexport) luaopen_array(lua_State *L)
{
luaL_newmetatable(L, "LuaBook.array");
luaL_setfuncs(L, arraylib_m, 0);
luaL_newlib(L, arraylib_f);
return 1;
}