为 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 库:如何创建嵌套的函数表?的主要内容,如果未能解决你的问题,请参考以下文章

类对象作为成员

Lua C API 嵌套表段。过错

子查询(嵌套子查询)

如何在Java中使用Lua脚本语言

如何使用 cffi-lua 向/从 C 函数传递 Lua 表

如何从 C 结构创建 Lua 表