为Lua包装C库:如何创建嵌套的函数表?

Jac*_*ter 10 c lua nested-table polarssl

与此问题相关的代码如下: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')
Run Code Online (Sandbox Code Playgroud)

或更全面

local polarssl = require 'polarssl'
local hash = polarssl.hash

a_sha512_hash = hash.sha512('test')
Run Code Online (Sandbox Code Playgroud)

如果你在上面的链接中引用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;
}
Run Code Online (Sandbox Code Playgroud)

其中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]: ?
Run Code Online (Sandbox Code Playgroud)

我试图寻找如何实现这个module.submodule方法的例子(如奈姆 VS luasockets),但每个人都似乎有实现它的方式不同.我完全迷失了.

Nic*_*las 15

每个人似乎都有不同的实现方式.

那是Lua; 每个人都以自己的方式行事.这是Lua最大的优势和最大的弱点:语言提供机制,而不是政策.

你需要做的第一件事是停止使用luaL_register.是的,我知道这很方便.但是你想要一些特别的东西,而luaL_register不是帮助你获得它.

您想要的是创建一个包含一个包含一个或多个函数的表的表.所以......那样做.

创建一个表.

lua_newtable(L);
Run Code Online (Sandbox Code Playgroud)

那很简单.该函数将表推送到堆栈,因此我们的堆栈现在有一个表.这是我们将返回的表格.

现在,我们需要创建一个新表来进入旧表.

lua_newtable(L);
Run Code Online (Sandbox Code Playgroud)

再次,简单.接下来,我们希望将我们想要的函数放入堆栈中的该表中.

lua_pushcfunction(L, hash_sha512);
Run Code Online (Sandbox Code Playgroud)

所以堆栈有三个东西:目标表,"哈希"表(我们将在一秒钟内"命名"它),以及我们想要放入"哈希"表的函数.

所以把函数放到哈希表中.

lua_setfield(L, -2, "sha512");
Run Code Online (Sandbox Code Playgroud)

它接受堆栈顶部的任何内容,并将其设置到堆栈上-2索引处的表上名为"sha512"的字段中.这就是我们的"哈希"表所在的位置.此函数完成后,它将从堆栈中删除顶部项.这使得"哈希"表位于顶部.

我们可以重复第二个功能的过程:

lua_pushcfunction(L, hash_sha384);
lua_setfield(L, -2, "sha384");
Run Code Online (Sandbox Code Playgroud)

现在,我们想将"哈希"表放入我们想要返回的表中.通过另一个lua_setfield电话,这很容易做到:

lua_setfield(L, -2, "hash");
Run Code Online (Sandbox Code Playgroud)

请记住:此函数采用堆栈顶部的任何内容.此时,我们要返回的表(将是我们模块的表)在堆栈上.

我们可以为"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
Run Code Online (Sandbox Code Playgroud)

模块的表现在有两个条目:"hash"和"hmac".两者都是包含两个函数的表.

我们可以将它粘贴到全局表中:

lua_pushvalue(L, -1);
lua_setfield(L, LUA_GLOBALSINDEX, "polarssl");
Run Code Online (Sandbox Code Playgroud)

并非每个模块制造商都希望这样做.有些人更喜欢强迫人们使用local polarssl = require "polarssl"语法,以避免污染全局命名空间.由你决定.

但你必须做的任何一种方法是返回这个表.从luaopen函数返回1 ,让Lua知道有一个返回值.lua_pushvalue上面的调用只是为了复制表而存在(记住:表被引用,所以就像复制指针一样).这样,当您使用时lua_setfield,副本将从堆栈中删除,而原始文件仍将用作返回值.

  • 为了讨论来自`require'的返回值而犯了一些麻烦......很多人似乎并没有真正意识到推荐的做法已经从"只是将你的模块放在全球范围内"转变为"返回你的来自require的模块,调用者应将其放在局部变量中" (3认同)