【问题标题】:Passing arbitrary struct from C to LUA and accessing it将任意结构从 C 传递到 LUA 并访问它
【发布时间】:2017-03-15 04:15:36
【问题描述】:

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

  1. 将任意结构从 C 传递到 Lua 函数。例如,考虑以下 2 个结构:

    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 函数的通用方法中调用它,否则我看不出它是怎么可能的。

更新:这是代码。例如,这里是 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;
}

【问题讨论】:

  • 关于更新:你是对的,你必须定义很多东西。我真的无法进一步回答这个问题,因为我真的不知道窗口和回调机制。但是,在您的第一个示例中,您以伪代码显示您正在将一些相关数据传递给 lua。为什么不准确传递您需要的值?这样,您可以以正确的方式(使用相应的参数)定义 lua 函数,而不必通过表访问值。但是,我仍然觉得这可以更轻松地完成。但这在很大程度上取决于你的结构,我不知道。
  • 我感觉你可以在你的 c++ 代码中使用某种继承,并为每个类调用一个适当的重载函数。

标签: c variables struct lua arguments


【解决方案1】:

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

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

#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" }

当然,你需要相应地命名这个库。

如果你需要使用用户数据,你可以同样的方式使用它。用户数据可以有元表。

【讨论】:

  • 但我不想在 LUA 中创建它们。在 LUA 中,我只想访问它们的变量,如宽度、高度等。此外,我有几十个结构。这就是重点:我必须为永远的结构创建“create_...”方法吗?
  • 好吧,您也可以随时在 c 中创建它们。问题是:为什么你有这么多不同的结构,它们似乎没有共同点(纸和人)(我知道这是一个例子,但仍然如此)。如果他们共同点,使用某种继承。在某个地方,您必须以某种方式定义所有单个“create_...”函数(或至少类似的函数)。没有办法解决这个问题。也许您需要澄清,您想要究竟做什么,而不是如何。然后我们可以看一下。但我感觉你想的太复杂了。
  • 我正在尝试创建一个窗口回调句柄(我的示例只是为了让事情更清楚,但我最终把事情搞砸了)。因此,当我的窗口接收到鼠标事件时,C 调用 lua 函数“onMouse(m)”,LUA 可以使用 m.x、m.y、m.click。轮子事件时,C调用“onWheel(w)”,LUA可以使用m.delta、m.pos等。
  • 您是将整个窗口句柄传递给 lua 函数还是只传递相关信息?如果您执行后者(考虑到您的示例,情况似乎如此),那么您将手动组装所有数据。因此,稍微扩展一下并构建一些保存信息或lua表的用户数据(顺便说一句,它的lua,而不是LUA)是没有问题的。但是,这似乎并不理想。你能更新你的例子并展示一些真实的代码吗(不要让它膨胀;做一个最小的 working 例子)。那我可以看看。
  • 更新了!我只是传递相关信息,但其中有很多。
猜你喜欢
  • 2014-05-14
  • 1970-01-01
  • 1970-01-01
  • 2012-07-25
  • 2013-07-24
  • 2015-11-14
  • 1970-01-01
  • 2012-05-27
  • 2011-11-10
相关资源
最近更新 更多