使用swig集成lua

戴维营教育原创文章,转载请注明出处。我们的梦想是做最好的iOS开发培训!

项目需要在应用中集成Lua脚本,这个问题不大,因为Lua解释器是用C写的,直接将源码引入工程就可以了。麻烦的是实际编写代码的时候,经常需要在Lua中调用C的一些函数。当然可以自己编写中间代码进行黏合,但抱着能省就省的心态,找到了SWIG(Simplified Wrapper and Interface Generator)。SWIG允许C/C++代码向多种脚本提供接口,包括Ruby、Perl、Python等。下面简单介绍一下如何用SWIG将C语言函数和数据类型向Lua公开。在Mac下面需要事先安装SWIG。我们可以使用HomeBrew或者MacPort进行安装。

SWIG接口文件一般用.i或者.swig作为扩展名。下面我们先简单的了解一下它的语法规则。SWIG会将我们的C函数库封装为Lua的模块。

%module cg
CGRect CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height);

在Lua中可以使用下面的代码进行调用。

local rect = cg.CGRectMake(0, 0, 100, 200)

当然,在使用项目中是不能直接使用这些接口文件进行编译的。我们需要通过swig命令进行转换。

$ swig -lua test.i

处理结果是名为test_wrap.c的C语言源文件,可以直接在项目中使用。可以通过-o参数指定输出文件的名称。

$ swig -lua -o test.c test.i

对于希望直接引入到处理结果中的代码,应该将它们写在%{ %}之间。

%module cg
%{
#import <CoreGraphics/CGGemetry.h>
void printInLua(void) {
    printf("Hello: %s", __func__);
}
%}
struct CGPoint {
        CGFloat x;
        CGFloat y;
};
typedef struct CGPoint CGPoint;
void printInLua(void);

extern double Foo;

处理后,上面的#import语句和printInLua函数都会被拷贝到目标文件中。但是对于需要在Lua中调用的函数或类型,需要在下面声明一下。

处理完后的结果中可以看到如下代码,SWIG通过一系列的宏减少了我们编写交互代码的工作量:

static int _wrap_printInLua(lua_State* L) {
  int SWIG_arg = 0;

  SWIG_check_num_args("printInLua",0,0)
  printInLua();

  return SWIG_arg;

  if(0) SWIG_fail;

fail:
  lua_error(L);
  return SWIG_arg;
}

在项目中使用时需要引入Lua的一些头文件,并且调用一个生成的函数extern int luaopen_test(lua_State *L);来载入刚才的cg模块。

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

//引入外部函数(swig自动生成的)
extern int luaopen_test(lua_State *L);

- (void)viewDidLoad {
    [super viewDidLoad];

    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
    //加载刚才的cg模块,必须调用
    luaopen_test(L);

    NSString *path = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"lua"];
    if (luaL_loadfile(L, [path UTF8String])) {
        lua_close(L);
    }
    else {
        lua_pcall(L, 0, 0, 0);
    }
}

这里用到了一个Lua脚本“test.lua“。

local p = test.CGPoint
p.x = 120
p.y = 32
print(p.x, p.y)

test.printInLua()

编译运行的结果为:

120 32
Hello: printInLua

这样就可以为Lua新增一些函数、数据类型或者是变量。

下面是引入CGGemetry.h中大部分函数到Lua中的一个SWIG文件。

%module cg

%{
#import <CoreGraphics/CGGeometry.h>
%}

typedef float CGFloat;

typedef struct CGPoint CGPoint;

struct CGPoint {
  CGFloat x;
  CGFloat y;
};

typedef struct CGSize CGSize;

struct CGSize {
  CGFloat width;
  CGFloat height;
};

typedef struct CGRect CGRect;

struct CGRect {
  CGPoint origin;
  CGSize  size;
};

CGPoint CGPointMake(CGFloat x, CGFloat y);

CGSize CGSizeMake(CGFloat width, CGFloat height);

CGRect CGRectMake(CGFloat x, CGFloat y, CGFloat width,
  CGFloat height);

CGFloat CGRectGetMinX(CGRect rect);
CGFloat CGRectGetMidX(CGRect rect);
CGFloat CGRectGetMaxX(CGRect rect);
CGFloat CGRectGetMinY(CGRect rect);
CGFloat CGRectGetMidY(CGRect rect);
CGFloat CGRectGetMaxY(CGRect rect);
CGFloat CGRectGetWidth(CGRect rect);
CGFloat CGRectGetHeight(CGRect rect);

bool CGPointEqualToPoint(CGPoint point1, CGPoint point2);
bool CGSizeEqualToSize(CGSize size1, CGSize size2);
bool CGRectEqualToRect(CGRect rect1, CGRect rect2);
bool CGRectIsEmpty(CGRect rect);

const CGPoint CGPointZero;
const CGSize CGSizeZero;
const CGRect CGRectZero;
const CGRect CGRectNull;
const CGRect CGRectInfinite;

通过处理后,就可以在Lua中通过cg模块使用上面所有的函数和结构体类型了。

参考资料

  1. SWIG and Lua

  2. 开发人员SWIG快速入门

戴维营学院(高级开发视频): http://v.diveinedu.com

潜心俱乐部(iOS面试必备): http://divein.club