简体   繁体   English

如何使用SWIG从Lua中的C ++类继承

[英]How to inherit from C++ class in lua using SWIG

For example, there have a class written in C++: 例如,有一个用C ++编写的类:

//Say.h
#pragma once

#include <iostream>

class Say
{
public:
    Say() {}
    virtual ~Say() {}
    virtual void SaySomething() { std::cout << "It should not be show..\n"; };
};

inline void CallCppFun(Say& intf) {
    intf.SaySomething();
}

and I write the Say.i: 我写了Say.i:

//Say.i
%module Test

%{
#include "Say.h"
%}

%include "Say.h"

%inline %{
inline void CallCppFun(Say& intf);
%}

and main.cpp: 和main.cpp:

//main.cpp
#include <iostream>

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

/* the SWIG wrappered library */
extern "C" int luaopen_Test(lua_State*L);

using namespace std;

int main()
{
    lua_State *L;
    L = luaL_newstate();
    luaL_openlibs(L);
    printf("[C] now loading the SWIG wrapped library\n");
    luaopen_Test(L);
    if (luaL_loadfile(L, "Test.lua") || lua_pcall(L, 0, 0, 0)) {
        printf("[C] ERROR: cannot run lua file: %s", lua_tostring(L, -1));
        exit(3);
    }

    return 0;
}

then run the command: 然后运行命令:

swig -c++ -lua say.i

I compile the auto-genearated file example_wrap.cxx and other cpp file and link successfully. 我编译自动生成的文件example_wrap.cxx和其他cpp文件并成功链接。

What I want to do in Test.lua is to inherit from C++ Say class in lua: 我想在Test.lua中做的是从lua中的C ++ Say类继承:

-- Test.lua
Test.Say.SaySomething = function(self)
    print("Inherit from C++ in Lua")
end

my = Test.Say()

my:SaySomething() -- doesn't appear to inherit successfully in lua call

Test.CallCppFun(my) -- doesn't appear to inherit successfully in c++ call

The result of print was not appear to inherit successfully both in lua call and c++ call: 在lua调用和c ++调用中,打印结果似乎都无法成功继承:

[C] now loading the SWIG wrapped library
It should not be show..
It should not be show..

I know it is support in inherit from C++ in Java: generating-java-interface-with-swig 我知道它支持从Java中的C ++继承: 生成带有斜杠的java-interface-with

I know there have a similar question in here, but doesn't give answer of the concrete problem I face to: implementing-and-inheriting-from-c-classes-in-lua-using-swig 我知道这里有一个类似的问题,但是没有给出我要面对的具体问题的答案: 在Lua中使用Cwig实现和继承C类

Does Lua support inherit from C++ class in lua using SWIG or even just use pure lua? Lua是否支持使用SWIG从lua中的C ++类继承,甚至仅使用纯lua? Please show some code example. 请显示一些代码示例。 If SWIG can't do this job, does it have some third-party-library support to do it easily? 如果SWIG无法完成这项工作,那么它是否具有一些第三方库支持来轻松​​完成此工作?

