Programming in Lua 3 Reading Notes (25)

Source: Internet
Author: User
Date: 2014.8.11part IV the c api 29 user-defined types in C
In the previous example, we have introduced how to extend Lua by writing functions in C. In this chapter, we will introduce how to use C to write new types to extend Lua, and use features such as meta methods to implement this function. This chapter describes how to use an example. The example implements a simple type: Boolean arrays. This function is implemented mainly because this method does not require too complex algorithms, so you can focus on the discussion of APIs. Of course, we can use a table in Lua, but we can use a C to implement it, where we store each entry in one single bit (refers to using a single digit to represent a Boolean value ?)., It saves 3% of memory overhead than table. To implement this type, you must first define the following:
#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 indicates the number of bits in the unsigned integer. Macro I _word calculates the number of digits in a given number, and macro I _bit calculates the mask for finding a positive digit. The following struct represents the type we define:
e.g.typedef struct NumArray{     int size;     unsigned int values[1];} NumArray;
Define the size of the array values as 1 to implement a placeholder, because C 89 does not allow the array size to 0. When we allocate this array, We will reset its actual size. The following expression calculates the actual size of N element arrays:
e.g.     sizeof(NumArray) + I_WORD( n -1 ) * sizeof(unsigned int)

29.1 userdataThe first thing to consider is what is used in Lua to represent the numarray data structure. Lua provides a basic type: userdata. A userdatum provides a memory area without any predefined operations. Therefore, you can use this type to store everything. The lua_newuserdata function allocates a memory area based on the given size, pushes the corresponding userdatum into the stack, and then returns the address of the memory block:
 void* lua_newuserdata(lua_State *L,size_t size);
