Lua 学习 chapter30 编写c函数的技巧

4.5k 词

目录

  1. 数组操作
  2. 字符串操作
  3. 在c函数中保存状态

生活总需要一点仪式感,然后慢慢的像那个趋向完美的自己靠近。

数组操作

Lua中的数组就是以特殊的方式使用边。像lua_setttable and lua_gettable这种用来操作的通用函数,也可以用于操作数组,不过C API为使用整数索引的表访问和更新提供了专门的函数:

1
2
void lua_geti (lua_State *L, int index, int key);
void lua_seti (lua_State *L, int index, int key);

lua_geti相当于:

1
2
lua_pushnumber(L, key);
lua_gettable(L,t);

lua_seti相当于:

1
2
3
lua_pushnumber(L, key);
lua_insert(L, -2);
lua_settable(L,t);

eg:函数map,该函数对数组中的所有元素调用一个指定的函数,然后用此函数返回的结果替换掉对应的数组元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int l_map(lua_State* L) {
	int i, n;
	luaL_checktype(L, 1, LUA_TTABLE); //确保指定的参数具有指定的类型
	luaL_checktype(L, 2, LUA_TFUNCTION);
	n = luaL_len(L, 1); //类似长度运算符
	for (int i = 0; i < n; i++)
	{
		lua_pushvalue(L, 2);
		lua_geti(L, 1, i);
		lua_call(L, 1, 1); //类似以pcall但是会传播错误,而不是返回错误码
		lua_seti(L, 1, i);
	}
	return 0;
}

字符串操作

当c函数接收到一个lua字符串为参数是,必须遵守两条规则,在使用字符串期间不能从栈中将其弹出,而且不应该修改字符串。

标准API为两种最用的字符串操作提供了支持,即字符串提取和字符串连接。要提取子串,那么基本的操作lua_pushlstring可以获取字符串长度作为额外的参数。因此,如果要把字符串s从i到j(include)的子串传递给lua:

1
lua_pushlstring(L, s + i, j - i +1);

eg:函数根据分隔符来分隔字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static int l_split(lua_State* L) {
	const char* s = luaL_checkstring(L, 1);
	const char* sep = luaL_checkstring(L, 2);
	const char* e;
	int i = 1;
	lua_newtable(L);
<span class="k">while</span> <span class="p">((</span><span class="n">e</span> <span class="o">=</span> <span class="n">strchr</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="o">*</span><span class="n">sep</span><span class="p">))</span> <span class="err">!</span><span class="o">=</span> <span class="n">NULL</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">lua_pushlstring</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">e</span> <span class="o">-</span> <span class="n">s</span><span class="p">);</span>
	<span class="n">lua_rawseti</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="n">i</span><span class="o">++</span><span class="p">);</span>
	<span class="n">s</span> <span class="o">=</span> <span class="n">e</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">lua_pushstring</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="n">s</span><span class="p">);</span>
<span class="n">lua_rawseti</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="n">i</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">1</span><span class="p">;</span>

}

在c函数中保存状态

C API提供了两个类似的地方来存储非局部数据,即注册表(registry)和上值(upvalue).

注册表

注册表是一张只能被C代码访问的全局表。通常情况下,我们使用注册表来存储多个模块间的共享数据。

注册表总是位于伪索引LUA_REGISTRYINDEX中。伪索引就像是一个栈中的索引,但是它所关联的值不在栈中。lua API中大多数接受索引作为参数的函数也能将伪索引作为参数,像lua_remove和lua_insert这种操作栈本身的函数除外。eg:获取注册表中键为“key”的值,可以使用如下的调用。

1
2
3
4
5
6
7
8
lua_getfield(L, LUA_REGISTRYINDEX, "Key");

static char key = 'k'
lua_pushstring(L, mystr);
lua_rawsetp(L,LUA_REGISTRYINDEX,(void *) &key);//设置值到注册表中

lua_rawgetp(L,LUA_REGISTRYINDEX,(void *) &key);//从注册表中取值
mystr = lua_tostring(L,-1);

            <hr style="visibility: hidden;"/>
            
            <hr style="visibility: hidden;"/>