It seems that SWIG with Lua doesn't actually support directors , which is needed for cross-language polymorphism to work. 带有Lua的SWIG似乎实际上并不支持Director ,这是跨语言多态性起作用所必需的。 That's not the end of the world though, it looks like your SaySomething method is the only virtual method in the Say class, so you can substitute a single callback function instead. 不过,这还不是世界末日,看起来您的SaySomething方法是Say类中唯一的虚拟方法,因此您可以替代单个回调函数。 (And if it were me I'd use std::function in the C++ interface design which simplifies a bit of the rest of this work). (如果是我,我会在C ++接口设计中使用std::function ,从而简化了其余工作)。

To show how this might work (and learn me some Lua in the process!) I put together a demo, which is slightly simplified over your test case in the question. 为了展示它是如何工作的(并向我学习一些Lua!),我整理了一个演示,该演示相对于问题中的测试用例略有简化。

In essence what I've ended up doing is modifying the 'in' typemap for a Callback class to be using luaL_ref to retain a 'reference' to an anonymous function, iff the input it is given isn't of the callback type to begin with. 本质上,我最终要做的是修改Callback类的“ in”类型luaL_ref以使用luaL_ref保留对匿名函数的“引用”, luaL_ref是输入的内容不是回调类型的开始用。 My typemap therefore checks what type it has been given and constructs an instance of a temporary, local type that inherits from the Callback type, holding and uses a reference to some Lua defined code until we call it with lua_pcall . 因此,我的类型映射检查了给定的类型,并构造了一个临时的本地类型的实例,该实例从Callback类型继承,保留并使用对Lua定义的代码的引用,直到我们使用lua_pcall对其进行lua_pcall为止。 Since we only hold that instance for the duration of the call we clean it up inside an argfree typemap later. 由于我们仅在调用期间保留该实例,因此我们稍后将其在argfree类型图中清除。 (I got the idea for this from a Lua mailing list post ) (我从Lua邮件列表帖子中得到了这个主意)

So my interface that demonstrates this all in one place looks like this: 因此,我的界面可以在一处展示所有内容,如下所示:

%module test

%{
#include <iostream>
%}

%typemap(in) const Callback& (Callback *tmp=NULL) %{
  if(lua_isuserdata(L,$argnum)) {
    if (!SWIG_IsOK(SWIG_ConvertPtr(L,$input,(void**)&$1,$descriptor,$disown))){
      SWIG_fail_ptr("$symname",$argnum,$descriptor);
    }
  }
  else if (lua_isfunction(L,$argnum)) {
    struct Lua$basetype : $basetype {
      Lua$basetype(lua_State* L, int idx) : L(L) {
        lua_pushvalue(L, idx); // This gets popped by luaL_ref
        // retain our argument
        ref = luaL_ref(L, LUA_REGISTRYINDEX);
      }
      virtual ~Lua$basetype() {
        // release our reference
        luaL_unref(L, LUA_REGISTRYINDEX, ref);
      }
      virtual void run(const std::string& str) const {
         // push our 'reference' onto the stack
         lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
         // manually prepare the function arguments
         lua_pushstring(L, str.c_str());
         // make the actual calll
         lua_pcall(L, 1, 0, 0);
         // TODO: catch error and throw as C++ exception?
      }
    private:
      lua_State* L; // Uh, is keeping this around bad?
      int ref;
    };
    tmp = new Lua$basetype(L,$input);
    $1 = tmp;
  }
  else {
    SWIG_fail_arg("$symname",$argnum,"$1_type");
  }
%}

%typemap(freearg) const Callback& %{
  delete tmp$argnum; // Fine, even if NULL remember
%}

%inline %{
  struct Callback {
    virtual ~Callback() {}
    virtual void run(const std::string& str) const { std::cout << "Got string: " << str << "\n"; }
  };

  void call_me(const Callback& cb) {
    std::cout << "Hello World:\n";
    cb.run("DO IT NOW");
  }
%}

This is enough that I can use it with the following Lua code: 这足以将其与以下Lua代码一起使用:

require("test")
cb=test.Callback()
test.call_me(cb)
cb=function(s) print("Lua got string: "..s) end
test.call_me(cb)

Which I can compile and run with: 我可以编译并运行:

swig3.0 -lua -c++ test.i
g++ -Wall -Wextra test_wrap.cxx -shared -o test.so -I /usr/include/lua5.1/
lua run.lua 
Hello World:
Got string: DO IT NOW
Hello World:
Lua got string: DO IT NOW

Note that this example has pretty much doubled the total amount of Lua I've written ever, so you really should check what I've done is sensible before using in production. 请注意,此示例将我编写的Lua总量增加了几乎一倍,因此您真的应该在将其用于生产之前检查我所做的事情是否合理。 Probably it'd be possible to add proper director support into SWIG using something like this, or emulate it better (ie more than one virtual function in a type) if you're more familiar with Lua. 可能可以使用类似这样的方法在SWIG中添加适当的Director支持,或者如果您更熟悉Lua,则可以更好地模拟它(即,一个类型中有多个虚拟函数)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM