"Programming in Lua 3" reading notes (24)

Date: 2014.8.8
PartⅣ at The C API

28 Techniques for Writing C Functions
official API and auxiliary library provides several mechanisms for helping to create a C function. In this chapter, we will introduce an array manipulation, string manipulation, and storage Lua variables in C.


28.1 Array Manipulation
     array in Lua, is the table in the special use of the title, we can use the table management methods to manage the array, named:. Lua_settable, lua_gettable time, API also offers an array of special functions. One reason for these additional functions are performance considerations: operating an internal algorithm usually has a loop through the array, so any effort to enhance this operation will have a very big influence on increasing the overall performance of the algorithm. Another convenience is the original consideration: Compared with the string key, numeric key is sufficient to meet the daily needs.
     API provides two additional functions:
     void lua_rawgeti (L * a lua_State, int index, int Key);
     void lua_rawseti (L * a lua_State, int index, int Key);
     these two functions substantially as meaning: index represented in table stack position; key represents the position of elements in the table. Call lua_rawgeti (L, t, key) corresponds to the following (when t is a positive value) in this operation:
     lua_pushnumber (L, Key);
     lua_rawget (L, t);
     invoke lua_rawseti (L, t, key) ( t still a positive value) is equivalent to the following operations:
     lua_pushnumber (L, Key);
     lua_insert (L, -2);
     lua_rawset (L, T);
     two functions use the raw, rapid. When the table is used as an array element method is rarely used.
     


String Manipulatio 28.2 n-
     when the C function receives a parameter from the string in Lua, you can just follow two rules: Do not Release the parameter obtained from the stack when the time parameter; Do not modify this parameter.
     And when you need to create number string in C back to Lua, you need a lot more attention. Note that the C code needed cache configuration and release buffer overflow. In this case, Lua's API provides a number of functions to help deal with these.
     The standard library provides the basic operation of the two strings: substring extraction composition and strings. Remove a substring, only needs to remember lua_pushlstring string length as an additional third parameter. So if you want to transfer the positions i to s j of the substring to lua, is the need to write:
     lua_pushlstring (L, + s i, j-i +. 1);
     and Lua are also provided in the API used for special combined function strings, called lua_concat. The equivalent lua .. operator: and then converting the number to a string when necessary trigger element method. Not only that, but also more than two strings at once combined. Call lua_concat (L, n) will be a combination of (and Release) data for n in the top of the stack, and push the result stack the value.
     And the other function is lua_pushfstring:
     const char * lua_pushfstring (a lua_State L *, const char * FMT, ...);
     This function is similar to the C sprintf function that creates a new string according to a specific format and some additional parameters. Of course, unlike the sprintf function, this function is not necessary to provide a buffer of, Lua will dynamically create a string, resized as necessary. The combined result string function to push the stack, and then returns a pointer pointing to the result. No need to worry about overflow the buff. To Lua5.2, the function takes the following format string:
% S is inserted into one end of the string to 0
% d an integer insert
% f Lua insertion of a number, i.e. Double
% inserting a pointer P
% c is inserted into a string an integer
%% to insert the character%
     when we just need to deal with a small number of combinations of strings, and use lua_concat lua_pushfstring is very effective. But when we need to deal with multiple strings, but also does not seem efficient, this time by means of an auxiliary library to use buff characteristics can significantly improve efficiency.



28.3 Storing State in C Functions
      usually, C function needs to store some non-local data that these data outlive their invocation (calling period longer than its lifetime?). In C, to achieve this demand typically use global or static variables. But when you use the standard library functions, the use of global and static variables are less desirable. First of all, you can not use variable C to store a generic Lua variables; second, the use of these variables in a variety of libraries will be invalid when Lua state.
     Lua function for storing two places nonlocal data: non-local variables and global variables. C API also provides a place to store two non-local data: registry and upvalues.
     registry is a global table, the C code can be accessed. In particular, this table may be used to store the data to a plurality of shared modules, but if you want only data for a single module or a function, you need to use upvalues.

Registry of The
     Registry always located at pseudo-index, which is defined by the value of LUA_REGISTRYINDEX. pseudo-index stack, and index similar, but not with the corresponding value in the stack. Lua API indices accept as an argument also accept pseudo-indices, such as lua_remove, lua_insert function. For example to obtain a value for the key "Key" from the registry, it may operate as follows:
e.g.
lua_getfield(L,LUA_REGISTRYINDEX,"Key");
     registry也就是Lua中的一个table,可以使用Lua中除nil外的所有类型变量来做它的index值。然而,因为C模块中所有的函数公用同一个registry,所以为了避免冲突,需要认真考虑选择合适的作为key的变量类型。字符串类型是作者推荐的。
     而使用数值作为key是不可取的,因为这些key是保留给reference system的。这个系统包含了一组辅助库中的一些函数,这些函数用来允许你存储变量到table中而不需要关心如何创建独特的名字。函数luaL_ref 创建一个新的reference:
