Как сохранить тип значения в пользовательских данных?

Эта SO статья та же самая, но ответ бесполезен, потому что ответ был на Lua, а вопрос был о C-API. Поэтому я снова спрашиваю. Надеюсь, другие извлекут пользу из этого вопроса.

На самом деле у меня есть 2 проблемы (я не могу заставить работать работу z и z, и я не могу заставить работать helloworld())

Я пытаюсь добраться до этого:

local x = MyCBoundLib.GetSomething()
print(x.y)
print(x.z)

куда x это пользовательские данные Я продолжаю получать attempt to index a userdata value

Я знаю, что "пользовательские данные не индексируются без метатаблицы, потому что это данные C/C++"

В моем C-коде я делаю что-то подобное, чтобы попытаться обернуть объект.

int push_Something(lua_State *L, void *object)
{
    struct SomethingWrapper *w = (struct SomethingWrapper *)lua_newuserdata(L, sizeof(struct SomethingWrapper));
    w->object = object;

    luaL_setmetatable(L, "Something");
    return 1;
}

Ранее я пытался зарегистрировать метатабель под названием Something, вот так:

luaL_newmetatable(L, "Something");
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
luaL_setfuncs(L, some_funcs, 0);
lua_pop(L, 1);

куда some_funcs имеет:

static luaL_Reg const some_funcs [] =
{
    { "helloworld",     l_helloworld },
    { NULL, NULL }
};

Если я попробую print(x.helloworld())Я получаю ту же ошибку: attempt to index a userdata value

Ни в одном из моих кодов я не знаю, как правильно прикрепить тип значения y или же z,

1 ответ

Решение

Во-первых, для helloworld ваш код работает:

/* file: hw.c
 * on Debian/Ubuntu compile with:
 *  `gcc -I/usr/include/lua5.2 -fpic -shared -o hw.so hw.c`
 */
#include <lua.h>
#include <lauxlib.h>

struct SomethingWrapper {
  void *object;
};

static int l_helloworld(lua_State *L) {
  lua_pushliteral(L, "Hello World!");
  return 1;
}

static luaL_Reg const some_funcs[] = {
  { "helloworld", l_helloworld },
  { NULL, NULL }
};

int push_Something(lua_State *L, void *object) {
  struct SomethingWrapper *w = lua_newuserdata(L, sizeof(*w));
  w->object = object;
  luaL_setmetatable(L, "Something");
  return 1;
}

int luaopen_hw(lua_State *L) {
  luaL_newmetatable(L, "Something");
  lua_pushvalue(L, -1);
  lua_setfield(L, -2, "__index");
  luaL_setfuncs(L, some_funcs, 0);
  lua_pop(L, 1);

  push_Something(L, NULL);
  return 1;
}

и тестовый скрипт:

-- file: hwtest.lua
local x = require( "hw" )
print( x.helloworld() )

Выход:

Привет, мир!

Для доступа к свойствам пользовательских данных необходимо установить __index к функции вместо таблицы. Функция вызывается с двумя аргументами (пользовательские данные и ключ) всякий раз, когда вы пытаетесь получить доступ к полю пользовательских данных, и вы можете запросить свой объект C и выдать желаемый результат.

Это немного усложняется, если вы собираетесь поддерживать методы и свойства одновременно, но основной подход заключается в следующем: вы используете функцию как __index Метаметод. Эта функция имеет доступ к таблице методов (например, через повышение значения или реестр и т. Д.) И пытается найти данный ключ в этой таблице. Если вам это удастся, вы вернете это значение. Если вы ничего не придумали, вы сравниваете данный ключ с действительными именами свойств вашего объекта C и возвращаете соответствующие значения, если вы получаете совпадение. (Если вы не получите совпадение, вы можете вернуть nil или поднять ошибку - решать вам.)

Вот многократная реализация этого подхода (извлечена из набора инструментов Луны):

static int moon_dispatch( lua_State* L ) {
  lua_CFunction pindex;
  /* try method table first */
  lua_pushvalue( L, 2 ); /* duplicate key */
  lua_rawget( L, lua_upvalueindex( 1 ) );
  if( !lua_isnil( L, -1 ) )
    return 1;
  lua_pop( L, 1 );
  pindex = lua_tocfunction( L, lua_upvalueindex( 2 ) );
  return pindex( L );
}

MOON_API void moon_propindex( lua_State* L, luaL_Reg const methods[],
                              lua_CFunction pindex, int nups ) {
  if( methods != NULL ) {
    luaL_checkstack( L, nups+2, "not enough stack space available" );
    lua_newtable( L );
    for( ; methods->func; ++methods ) {
      int i = 0;
      for( i = 0; i < nups; ++i )
        lua_pushvalue( L, -nups-1 );
      lua_pushcclosure( L, methods->func, nups );
      lua_setfield( L, -2, methods->name );
    }
    if( pindex ) {
      lua_pushcfunction( L, pindex );
      if( nups > 0 ) {
        lua_insert( L, -nups-2 );
        lua_insert( L, -nups-2 );
      }
      lua_pushcclosure( L, moon_dispatch, 2+nups );
    } else if( nups > 0 ) {
      lua_replace( L, -nups-1 );
      lua_pop( L, nups-1 );
    }
  } else if( pindex ) {
    lua_pushcclosure( L, pindex, nups );
  } else {
    lua_pop( L, nups );
    lua_pushnil( L );
  }
}

Использование:

/*  [ -nup, +1, e ]  */
void moon_propindex( lua_State* L,
                     luaL_Reg const* methods,
                     lua_CFunction index,
                     int nup );

Эта функция используется для создания метаполя __index. Если index равен NULL, а методов нет, создается таблица, содержащая все функции в методах, и помещается в верхнюю часть стека. Если index не NULL, а методы есть, указатель на функцию index просто помещается в верхнюю часть стека. В случае, если оба значения не равны NULL, создается новое замыкание C и помещается в стек, который сначала пытается найти ключ в таблице методов, а в случае неудачи вызывает исходную функцию индекса. Если оба имеют значение NULL, nil помещается в стек. Если nup не равен нулю, заданное число повышений выталкивается из вершины стека и становится доступным для всех зарегистрированных функций. (Если index и методы не равны NULL, функция index получает два дополнительных значения в индексах 1 и 2.) Эта функция используется в реализации moon_defobject, но, возможно, она будет полезна вам независимо.

Если вы попытаетесь сохранить произвольные значения Lua в пользовательских данных (как предполагает заголовок) - вы не сможете. Но вы можете связать дополнительную таблицу (называемую "uservalue") с каждым userdata и хранить там произвольные значения. Подход аналогичен приведенному выше, но вместо сопоставления с предварительно определенными именами свойств и прямого доступа к объекту C вы сначала выдвигаете таблицу пользовательских значений (используя lua_getuservalue), а затем найдите свое поле там.

Другие вопросы по тегам