2015-04-30 4 views
1

This SO article is the same thing,, но ответ бесполезен, потому что ответ был в Lua, и вопрос был о C-API. Поэтому я снова спрашиваю. Надеюсь, другие будут пользоваться этим вопросом.Как сохранить тип значения в пользовательских данных?

Я на самом деле с 2 проблемы (я не могу получить Y в Z, чтобы работать, и я не могу получить HelloWorld(), чтобы работать)

Я пытаюсь получить на это:

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

Где x - это userdata. Я постоянно получаю attempt to index a userdata value

Я знаю, что «userdata isn't indexable without a metatable because it's C/C++ data»

В моей 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.

+0

Вы зарегистрировали/создали метатет перед использованием 'push_Something' правильно? –

+0

Правильно, метатабель был создан первым – 010110110101

ответ

4

Во-первых, для 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()) 

Выход:

Hello World!

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

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

Вот многоразовая реализация этого подхода (извлеченная из moon toolkit):

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); 

This function is used for creating an __index metafield. If index is NULL but methods is not, a table containing all the functions in methods is created and pushed to the top of the stack. If index is not NULL, but methods is, the index function pointer is simply pushed to the top of the stack. In case both are non NULL, a new C closure is created and pushed to the stack, which first tries to lookup a key in the methods table, and if unsuccessful then calls the original index function. If both are NULL, nil is pushed to the stack. If nup is non-zero, the given number of upvalues is popped from the top of the stack and made available to all registered functions. (In case index and methods are not NULL, the index function receives two additional upvalues at indices 1 and 2.) This function is used in the implementation of moon_defobject, but maybe it is useful to you independently.

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

+0

Стол '__index' таблицы userdata должен работать просто отлично, он просто не разрешит доступ к каким-либо свойствам C-стороны сам по себе. Я рад, что вы сказали, что исходный код работал, это выглядело хорошо для меня, поэтому я был в замешательстве. –

+0

Да, но поскольку OP использует 'luaL_newmetatable' /' luaL_setmetatable', метатет и метаметод '__index' (функция или таблица) разделяются всеми объектами данного типа пользовательских данных. Если вы хотите реализовать свойства, используя таблицу '__index', вам нужны отдельные метаданные для каждого объекта userdata. – siffiejoe

+0

Согласовано. Я просто отвечал на «Для доступа к свойствам по пользовательской информации вам нужно установить __index для функции вместо таблицы». что заставило его звучать (по крайней мере, для меня), что таблица просто не сработает. –

Смежные вопросы