用SWIG

时间:2016-11-18 18:14:26

标签: c++ c lua swig

我需要在我的主程序中提供数据结构指针,我将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脚本的最后一行中,我遇到了分段错误(当我尝试访问pp-&gt; a in&#34; mc:ReadSimpleStruct(myptr)&#34;)。

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

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

1 个答案:

答案 0 :(得分:0)

使用调试器(或者只是在<topic>Topic1 <words>word1word2word3word4word5word6</words> </topic> <topic>Topic2 <words>word7word8word9</words> </topic> <topic>Topic3 <words>word10word11word12</words> </topic> 内部打印一些额外的内容),我们可以很快看到segfault的表面原因。在我的测试设置中,函数的TestClass::ReadSimpleStruct参数值为tmp。这显然是不对的,但理解为什么以及如何解决它需要更多的调查。

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

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

  

light userdatum是表示C指针的值(即void *值)

问题是,如果我们检查生成的example_wrap.cxx文件,我们可以看到SWIG实际上试图比这更聪明,如果我们在生成的luaL_dostring之前跟踪arg2的代码{1}}我们可以看到它正在调用(arg1)->ReadSimpleStruct(arg2)(最终调用SWIG_ConvertPtr),然后执行:

SWIG_Lua_ConvertPtr

即。你正在做的不是SWIG期望看到的 lua_touserdata(L, index); //... Some typing stuff from the macro *ptr=usr->ptr; // BOOM! ,SWIG期望通过其输入系统管理它们作为来自其他函数或SWIG管理全局变量的返回值。 (我有点惊讶的是,SWIG让它在不引发错误的情况下达到了段错误,但我认为这是因为void *在某种程度上是特殊的。

这个老问题是确认我对lua_pushlightuserdata的理解的一个很好的例子。基本上我们需要编写自己的typemap来使这个函数参数得到处理你尝试使用它的方式(如果你真的不想让SWIG管理它?)。我们想做的事情很简单。这里的用例大致类似于我链接的示例,除了我们调用void*时我们所追求的变量是一个函数参数。这意味着它在堆栈中的正偏移量,而不是负值。事实上,SWIG可以告诉我们使用lua_touserdata替换的typemape中的偏移量,因此我们的typemap不仅适用于成员函数的第一个参数。

因此我们的typemap为修改后的example.i文件中的任何函数参数$input执行此操作:

void * tmp

然后编译并运行:

%module example

%{
#include "example.h"
%}

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

%include "example.h"