将任意结构从C传递到LUA并访问它

时间:2016-11-01 23:05:41

标签: c variables struct lua arguments

我面临的这个问题涉及两个部分:

  1. 将任意结构从C传递给Lua函数。举个例子,考虑这两个结构:

    struct Person{
      int age;
      char *name;
    };
    
    struct Paper{
      int width;
      int height;
      char *color;
    };
    

    我希望能够通过简单的C函数将它们传递给Lua,例如:

    template<class T>
    void call(T t){
       ...  (pseudo-code)
    
      check t:
         if Person, lua_getglobal(L, "sendPerson")
         if Paper, lua_getglobal(L, "sendPaper")
    
      for each element in t, check its type
         if integer, lua_pushinteger(...)
         if char*, lua_pushstring(...)
      ...
    
      lua_pcall(...)
    }
    
    Person p = {33, "David"};
    Paper A4 = {210, 297, "white"};
    call(p);
    call(A4);
    

    2)访问。在Lua方面,我如何通过名称访问这些变量?

    function sendPerson(p)
        print(p.age)
        print(p.name)
    end
    
    function sendPaper(p)
        print(p.width)
        print(p.height)
        print(p.color)
    end
    

    我知道我可以使用setfield,但除非我可以在调用Lua函数的泛型方法中调用它,否则我看不出它是如何实现的。

  2. 更新:这是代码。例如,这是WM_LBUTTONDOWN事件。这样,我需要有2个方法和1个结构用于处理我发送给lua的消息:第一个创建线程的方法,第二个调用lua函数的方法,以及结构,所以我可以将它作为参数传递给线程功能

    struct LButtonEvent{
    int x, y, k;
    };
    
    LuaScript *L = new LuaScript(FILENAME);
    
    void _onLButtonDown(LPVOID arg)
    {
        LButtonEvent *b = (LButtonEvent*) arg;
        lua_State *l = L->getState();
        lua_getglobal(l, "onLButtonDown");
        lua_newtable(l);
        lua_pushinteger(l, b->x); lua_setfield(l, -2, "x");
        lua_pushinteger(l, b->y); lua_setfield(l, -2, "y");
        lua_pushinteger(l, b->k); lua_setfield(l, -2, "k");
    
        lua_pcall(l, 1, 0, 0);
    }
    
    void LuaScript::onLButtonDown(LButtonEvent b)
    {
        // I want my window to continue proccessing messages. If lua script calls "sleep", the window freezes.
        CreateThread(0, 0, (LPTHREAD_START_ROUTINE) _onLButtonDown, (LPVOID) &b, 0, 0); 
    }
    
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        switch (message)
        {
        case WM_LBUTTONDOWN:
            LButtonEvent b;
            b.x = (int)(short)LOWORD(lParam);
            b.y = (int)(short)HIWORD(lParam);
            b.k = wParam;
            L->onLButtonDown(b);
            break;
    
        ....
        }
    
        return 0;
    }
    

1 个答案:

答案 0 :(得分:2)

我不会通过不同的功能来区分这两种(或更多)类型。使用metatables!这样,你不需要知道确切的类型,只需使用唯一的可用函数,它就会做正确的事情。

在这里您可以看到如何做到这一点,但请记住,有更好的方法来创建和打开库。

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

#include <stdio.h>

int create_person(lua_State* L)
{
    int age = lua_tointeger(L, -2);
    const char* name = lua_tostring(L, -1);

    lua_newtable(L);
    lua_pushinteger(L, age);
    lua_pushstring(L, name);
    lua_setfield(L, -3, "name");
    lua_setfield(L, -2, "age");

    luaL_setmetatable(L, "personmeta");

    return 1;
}

int print_person(lua_State* L)
{
    lua_getfield(L, -1, "name");
    const char* name = lua_tostring(L, -1);
    lua_getfield(L, -2, "age");
    int age = lua_tointeger(L, -1);

    printf("Name: %s\nAge : %d\n", name, age);
    return 0;
}

int create_paper(lua_State* L)
{
    int width = lua_tointeger(L, -3);
    int height = lua_tointeger(L, -2);
    const char* color = lua_tostring(L, -1);
    lua_newtable(L);

    lua_newtable(L);
    lua_pushinteger(L, width);
    lua_pushinteger(L, height);
    lua_setfield(L, -3, "height");
    lua_setfield(L, -2, "width");

    lua_pushstring(L, color);
    lua_setfield(L, -3, "color");
    lua_setfield(L, -2, "dimensions");

    luaL_setmetatable(L, "papermeta");

    return 1;
}

int print_paper(lua_State* L)
{
    lua_getfield(L, -1, "color");
    const char* color = lua_tostring(L, -1);
    lua_getfield(L, -2, "dimensions");
    lua_getfield(L, -1, "width");
    lua_getfield(L, -2, "height");
    int height = lua_tointeger(L, -1);
    int width = lua_tointeger(L, -2);

    printf("Paper: { %d, %d, %s }\n", width, height, color);
    return 0;
}

int luaopen_llib(lua_State* L)
{
    // initialization

    // create metatable for persons
    luaL_newmetatable(L, "personmeta");
    luaL_newmetatable(L, "personmeta");
    lua_setfield(L, -2, "__index");
    lua_pushcfunction(L, print_person);
    lua_setfield(L, -2, "print");
    lua_pop(L, 1);

    // create metatable for paper
    luaL_newmetatable(L, "papermeta");
    luaL_newmetatable(L, "papermeta");
    lua_setfield(L, -2, "__index");
    lua_pushcfunction(L, print_paper);
    lua_setfield(L, -2, "print");
    lua_pop(L, 1);

    // create library table and store the creation functions
    lua_newtable(L);
    lua_pushcfunction(L, create_person);
    lua_setfield(L, -2, "create_person");
    lua_pushcfunction(L, create_paper);
    lua_setfield(L, -2, "create_paper");
    return 1;
}

Lua测试代码:

local llib = require "llib"

local p = llib.create_person(33, "David")
p:print() -- Name: David 
          -- Age : 33

local paper = llib.create_paper(210, 297, "white")
paper:print() -- Paper: { 210, 297, "white" }

当然,您需要相应地命名此库。

如果您需要使用userdata,您可以使用相同的方式。 Userdata可以有metatables。