yanbin's Blog
Lua 调用的 C 函数保存 state 的两种方式: Storing State in C Functions 笔记
/* variable with a unique address */ static char key = 'k'; /* store a string */ lua_pushlightuserdata(L, (void *)key); /* push address */ lua_pushstring(L, myStr); /* push value */ lua_settable(L, LUA_REGISTRYINDEX); /* registry[&kye] = myStr */ /* retrieve a string */ lua_pushlightuserdata(L, (void *)key); /* push address */ lua_gettable(L, LUA_REGISTRYINDEX); /* retrieve value */ const char *myStr = lua_tostring(L, -1); /* convert to C string */
/* variable with a unique address */ 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); const char *myStr = lua_tostring(L, -1);
以上这些创建 key 以访问 table 规则对普通 table 也适用。
static int counter(lua_State *L); int newCounter(lua_State *L) { lua_pushinteger(L, 0); /* 使用 counter 作为 base function 创建 C closure, 讲 stack TOP N 作为 upvalues */ lua_pushcclosure(L, &counter, 1); return 1; }
static int counter(lua_State *L) { int val = lua_tointeger(L, lua_upvalueindex(1)); lua_pushinteger(L, ++val); lua_pushvalue(L, -1); /* duplicate new vlaue */ lua_replace(L, lua_upvalueindex(1)); return 1; }
#define luaL_newlib(L, lib) \ (luaL_newlibtable(L, lib), luaL_setfuncs(L, lib, 0)) 关键点是 luaL_setfuncs() 这个函数讲 statck top N 个 value 作为 upvalues 指定给 library 中的函数。 luaL_newlib() 定义中这个 N 是 0, 这个函数是用不成了。不过可以用如下方式: /* create library table */ luaL_newlibtable(L, lib); /* create shared value */ lua_newtable(L); /* add functions in list 'lib' to the new library, * sharing previous table as upvalue */ luaL_setfuncs(L, lib, 1);
参考:
Programming in Lua third edition 28.3
为什么使用 do {} while(0)
有些宏定义含有多行代码,一般使用 do {} while(0) 把代码放在 'do' 和 'while' 之间的 '{}' 中。
#define foorbar(msg, callback) do {\ struct Task __task = msg_to_task((msg)); \ if (__task != NULL) { \ process(__task, (callback)); \ char *__msg = task_to_msg(__task); \ if (__msg != NULL) \ { send_msg(__msg); \ free(__msg); \ } \ destroy_task(__task); \ } while (0)
这样用的原因是:
1.符合 C/C++ 语法习惯。
每条语句后面都一个';', 而 do {} while 语句后面也要加一个 ';'.
2.避免出现语法错误。
不用 do {} while(0) 把多行代码包起来,在 if else 语句中就会有语法错误,例如:
#define foorbar(a, b) foor((a)); bar((b)) if (something) /* 以下有语法错误 */ foorbar(3, 2); else // do something
仅仅使用 '{}' 把多行代码包起来,如果在调用宏之后加 ';', 也会有语法错误。
#define foorbar(a, b) {\ foor((a)); bar((b));\ } foorbar(3, 2); // 此处有语法错误 /* 编译器提示: * error: expected ‘;’ before ‘}’ token * 如果不加 ';', 不会有语法错误但是这样不符合 C/C++ 的语法习惯 */
3.do {} while(0) 可以根据条件跳出执行。
#define foorbar() do {\ struct condition __cond; \ if (__cond.wait_cond()) \ break; // 条件发生退出执行 \ // 条件没有发生 do something } while(0)
4.私以为 do {} while(0) 可以保证代码执行并且只执行一次。
5.需要注意的地方。
(a)宏定义时用 '\' 连接多行语句;
(b)宏定义中定义变量,注意与外部变量名字冲突,不然原本希望用外面的变量,却用了新定义的变量。
(c)有些编译器会优化掉 do {} while(0); 直接展开 '{}' 内的代码, 如(b)所描述,此时会出现语法错误。
FIXME: 如果内部有 'break' 并且 'break' 的执行依赖运行时条件,编译器就不会优化掉 do {} while(0); 了。
举例:
#define foorbar() do {\ struct condition cond; \ if (cond.wait_cond()) \ break; // 条件发生退出执行 \ // 条件没有发生 do something } while(0) struct condition cond; // do something foobar(); // 到底用的是哪一个 cond?
#define foorbar(a, b) do {\ const char *something = get_something(a, b); \ } while(0) const char *something; // do something foorbar(3, 9); // 如果编译器优化掉了 do {} while(0); 这里有语法错误。
感谢 老猫,mike2,MovableType@源赖朝 三位网友。
参考:
do { … } while (0) — what is it good for?
do{}while(0) 的作用