为 Lua 包装 C 库:如何创建嵌套的函数表?
Posted
技术标签:
【中文标题】为 Lua 包装 C 库:如何创建嵌套的函数表?【英文标题】:Wrapping a C library for Lua: how do I create nested tables of functions? 【发布时间】:2012-03-20 14:37:15 【问题描述】:这个问题相关的代码在这里:https://github.com/jchester/lua-polarssl/tree/master/src
目前我正在尝试包装 PolarSSL 库 (http://polarssl.org) 的一部分,以便让我访问 SHA-512 HMAC(luacrypto 不提供此功能)。
我想要的 API 是这样的:
a_sha512_hash = polarssl.hash.sha512('text')
或更完整
local polarssl = require 'polarssl'
local hash = polarssl.hash
a_sha512_hash = hash.sha512('test')
如果您在上面的链接中引用 polarssl.c,您会看到我编写了包装 PolarSSL 代码的函数。然后我正在尝试构建函数表:
LUA_API int luaopen_polarssl( lua_State *L )
static const struct luaL_Reg core[] =
NULL, NULL
;
static const struct luaL_Reg hash_functions[] =
"sha512", hash_sha512 ,
"sha384", hash_sha384 ,
NULL, NULL
;
static const struct luaL_Reg hmac_functions[] =
"sha512", hmac_sha512 ,
"sha384", hmac_sha384 ,
NULL, NULL
;
luaL_register( L, CORE_MOD_NAME, core );
luaL_register( L, HASH_MOD_NAME, hash_functions );
luaL_register( L, HMAC_MOD_NAME, hmac_functions );
return 1;
其中 CORE_MOD_NAME = 'polarssl',HASH_MOD_NAME = 'polarssl.hash',HMAC_MOD_NAME = 'polarssl.hmac'。
当我在这个问题的顶部运行类似于 Lua 代码的测试脚本时,我得到了:
lua: test.lua:23: attempt to index global 'polarssl' (a nil value)
stack traceback:
test.lua:23: in main chunk
[C]: ?
我已经尝试寻找如何实现此 module.submodule 方法的示例(例如 naim 与 luasockets),但每个人似乎都有不同的实现方式。我完全迷路了。
【问题讨论】:
我无法链接到 naim 和 luasockets,因为我达到了 看起来因果报应就像美味的棒棒糖一样,所以更新了带有链接的帖子。 【参考方案1】:每个人似乎都有不同的实现方式。
那是 Lua;每个人都以自己的方式做事。这是 Lua 最大的优势和最大的弱点:语言提供机制,而不是策略。
您需要做的第一件事是停止使用luaL_register
。是的,我知道这很方便。但是你想要一些特别的东西,luaL_register
不会帮你得到它。
您想要创建一个包含一个或多个函数的表。所以……就那样做吧。
创建一个表。
lua_newtable(L);
这很容易。该函数将一个表压入堆栈,因此我们的堆栈现在在其顶部有一个表。这是我们将返回的表格。
现在,我们需要创建一个新表进入旧表。
lua_newtable(L);
再次,简单。接下来,我们要将要进入该表的函数放入堆栈中。
lua_pushcfunction(L, hash_sha512);
所以堆栈包含三样东西:目标表、“散列”表(我们稍后会“命名”它)以及我们想要放入“散列”表中的函数。
所以把函数放入哈希表中。
lua_setfield(L, -2, "sha512");
这将获取堆栈顶部的任何内容并将其设置到堆栈上 -2 索引处的表上名为“sha512”的字段中。这就是我们的“哈希”表所在的位置。此函数完成后,它会从堆栈中删除顶部项目。这会将“哈希”表留在顶部。
我们可以对第二个函数重复这个过程:
lua_pushcfunction(L, hash_sha384);
lua_setfield(L, -2, "sha384");
现在,我们要将“哈希”表放入我们要返回的表中。这很容易通过另一个lua_setfield
电话完成:
lua_setfield(L, -2, "hash");
记住:这个函数接受 whatever 在栈顶。此时,我们要返回的表(将是我们模块的表)在堆栈上。
我们可以对“hmac”表重复这个过程:
lua_newtable(L); //Create "hmac" table
lua_pushcfunction(L, hmac_sha512);
lua_setfield(L, -2, "sha512");
lua_pushcfunction(L, hmac_sha384);
lua_setfield(L, -2, "sha384");
lua_setfield(L, -2, "hmac"); //Put the "hmac" table into our module table
模块的表现在有两个条目:“hash”和“hmac”。两者都是包含两个函数的表。
我们可以用这个把它放到全局表中:
lua_pushvalue(L, -1);
lua_setfield(L, LUA_GLOBALSINDEX, "polarssl");
并非每个模块制造商都想这样做。有些人喜欢强迫人们使用local polarssl = require "polarssl"
语法,以避免污染全局命名空间。这取决于你。
但无论哪种方式,您都必须返回这张表。从 luaopen
函数返回 1,让 Lua 知道有一个返回值。上面的lua_pushvalue
调用仅用于复制表(请记住:引用了表,因此就像复制指针一样)。这样,当您使用lua_setfield
时,副本会从堆栈中移除,而原始副本仍将用作返回值。
【讨论】:
谢谢。一开始我有点困惑,但在纸上画出堆栈的运动有助于我理解这个过程。 +1 用于讨论require
的返回值……很多人似乎并不真正意识到推荐的做法已经从“坚持你的模块”转变为在全局中”改为“从 require 中返回你的模块,调用者应该把它放在一个局部变量中”【参考方案2】:
与问题没有直接关系,但以下说法并不完全正确:
目前我正在尝试包装 PolarSSL 库 (http://polarssl.org) 的一部分,以便让我访问 SHA-512 HMAC(luacrypto 不提供此功能)。
我不知道您指的是哪个版本的 LuaCrypto,但this LuaCrypto fork 确实提供了 SHA-512 HMAC,以及 OpenSSL 自动支持的任何其他摘要类型。只需将 "sha512"
作为摘要类型传递:
hmac.digest("sha512", message, key)
文档只说明了部分支持的摘要类型,完整列表可以通过调用crypto.list("digests")
来检索。
table.foreach(crypto.list("digests"), print)
我想,即使是原始的 LuaCrypto 也应该支持 SHA-512。
【讨论】:
谢谢,我不知道。但是,我正在使用不包含 SHA512 的“主”叉。我选择封装 PolarSSL 是因为它很小且 API 非常简单——每个模块都是独立的,可以独立编译。例如,我只编译了 SHA512/384 模块。总大小:22kb。 这确实是一个非常合理的尺寸!如果您想嵌入具有加密功能的 Lua,看起来很棒。以上是关于为 Lua 包装 C 库:如何创建嵌套的函数表?的主要内容,如果未能解决你的问题,请参考以下文章