【问题标题】:share data pointer between c and lua module compiled with SWIG在使用 SWIG 编译的 c 和 lua 模块之间共享数据指针
【发布时间】:2016-11-18 18:14:25
【问题描述】:

我需要在我定义了 Lua 状态的主程序中为通过使用 SWIG 包装 c++ 代码创建的动态加载的 Lua 模块提供数据结构指针。

这是我的代码示例:

在 SimpleStruct.h 中:

#pragma once
struct SimpleStruct
{
    int a;
    double b;
};

在 exmaple.h(这个是用 SWIG 编译的)到 Lua 库:

#pragma once

#include "SimpleStruct.h"
#include <iostream>

class TestClass
{

public:
    TestClass()
    {
      std::cout<<"TestClass created"<<std::endl;
    }

    ~TestClass() {}


    void ReadSimpleStruct(void * tmp)
    {

      std::cout<<"reading pointer: "<<std::endl;

      SimpleStruct * pp = reinterpret_cast< SimpleStruct * >(tmp);

      std::cout<<"Simple Struct: " << pp->a << " " << pp->b << std::endl;
    }

};

仅在 example.cpp 中:

 #include "example.h"

这是我的主程序(LuaTest.cpp):

extern "C" 
{ 
    #include <lua.h> 
    #include <lauxlib.h> 
    #include <lualib.h> 
} 


#include <iostream>
#include "SimpleStruct.h"



int main(int argc, char ** argv)
{

lua_State * L = luaL_newstate();
luaL_openlibs(L);

SimpleStruct * ss = new SimpleStruct();
ss->a = 1;
ss->b = 2;

lua_pushlightuserdata(L,ss);
lua_setglobal( L, "myptr");


int s = luaL_dostring(L, "require('example')");
s = luaL_dostring(L, "mc = example.TestClass()");
s = luaL_dostring(L, "mc:ReadSimpleStruct(myptr)");

if(s)
{
    printf("Error: %s \n", lua_tostring(L, -1));
    lua_pop(L, 1);
}

lua_close(L);

std::cout<<"done"<<std::endl;

return 0;
}

example.i(复制自 SWIG 中的 Lua 示例):

/* File : example.i */
%module example

%{
#include "example.h"
%}

/* Let's just grab the original header file here */
%include "example.h"

我将所有内容编译如下:

swig -c++ -lua example.i
g++ -c -fpic example.cpp example_wrap.cxx -I/usr/local/include -I/usr/include/lua5.2/
g++ -shared example.o example_wrap.o -o example.so
g++ LuaTest.cpp -o luatest -llua5.2 -I/usr/include/lua5.2/ -Wall

在 Ubuntu 16.04 上(和在 osx 上,具有不同的路径和相同的结果)。

在 Lua 脚本的最后一行我遇到了分段错误(当我尝试访问“mc:ReadSimpleStruct(myptr)”中的 pp->a 时)。

所以我的问题是:如何使用 Lua light userdata 向加载的 Lua 库提供指向 c++ 对象的指针?

一般来说:我的主程序中有一个包含游戏参数和对象的类,我想提供一个指向该类的指针,指向使用 SWIG 编译的其他加载的 Lua 库。

【问题讨论】:

  • 你能把它做成一个真实的、完整的最小例子来展示它吗?我能理解的——我们需要知道“其他加载的 Lua 库”是如何工作的。
  • 我认为,这是一个完整的真实示例:我使用 SWIG 将 TestClass 编译为名为“example”的 lua 库(这是“我的另一个 lua 库”),接下来在主程序中加载它库(通过“需要示例”)。接下来,我将创建一个 SimpleClass 结构的实例,并使用指向该实例的指针创建轻量级用户数据。最后,我希望能够通过 TestClass 对象('mc:ReadSimpleStruct(myptr))的方法从该指针读取数据。仅此而已,这将使我能够通过创建 SWIG 的 Lua 库访问我自己的全局参数。
  • 您问题中的代码有很多....。它甚至没有说明您的目标平台是什么。有一个名为 SimpleStruct.h 的文件,但包含 TestClass.h。没有 .i 文件。在我写下您未包含的所有缺失部分之前,我什至无法开始回答您的问题。我不知道你的目标是什么版本的 Lua。
  • 好的,我已经编辑了我的问题并提供了整个代码。对于在第一个版本中没有提供完整信息,我深表歉意,但我认为问题出在我的 TestClass 声明中。
  • 这看起来好多了 - 我会尝试在调试器中使用它,看看是否能找到解决方案。 (对于其他可能想要回答的人来说,他们也会发现它更容易)

标签: c++ c lua swig


【解决方案1】:

通过使用调试器(或者只是在TestClass::ReadSimpleStruct 中打印一点额外内容),我们至少可以很快看到段错误的表面原因。在我的测试设置中,函数的 tmp 参数的值是 0x20。这显然是不对的,但要了解为什么以及如何解决它需要更多调查。

作为起点,我向luaL_dostring(L, "print(myptr)") 添加了另一个调用,并使用调试器检查全局变量是否按预期工作。为了更好地衡量,我在每次调用 luaL_dostring 之后添加了一些断言语句,因为您实际上只检查最后一个的返回值,尽管在这里并没有真正产生任何区别。

在我的生活中没有写太多 Lua,我查看了 'Light userdata' 的文档,我看到你正在使用但不知道它是什么。听起来很理想:

一个轻量级的用户数据是一个代表一个C指针的值(也就是一个void *值)

问题在于,如果我们检查生成的 example_wrap.cxx 文件,我们可以看到 SWIG 实际上试图比这更聪明,并且如果我们在生成的调用之前跟踪 arg2 的代码 (arg1)-&gt;ReadSimpleStruct(arg2)我们可以看到它正在调用SWIG_ConvertPtr(最终调用SWIG_Lua_ConvertPtr),然后它会:

  lua_touserdata(L, index);
  //... Some typing stuff from the macro
  *ptr=usr->ptr; // BOOM!

即您所做的并不是 SWIG 期望看到的 void *,SWIG 期望通过其类型系统将它们全部作为来自其他函数或 SWIG 管理的全局变量的返回值进行管理。 (我有点惊讶 SWIG 让它达到段错误而没有引发错误,但我认为这是因为 void* 有点特殊)

这个老问题很好地证明了我对lua_pushlightuserdata 的理解。基本上,我们将需要编写自己的类型映射,以使该函数参数按照您尝试使用它的方式进行处理(如果您真的不想让 SWIG 管理它?)。我们想要做的很简单。这里的用例也与我链接的示例基本相似,除了我们调用lua_touserdata 时所使用的变量是函数参数。这意味着它在堆栈中处于正偏移,而不是负偏移。事实上,SWIG 可以通过 $input 替换告诉我们类型映射内部的偏移量,因此我们的类型映射不仅适用于成员函数的第一个参数。

所以我们的类型映射,它对我们修改后的 example.i 文件中的任何函数参数 void * tmp 执行此操作变为:

%module example

%{
#include "example.h"
%}

%typemap(in) void * tmp %{
    $1 = lua_touserdata(L, $input);
%}

%include "example.h"

然后编译并运行:

swig -c++ -lua example.i
g++ -fPIC example_wrap.cxx -I/usr/local/include -I/usr/include/lua5.2/ -shared -o example.so && g++ -Wall -Wextra LuaTest.cpp -o luatest -llua5.2 -I/usr/include/lua5.2/ 
./luatest 
TestClass created
userdata: 0x11d0730
reading pointer: 0x11d0730
Simple Struct: 1 2
done

【讨论】:

    猜你喜欢
    • 2012-07-26
    • 1970-01-01
    • 2022-01-17
    • 2011-06-10
    • 1970-01-01
    • 2017-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多