If you need to allocate memory for other purposes, it is very easy to use a pointer of the given size to create a userdatum and then store it on the actual memory block with a pointer. This will be introduced in later chapters. When lua_newuserdata is used in combination, creating 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 is registered in Lua, you can use a = array. New (1000) to create a new array. Arrar. Set (A, index, value) is used to store an entry (an element ?). In addition, it is consistent with the other data structures in Lua. The new arrary index value starts from 1. The following function sets the value of a given index in an array.
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;}
Some bitwise operations are required in the function, and it seems delicious. Because the Boolean variable in Lua accepts any type of value, here we use lual_checkany to detect three parameters: ensure that each parameter has a corresponding value (the function requires three parameters, three parameters are required ). If the conditions are not met, an error is returned:
E. g. array. set (0, 11, 0) -- stdin: 1: bad argument #1 to 'set' ('array' expected) the first parameter must be an array of the 'array' type. set (A, 1) -- stdin: 1: bad argument #3 to 'set' (value expected) The function only passes two parameters, so the error is that the third parameter is missing, the third parameter must have a value.
The following function obtains the value 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 obtain the array size:
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 preceding operations, initialize the database and add it to Lua:
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;}
From the above operation, we can see that the user-defined library feature is used to make Lua support for C-defined types. Write the library in C, register it in Lua, and use it in Lua. After opening the custom library, you can use our new type in the following ways:
A = array. new (1000) print (a) --> User dataprint (array. size (a) --> 1000for I = Do array. set (A, I, I % 5 = 0) -- set endprint (array. get (A, 10) -- true -- get


  29.2 million itemsThe functions implemented above have a security risk. If you use array. Set (Io. stdin, 1, false) to set a value. At this time, the userdatum Pointer Points to a stream (File *), because its type is a userdatum, array. Set will accept this value. This will cause memory conflicts (the error Meg tells you that the index overflows ). This is not accepted by the Lua database. Generally, it is good to create a unique retriable for the newly defined type as a unique identifier to distinguish it from other userdata. Each time we create a userdata, the corresponding retriable will be used to mark this userdata. Each time we get a userdata, we will check whether it is a correct retriable. Because the Lua Code cannot modify the upload Abel of userdatum, you don't have to worry about this affecting our code. In the next step, you should note how to store the retriable that we will use here. In the previous chapter, we mentioned two Data Storage Methods: Registry and upvalue. Generally, in Lua, to register any defined C type to the registry, the type name will be used as the index, and then the retriable will be used as the value. At the same time, we also need to consider the naming conflict issue, therefore, use "luabook. array "as the name. Then we use the functions in the auxiliary library to implement the functions 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 a retriable), place the created table on the top of the stack, and store it in rigistry with the given name. The second function, get a retriable according to the given name in rigistry. The third function checks whether the retriable of the object at the given index is equal to the retriable named tname. If they are different, an error is thrown, if they are the same, the location of userdata will be returned. Modify the function for opening the database and add the function for creating a new retriable:
Int luaopen_array (lua_state * l) {lual_newretriable (L, "luabook. array ");/* added the ability to create retriable */lual_newlib (L, arraylib); return 1 ;}
Modify the function used to create an array and set the retriable for the array created each time:
static int newarray(lua_State *L){     //new     luaL_getmetatable(L,"LuaBook.array");     lua_setmetatable(L,-2);     return 1;}
The lua_setretriable function releases a table from the stack and sets it to the retriable of the given index object. Then, when using setarray, getarray, and getsize, we need to check the first parameter. If the first parameter is incorrect, for example, array. get (Io. stdin, 10), then the compiler will throw an error: Error: bad argument #1 to 'get' ('array' expected)

  29.3 object-oriented accessThis part will be implemented by converting the newly implemented type into an object so that we can operate it using the object-oriented Syntax, for example:
A = array. New (1000) print (A: size () -- use the colon operator A: Set (10, true )...
Here a: size () is equivalent to a. Size (a). The key to implementing this function is to use the _ index meta method. In the table, if the value of the given key is not found, Lua will call this metadata method. For userdata, this metadata method is called every time because there is no key at all. Example:
local metaarray = getmetatable(array.new(1))metaarray.__index = metarraymetaarray.set = array.setmetaarray.get = array.getmetaarray.size = array.size
The function of the first line of code is to create a new array, obtain its retriable, and assign it to metaarray (although userdata cannot be set to retriable in Lua, it can be achieved ). The following code sets the metadata method of retriable. When we call. when the size is calculated, Lua cannot find the key "size" from object A, and it will search for this value from field _ index, in this case, the _ index corresponds to metadry itself, and we set metaarray. size = array. size, so. size (a) returns an array. size (a), as we wish. We can also use C to implement the above features, and we can do better in C: because at this time, array is an object, and the object has its own encapsulated operations, therefore, we do not need to put these operations such as getsize into the list to be registered. You only need to put the function for creating the new object to the list. All other operation functions become object methods. The previously implemented methods such as getsize, getarray, and setarray do not need to be changed in implementation. what needs to be changed is how we register these functions. Therefore, we need to modify the method for opening the database. First, we need two separate lists: the first for common functions and the second for the meta method.
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}};
Correspondingly, the function luaopen_array for opening the database needs to create a retriable, assign it to _ index, register other operation functions, and finally create an 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;}
Use lual_setfuncs to configure the functions in the arraylib_m list to the retriable, create a new table using lual_newlib, and register the functions in arraylib_f.
  29.4 array accessThe object-oriented syntax can also be implemented in arrays. For example, we can use a [I] instead of a: Get (I. We can implement our needs by defining some metadata methods:
e.g.local metaarray = getmetatable(array.new(1))metaarray.__index = array.getmetaarray.__newindex = array.setmetaarray.__len = array.size
In this way, we can use the array syntax to implement the functions we need:
a = array.new(1000)a[10] = true          --     'setarray'print(a[10])           --     'getarray'print(#a)               --     'getsize'
Similarly, we also need to register these methods in C by modifying the initialization function.
  29.5 light userdataThe userdata we used previously is called full userdata. In addition, there is another type of userdata, called light userdata. A light userdatum is a value that represents the C pointer (a void * value ). Light userdata is a value rather than an object. Therefore, it cannot be created. Use the lua_pushlightuserdata function to push a light userdatum to the stack: void lua_pushlightuserdata (lua_state * l, void * P); although it is called userdata, however, light userdata and full userdata are different concepts. Light userdata is not a buffers, but a pointer. Light user data is not retriable, and like number, light uesrdata is not managed by garbage collector. Sometimes light userdata performs lightweight replacement of full userdata. However, this is not absolute. First, light userdata has no retriable, so there is no way to know their types. Second, full userdata is not very popular. Actually, light userdata is used for comparison. Because full userdata is an object, it is equal only to itself. Light userdata represents a C pointer, so it is equal to any userdata that represents the same pointer. Therefore, we can use light userdata to find the C object in Lua.

Programming in Lua 3 Reading Notes (25)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.