Обертка библиотеки C для Lua: как создать вложенные таблицы функций?

Код, связанный с этим вопросом, находится здесь: 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), но, кажется, у всех есть иной способ ее достижения. Я полностью потерян.


person Jacques Chester    schedule 02.03.2012    source источник
comment
Я не могу ссылаться на naim и luasockets, потому что я достиг предела в 10 баллов для ссылок.   -  person Jacques Chester    schedule 02.03.2012
comment
Похоже, карму раздают, как вкусные леденцы, поэтому обновил пост со ссылками.   -  person Jacques Chester    schedule 02.03.2012


Ответы (2)


кажется, у всех разные способы достижения этого.

Это Луа; каждый делает по своему. Это самая большая сила и самая большая слабость Lua: язык предоставляет механизмы, а не политики.

Первое, что вам нужно сделать, это перестать использовать luaL_register. Да, я знаю, что это удобно. Но вы хотите чего-то особенного, и luaL_register не поможет вам это получить.

Вам нужно создать таблицу, содержащую таблицу, содержащую одну или несколько функций. Итак... сделай это.

Создайте таблицу.

lua_newtable(L);

Это было просто. Функция помещает таблицу в стек, так что теперь в нашем стеке есть таблица поверх него. Это таблица, которую мы вернем.

Теперь нам нужно создать новую таблицу, чтобы войти в старую.

lua_newtable(L);

Опять же, легко. Затем мы хотим поместить функцию, которую мы хотим ввести, в эту таблицу в стеке.

lua_pushcfunction(L, hash_sha512);

Итак, в стеке есть три вещи: целевая таблица, «хеш-таблица» (мы перейдем к «именованию» ее через секунду) и функция, которую мы хотим поместить в «хеш-таблицу».

Итак, поместите функцию в хеш-таблицу.

lua_setfield(L, -2, "sha512");

Это берет все, что находится на вершине стека, и устанавливает его в поле с именем «sha512» в таблице с индексом -2 в стеке. Вот где наша «хеш-таблица». После завершения этой функции она удаляет верхний элемент из стека. Это оставляет «хеш-таблицу» наверху.

Мы можем повторить процесс для второй функции:

lua_pushcfunction(L, hash_sha384);
lua_setfield(L, -2, "sha384");

Теперь мы хотим поместить хеш-таблицу в таблицу, которую хотим вернуть. Это достаточно легко сделать с помощью другого вызова lua_setfield:

lua_setfield(L, -2, "hash");

Помните: эта функция берет все, что находится на вершине стека. На данный момент таблица, которую мы хотим вернуть (которая будет таблицей нашего модуля), находится в стеке.

Мы можем повторить этот процесс для таблицы «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", чтобы не загрязнять глобальное пространство имен. Тебе решать.

Но что вы должны сделать в любом случае, так это вернуть эту таблицу. Верните 1 из вашей функции luaopen, чтобы Lua знал, что есть одно возвращаемое значение. Вышеприведенный вызов lua_pushvalue существует с единственной целью копирования таблицы (помните: на таблицы ссылаются, так что это похоже на копирование указателя). Таким образом, когда вы используете lua_setfield, копия удаляется из стека, а оригинал остается для использования в качестве возвращаемого значения.

person Nicol Bolas    schedule 02.03.2012
comment
Спасибо. Сначала я был немного озадачен, но наброски движений стека на бумаге помогли мне понять процесс. - person Jacques Chester; 02.03.2012
comment
+1 за то, что приложил некоторые усилия, чтобы обсудить возвращаемое значение из require ... многие люди, похоже, действительно не знают, что рекомендуемая практика изменилась с простого вставления вашего модуля в глобальный, чтобы вернуть ваш модуль из запроса, и вызывающий должен поместить это в локальную переменную - person snogglethorpe; 03.03.2012

Не имеет прямого отношения к вопросу, но следующее утверждение не совсем верно:

В настоящее время я пытаюсь обернуть одну часть библиотеки PolarSSL (http://polarssl.org), чтобы получить доступ к SHA-512 HMAC (luacrypto не предоставляет этого).

Я не знаю, какую версию LuaCrypto вы имеете в виду, но эта вилка LuaCrypto предоставляет HMAC SHA-512. , а также любой другой тип дайджеста, автоматически поддерживаемый OpenSSL. Просто передайте "sha512" в качестве типа дайджеста:

hmac.digest("sha512", message, key)

В документации указана только часть поддерживаемых типов дайджестов, полный список можно получить, вызвав crypto.list("digests").

table.foreach(crypto.list("digests"), print)

Если подумать, даже оригинальный LuaCrypto должен поддерживать SHA-512.

person Michal Kottman    schedule 02.03.2012
comment
Спасибо, я не знал об этом. Однако я запускал основной форк, который не поддерживает SHA512. Я решил обернуть PolarSSL, потому что он небольшой, а API очень простой — каждый модуль является автономным и может быть независимо скомпилирован. Например, я скомпилировал только модуль SHA512/384. Общий размер: 22кб. - person Jacques Chester; 03.03.2012
comment
Это действительно очень разумный размер! Выглядит отлично, если вы хотите внедрить Lua с криптографическими возможностями. - person Michal Kottman; 03.03.2012