"Programming in Lua 3" reading notes (XXV)

Date: 2014.8.11
PartⅣ The C API 
 
29 User-Defined Types in C

     In the previous case, we have been introduced to expand if by writing function with C Lua. In this chapter, will introduce a new type by writing in C to extend Lua, will use the element method and other features to achieve this function.
     An example to introduce this chapter will be introduced, an example of realization of the function is to achieve a simple type: boolean arrays. The main function is to implement this method does not require too complicated algorithm, and therefore can focus on the discussion of the API. Of course, we can be implemented in a table with Lua, but with a C to achieve, where we store each entry in one single bit (refer to the number of bits used to represent a boolean value?)., Than with the table to achieve savings 3% memory overhead.
     The first is the realization of this type need to do some definitions:
#include <limits.h>
#define BITS_PER_WORD (CHAR_BIT*sizeof(unsigned int))
#define I_WORD(i)             ((unsigned int)(i) / BIT_PER_WORD)
#define I_BIT(i)                  (1 << ((unsigned int)(i) % BIT_PER_WORD))
     BITS_PER_WORD represents a number of bits in an unsigned integer. Macro I_WORD calculated numbers given number of bits, is calculated macro I_BIT find a positive number digit mask.
     Type in the following struct on behalf of our definition:
e.g.
typedef struct NumArray
{
     int size;
     unsigned int values[1];
} NumArray;
     Define the size of an array of values ​​for 1, to achieve a placeholder, since the size of the C 89 does not allow the array to 0. When we allocate this array will be reset when its actual size. The following expression is calculated actual size of the n elements of the array:
e.g.
     sizeof(NumArray) + I_WORD( n -1 ) * sizeof(unsigned int)

29.1 UserData
     The first consideration is what is used to represent the Lua NumArray this data structure. Lua provides a basic type: userdata. Userdatum provides a block of memory, did not do any predefined operation, so you can store any thing with this type.
     The function lua_newuserdata given the size of an allocated memory area, the corresponding userdatum pushed onto the stack, and then return the address of the memory block:
 void* lua_newuserdata(lua_State *L,size_t size);
     If needed to allocate memory to other purposes, the use of a given size to create a pointer userdatum, and then stores a pointer to the actual memory block is very easy. In later chapters I will explain this.
     Combined with lua_newuserdata, then create a new boolean arrays will be implemented as follows:
e.g.
static int newarray(lua_State *L)
{
     int i;
     size_t nbytes;
     NumArray *a;

     int n = luaL_checkint(L,1);
     luaL_argcheck(L,n >= 1,1,"invalid siez");
     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;

          return 1;

}
     Once newarray registered to Lua, then you can create a new array through as a = array.new (1000).
     The use arrar.set (a, index, value) to store one entry (one element?). Lua and in line with the rest of the data structure, the new index value from the start arrary 1, the following function sets a given array index value
static int setarray(lua_State *L)
{
     NumArray *a = (NumArray*)lua_touserdata(L,1);
     int index = luaL_checkint(L,2) - 1;

     luaL_argcheck(L,a != NULL,1,"'array' expected");
     luaL_argcheck(L,0 <= index && index < a->size,2,"index out of range");
     luaL_checkany(L,3);

     if(lua_toboolean(L,3))
          a->values[I_WORD(index)] != I_BIT(index);
     else
          a->values[I_WORD(index)] &= ~I_BIT(index);

     return 0;
}
     To function to a number of bit operation, I feel good hard look. Since a boolean variable in Lua accepts any type of value, and therefore is used here to detect luaL_checkany three parameters: to ensure that each parameter has a corresponding value (the function to three parameters, then the need to have three parameters). If the condition is not satisfied, then the error will be:
e.g.
array.set(0,11,0)
     --stdin:1: bad argument #1 to 'set' ('array' expected) 这里的意思是第一个参数必须是数组'array'类型的
array.set(a,1)
     --stdin:1: bad argument #3 to 'set' (value expected) 这里函数只传递两个参数,因此报错的是少了第三个参数,第三个参数必须有值。
     The following function values ​​obtained from the data:
