Programming in Lua 3 Reading Notes (24)

Source: Internet
Author: User
Date: 2014.8.8
Part IV the C API

28 techniques for writing c Functions
The official APIs and auxiliary libraries provide several mechanisms to help create C functions. In this chapter, we will introduce array control, String Control, and the storage of Lua variables in C.


28.1 array manipulation
The array in Lua is the name of a table in special use cases. We can use the table management method to manage the array. Its name is lua_settable and lua_gettable. at that time, APIS also provided some special functions for arrays. One reason for providing these extra functions is performance considerations: An algorithm usually has an operation to traverse the array, therefore, any effort to improve this operation will have a great impact on the performance improvement of the entire algorithm. Another original consideration is convenience: Compared with string-type keys, numeric keys are sufficient to meet daily needs.
The API provides two additional functions:
Void lua_rawgeti (lua_state * l, int index, int key );
Void lua_rawseti (lua_state * l, int index, int key );
These two functions mean: Index indicates the position of the table in the stack; key indicates the position of the element in the table. Call lua_rawgeti (L, t, key) to perform the following operations (when T is positive:
Lua_pushnumber (L, key );
Lua_rawget (L, t );
Calling lua_rawseti (L, t, key) (still T is a positive value) is equivalent to the following operation:
Lua_pushnumber (L, key );
Lua_insert (L,-2 );
Lua_rawset (L, t );
Both functions use raw operations, which is fast. Table is rarely used as an array.



28.2 string manipulatioN
When C functions receive a string parameter from Lua, they only need to follow two rules: do not introduce this parameter from the stack when obtaining this parameter; do not modify this parameter.
When you need to create a character string in C and return it to Lua, you need to pay more attention. C code needs to pay attention to cache configuration and release, cache overflow, and so on. In this case, the Lua API provides some functions to help with this.
The standard library provides two basic operations for strings: the removal of substrings and the combination of strings. To retrieve a substring, you only need to remember that the length of the lua_pushlstring must be the third parameter. Therefore, if you want to transmit the substring from s position I to J to Lua, you need to write the following:
Lua_pushlstring (L, S + I, j-I + 1 );
Lua also provides a special function in the API for string combination, called lua_concat. Equivalent to the... operator in Lua: converts a number to a string and triggers the metadata method as needed. In addition, you can combine more than two strings at a time. Calling lua_concat (L, n) will combine (and launch) the data at the top of N stacks, and then push the result to the top of the stack.
Another function is lua_pushfstring:
Const char * lua_pushfstring (lua_state * l, const char * FMT ,...);
This function is similar to the sprintf function in C. This function creates a new string based on the specific format and some additional parameters. Of course, unlike the sprintf function, this function does not need to provide a buffer. Lua dynamically creates a string and its size is adjusted as needed. The function pushes the combined result string to the stack and returns a pointer to the result. You do not need to worry about Buff overflow. To lua5.2, this function accepts the following format strings:
% S inserts a string ending with 0
% D insert an integer
% F insert a Lua number, that is, double
% P inserts a pointer
% C insert an integer as a string
% Insert characters %
When we only need to process a small number of string combinations, It is very effective to use lua_concat and lua_pushfstring. However, when we need to process multiple strings, it seems inefficient. At this time, using the auxiliary library to use the buff feature can significantly improve efficiency.



28.3 storing state in c Functions
Generally, the C function needs to store some non-local data, that is, the outlive their invocation (the lifetime is longer than the call period ?). In C, global or static variables are usually used to achieve this requirement. However, when you use standard library functions, using global and static variables is not desirable. First, you cannot use C variables to store a generic Lua variable. Second, libraries that use these variables will be invalid when there are multiple Lua states.
Lua functions store non-local data in two places: global variables and non-local variables. The c api also provides two areas for storing non-local data: Registry and upvalues.
Registry is a global table that can only be accessed by C code. Specifically, you can use this table to store data shared to multiple modules. If you want to provide data for only one module or a single function, you need to use upvalues.

The Registry
Registry is always located in the pseudo-index, and its value is defined by lua_registryindex. The pseudo-index is similar to the index in the stack, but the value corresponding to it is not in the stack. The functions that use indices as parameters in Lua API also accept pseudo-indices, such as lua_remove and lua_insert functions. For example, to obtain a value with the key as "key" from the Registry, you can perform the following operations:
e.g.lua_getfield(L,LUA_REGISTRYINDEX,"Key");
Registry is a table in Lua. You can use all types of variables except nil in Lua to create its index value. However, all functions in the C Module share the same registry, so to avoid conflicts, You need to carefully select the appropriate variable type as the key. The string type is recommended by the author.
However, using numeric values as keys is not advisable because these keys are reserved for reference system. This system contains a group of functions in the auxiliary library that allow you to store variables in the table without worrying about how to create unique names. The lual_ref function creates a new reference:
e.g.int r = luaL_ref(L,LUA_REGISTERINDEX);
Calling this function will introduce a value from the stack and store it in the registry as a value Key. This key is called reference.
We use references mainly when we need to store the reference of a Lua variable in a C data structure. As we have said before, we should not store these pointers outside of the C function (including pointers to the Lua string. In addition, Lua does not provide pointers to other objects, such as tables and functions. Therefore, we cannot direct the pointer to the Lua object, but can only point to these objects by creating a reference and then store them in C.
Push the reference R pointing to a value to the stack and perform the following operations:
e.g.lua_rawgeti(L,LUA_REGISTRYINDEX,r);
Finally, to release the value and reference, you need to call the lual_unref function:
e.g.lua_unref(L,LUA_REGISTRYINDEX,r);
After the preceding call is executed, you may return this reference when you call lual_ref again.
Reference system treats nil as a special case. Whenever we call a nil value with lual_ref, a new reference will not be created, but a constant lua_refnil will be returned, the release action for this operation is invalid:
Lual_unref (L, lua_registryindex, lua_refnil );
The following operations:
Lua_rawgeti (L, lua_registryindex, lua_refnil) will push an nil to the stack.
It also defines another constant lua_noref, which is a value different from any useful reference and used to mark a reference as unavailable.

The other way to create keys is to use static variable addresses: The C Linker will ensure that these keys are unique. Here we need to use lua_pushlightuserdata to implement this operation, and use a C pointer to push a value to the Lua stack. The following example shows how to store and use a string to the registry using this method:
e.g./* variable with a unique address*/static char Key = "k";/* store a string*/lua_pushlightuserdata(L,(void*)&Key);lua_pushstring(L,myStr);lua_settable(L,LUA_REGISTRYINDEX);/* retrieve a string*/lua_pushlightuserdata(L,(void*)&Key);lua_gettable(L,LUA_REGISTRYINDEX);myStr = lua_tostring(L,-1);

Lua5.2 provides two new functions to simplify the use of the variable address as a unique key: lua_rawgeti and lua_reaseti, but uses the C pointer as the key rather than the integer. You can use these two functions to rewrite the previous Code:
e.g.static char Key = "k";/* store a string*/lua_pushstring(L,myStr);lua_rawsetp(L,LUA_REGISTRYINDEX,(void*)&Key);/* retrieve a string*/lua_rawgetp(L,LUA_REGISTRYINDEX,(void*)&Key);myStr = lua_tostring(L,-1);


Upvalues
Unlike registry storage, upvalue implements static variables similar to those in C and is only visible in specific functions. Each time a new C function is created in Lua, it can be associated with any number of upvalues. Each upvalue can store a Lua variable value. Then, when this function is called, you can use pseudo-indices to freely access these upvalues.
The C function with upvalues is called closure, similar to closure in Lua. However, we can use the same set of C function code and different upvalues to create different closure.
Use an example to explain: use C to create a newcounter function, which is a factory. Every time you access this function, a new counter function is returned. Although the same set of code is used, each time it is an independent counter:
e.g.static int counter(lua_State *L);     /* forward declaration*/int newCounter(lua_State *L){     lua_pushinteger(L,0);     lua_pushcclosure(L,&counter,1);     return 1;}
The key function here is lua_pushcclosure, which is used to create a new closure. The second parameter of the function is base function ?), The third parameter is the number of upvalues. Before creating a new closure, you must push the initialized upvalues to the stack. In this example, a 0 value is initialized as the upvalues of closure.
Let's take a look at the counter Function Definition:
e.g.static int counter(lua_State *L){     int val = lua_tointger(L,lua_upvalueindex(1));     lua_pushinteger(L,++val);     /* new value */     lua_pushvalue(L,-1);     /* duplicate it */     lua_replace(L,lua_upvalueindex(1));     /* update upvalue */     return 1; /* return new value */}
The key function here is lua_upvalueindex, which generates a upvalue's pseudo-index. In particular, the expression lua_upvalueindex (1) generates the pseudo-index of the first upvalue of the running function. This index is similar to the index in the stack, but these indexes are not in the stack. Therefore, calling lua_tointeger will obtain the first upvalue and convert it into a number. The counter function pushes forward a new value: ++ Val to obtain the duplicate of the previous number.


Shared upvalues
Usually we need to share some variables with all functions in a library. Previously, we can implement it through registry, and now we can also implement it through upvalues.
Unlike Lua's closure, C's closure cannot share upvalues. Each closure has its own private upvalues. However, you can set different functions to make upvalues represent the same table. In this way, this table becomes a mechanism for all functions in the database to share data.
Lua5.2 provides a function to implement the above mechanism. We have used lual_newlib to open the C library, and Lua uses the following macro to implement the function just mentioned:
e.g.#define luaL_newlib(L,l)                (luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
This macro creates a new table for the database, And the lual_setfuncs function adds the function in list l to the new table.
The third parameter of lual_setfuncs represents the number of upvalues in the new functions in the database. These initialized upvalues values must also be in the stack, that is, those in lua_pushcclosure. Therefore, to create a database where all functions share a table as its single upvalue database, you can use the following code:
e.g./* create library table ('lib' is its list of function) */luaL_newlibtable(L,lib);/* create shared upvalue */lua_newtable(L);/* add functions in list 'lib' to the new library,sharing previous table as upvalue*/luaL_setfuncs(L,lib,1);
The last function call: lual_setfuncs (L, Lib, 1) also removes the shared table from the stack and only applies the table to the new database.

Programming in Lua 3 Reading Notes (24)

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.