e.g.
int r = luaL_ref(L,LUA_REGISTERINDEX);
     调用这个函数将会从栈中推出一个值,然后以一个数值key存储至registry中。称这个key为reference。
     我们使用references主要是当我们需要在一个C的数据结构中存储一个Lua变量的reference。之前我们已经说过,我们不应该在C函数(包含指向Lua字符串的指针)之外存储这些指针。不仅如此,Lua也不提供指向其余对象的指针,如table和函数。因此,我们不能通过指针指向Lua的对象,而只能通过创建reference指向这些然后存储在C中。
     将指向某个值的reference r推进至栈中,执行如下操作:
e.g.
lua_rawgeti(L,LUA_REGISTRYINDEX,r);
     最后,要释放值和reference,需要调用函数luaL_unref:
e.g.
lua_unref(L,LUA_REGISTRYINDEX,r);
     执行上述调用之后,重新调用luaL_ref 可能会再次返回这个reference。
     reference system将nil视为一个特殊情况,无论何时我们以luaL_ref调用一个nil的值,将不会创建一个新的reference,而是返回常量LUA_REFNIL,而对这个操作执行释放动作是无效的:
luaL_unref(L,,LUA_REGISTRYINDEX,LUA_REFNIL);
     而以下操作:
lua_rawgeti(L,LUA_REGISTRYINDEX,LUA_REFNIL)将会push一个nil至栈中。
     也定义了另一个常量LUA_NOREF,这是一个与任何有用的reference不同的数值,用来标记一个reference为不可用。

     而另一个创建key的方式是使用静态变量的地址:C链接器将会保证这些key是独一无二的。这里就需要使用lua_pushlightuserdata来实现这个操作了,使用一个C指针来push一个值至Lua的栈中。下面的例子展现了用这个方法如何向registry存储和使用一个字符串:
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 为了简化使用变量的地址作为一个唯一key,提供了两个新的函数:lua_rawgeti和lua_reaseti ,只是使用C指针作为key,而不是整数。使用这两个函数可以重写之前的代码:
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
     与registry存储方式不同,upvalue实现的是类似于C中的静态变量,只在特定函数中可见。每次在Lua中创建一个新的C函数,都可以与之关联任意数量的upvalues;每个upvalue可以存储一个Lua的变量值。之后,当这个函数被调用了,可以使用 pseudo-indices 自由访问这些upvalues。
     称这种带upvalues的C函数为closure,类似于Lua中的closure。但我们可以用同一套C函数代码,不同的upvalues,来创建不同的closure。
     以一个例子做解释:用C创建一个newCounter函数,这个函数是一个工厂,每次访问这个函数都返回一个新的counter函数。尽管是使用同一套代码,但是每次都是一个独立的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;
}
     这里的关键函数是lua_pushcclosure ,这个函数用来创建一个新的closure。函数的第二个参数是base function(什么意思?),第三个参数是upvalues的数量。而在创建一个新的closure之前,需要将初始化的upvalues 推进至栈中。在这里的例子中,初始化了一个0 作为closure的upvalues。
     在看一下counter函数的定义:
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 */
}
     在这里关键的函数是lua_upvalueindex ,这个函数产生一个upvalue 的 pseudo-index。特别的,表达式lua_upvalueindex(1) 产生正在运行的函数的第一个upvalue的pseudo-index。这个index和栈中的index类似,只是这些index不是在栈中而已。因此调用lua_tointeger 将会得到第一个upvalue且将其转化为一个number。函数counter 推进一个新的值:++val,得到之前那个number的复本。


Shared upvalues
     通常我们需要在一个库中给所有的函数共享一些变量。之前我们已经可以通过registry来实现,现在我们也可以通过upvalues来实现。
     不像Lua的closure,C的closure不能共享upvalues的。每个closure都有自己私有的upvalues。但是可以通过设置不同的函数来使得upvalues代表同样的一个table,这样这个table就成为了库中所有函数都能共享数据的机制。
     Lua5.2提供了一个函数用来实现上面的这个机制,我们已经使用luaL_newlib 来打开了C的库,而Lua用下面的这个宏来实现刚提到的这个函数:
e.g.
#define luaL_newlib(L,l) \
               (luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
     这个宏就是为库创建了一个新的table,而函数luaL_setfuncs 则将列表l中的函数加入到新的table中。
     而luaL_setfuncs 的第三个参数则代表了库中新函数有多少个upvalues,这些初始化upvalues的值也必须在栈中,即lua_pushcclosure中出现的那些。因此,创建一个库中所有函数共享一个table作为其单一的upvalue的库,可以使用以下代码:
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);
     最后的这个函数调用:luaL_setfuncs(L,lib,1) 也会将这个共享的table从栈中移除掉,而只将该table应用于新的库中。

转载于:https://www.cnblogs.com/zhong-dev/p/4044560.html

Guess you like

Origin blog.csdn.net/weixin_34356310/article/details/94600988