static int getarray(lua_State *L)
{
     NumArray *a = (NumArray*)lua_touserdata(L,1);
     int index = luaL_checkint(L,2) - 1;

     luaL_argcheck(L, a != NULL,1,"'array' expected");
     luaL_argcheck(L,0 <= index && index < a->size,2,"index out range");

     lua_pushboolean(L,a->value[I_WORD(index)] & I_BIT(index));

     return 1;
}
     The following function is used to get the size of the array:
static int getsize (lua_State *L)
{
     NumArray *a = (NumArray*)lua_touserdata(L,1);
     luaL_argcheck(L, a != NULL,1"'array' expected");
     lua_pushinteger(L,a->size);
     return 1;
}
     After processing the above operation is initializing the library, and then added to Lua to:
static const struct luaL_Reg arraylib [] =
{
     {"new",newarray},
     {"set",setarray},
     {"get",getarray},
     {"size",getsize},
     {NULL,NULL}
};


int luaopen_array(lua_State *L)
{
     luaL_newlib(L,arraylib);
     return 1;
}
     As can be seen from the above operation, so Lua support type defined in C, is the use of the characteristics of custom libraries. Library written in C, and then register to Lua, and then use it in Lua.
     After opening a custom library, you can use our new type of writing the following ways:
a = array.new(1000)
print(a)               --> user data
print(array.size(a))     --> 1000
for i = 1,1000 do
     array.set(a,i,i % 5 == 0)     --设定
end
print(array.get(a,10))          --true     --得到


 
29.2 Metatables
     The partial implementation of the functions of a security risk. If the user array.set (io.stdin, 1, false) is set to a value. At this time, a pointer userdatum stream (FILE *), because it is a type userdatum, array.set accept this value. In this case the problem can cause memory conflicts (and in fact error meg get is telling you index overflow). This is not accepted by the Lua libraries.
     In general, to create a unique metatable our newly defined type as a unique identifier used to distinguish it from other zones to the userdata is a good method. Every time we create a userdata, will be used with the corresponding metatable to mark the userdata; and every time we get a userdata, whether it would be correct detection metatable. Because Lua code can not be modified userdatum of metatabel, so do not worry this will affect our code.
     The next step to note is that, how to store what we need to metatable here. Two reference data stored in the last chapter mode: registry and upvalue. Usually in Lua, to arbitrarily defined C Type registration to the registry will be used as a type name index, then metatable as a value, and we also need to take into account the issue of naming conflicts, so here use "LuaBook.array" as the name.
     Then it is to use an auxiliary library functions to achieve the functionality we need here:
int luaL_newtatable(lua_State *L,const char *tname);
void luaL_getmetatable(lua_State *L,const char *tname);
void *luaL_checkudata(lua_State *L,int index,const char *tname);
     The first function will create a new table (used as the metatable), the created table in the stack, and then stores the name given to rigistry; the second function, according to the given rigistry get the name of a metatable; third function, metatable detect objects of a given index position with metatable called tname are equal, if not identical, it will lead to error, they would return to the same position userdata.
     Modify the function to open the library, Add to create a new metatable features:
int luaopen_array(lua_State *L)

{
     luaL_newmetatable(L,"LuaBook.array");          /* 这里加入了创建metatable 的功能 */
     luaL_newlib(L,arraylib);
     return 1;
}
     Then modify the function to create the array, each array is set metatable created:
static int newarray(lua_State *L)
{
     //new
     luaL_getmetatable(L,"LuaBook.array");
     lua_setmetatable(L,-2);

     return 1;
}
     Release lua_setmetatable a function table from the stack, and then set it to a given index metatable object.
     Then use setarray, getarray, getsize when you need to do testing of the first parameter.
     After that, if the first parameter error, such as: array.get (io.stdin, 10), the compiler will throw an error:
error: bad argument #1 to 'get' ('array' expected)


 
29.3 Object-Oriented Access
     This part is to be realized, converting our new implementation of this type of an object, so that we can manipulate an object oriented syntax (object-oriented syntax), such as:
a = array.new(1000)
print(a:size())          --使用了冒号操作符
a:set(10,true)
…
     Here a: critical size () corresponds a.size (a), this function is achieved with the use of __index element method. In the table, if the given key value is not found, then lua will call this element method. For userdata, because it is simply not key, so each will call this element method.
     Example:
local metaarray = getmetatable(array.new(1))
metaarray.__index = metarray
metaarray.set = array.set
metaarray.get = array.get
metaarray.size = array.size
     The main function of the first line of code is: create a new array, and then get it metatable, assigned to metaarray (although not set in lua metatable to userdata, but you can get metatable). The code is provided behind the associated element of metatable method. When we calculate the size of the call a.size, Lua can not find a target from the "size" of this key, it will look for the value from the field __index, at a time corresponding __index is metaarry itself, and we set metaarray.size = array.size, so a.size (a) return result is array.size (a), we've done.
     We can also be used to implement the above-described characteristic C, and C, can do better: because the array is an object, the object has its own good operation inside the package, so we will not necessary as these getsize and other operations to put in the list to be registered. Only need to create a new object functions can be put to the list. All other operating functions have become the method of the object.
     getsize, getarray, setarray other methods before implemented in the realization of the need to do additional changes need to change how we register these functions. Therefore, we need to change the way we open the library, we first need two separate lists: the first to use a normal function while the second element method to be used.
static const struct luaL_Reg arraylib_f [] =
{
     {"new",newarray},
     {NULL,NULL}
};

static const struct luaL_Reg arraylib_m [] =
{
     {"set",setarray},
     {"get",getarray},
     {"size",getsize},
     {NULL,NULL}
};
     Accordingly, the function luaopen_array open the library at this time we need to create metatable, then assign itself as __index, registered the rest of the operating functions, and finally create the array table:
int luaopen_array(lua_State *L)
{
     luaL_newmetatable(L,"LuaBook.array");

     lua_pushvalue(L,-1);
     lua_setfield(L,-2,"__index");

     luaL_setfuncs(L,arraylib_m,0);
     luaL_newlib(L,arraylib_f);
     return 1;
}
     As used herein, luaL_setfuncs will arraylib_m in the list of functions to metatable configuration, and then use luaL_newlib create a new table, and then register the function arraylib_f.

 
29.4 Array Access
An array of object-oriented syntax, may also be implemented using an array of generally syntax. As compared to the use of a: get (i), we can also be implemented with a [i]. We can achieve our needs by defining element method:
e.g.
local metaarray = getmetatable(array.new(1))
metaarray.__index = array.get
metaarray.__newindex = array.set
metaarray.__len = array.size
So that we can use an array syntax to achieve the functionality we need:
a = array.new(1000)
a[10] = true          --     'setarray'
print(a[10])           --     'getarray'
print(#a)               --     'getsize'
Similarly, we also need to register these element method in C, but also by modifying the initialization function.

 
29.5 Light Userdata
     Before we use to Userdata called full userdata, in addition, there is another type of userdata, called light userdata.
     A light userdatum C is representative of a pointer value (of a void * value). light userdata is a value, not an object; therefore can not be created. Use function lua_pushlightuserdata to be a light userdatum advance to the stack:
     lua_pushlightuserdata void (lua_State L *, void * p);
     Despite all called userdata, but full userdata and light userdata are two different concepts. light userdata is not the buffers, but only a pointer to it. light user data is not metatable, but with the same number, light uesrdata garbage collector is not being managed.
     Sometimes, light userdata is a lightweight alternative to the work performed by the full userdata. But this is not the absolute. First, light userdata no metatable, so there is no way to know their type; secondly, full userdata is also not take up a lot of overhead.
     Real use light userdata is used for comparison. Because full userdata are objects will equal only in comparison with his own. Representative light userdata and C is a pointer, it will be equal to the same userdata independently represents a pointer. So we can use light userdata to find C objects in Lua.

Reproduced in: https: //www.cnblogs.com/zhong-dev/p/4044558.html

Guess you like

Origin blog.csdn.net/weixin_34396902/article/details/94600992