2012年6月3日 星期日

SWIG (Simplified Wrapper and Interface Generator)


SWIG (Simplified Wrapper and Interface Generator)
除能簡化 C/C++函式物件等在各語言間的使用,亦能達到相當程度的防駭功用
(因包裝後的程式碼已轉換,與原程式不同,故原碼即使有buffer overflow等弱點,轉換後弱點亦會消失)

swig預設輸出為當前目錄(i.e.相當於有一預設選項 -outcurrentdir),
可用 -outdir <目錄名> 或 -o [目錄名/]輸出wrap檔名 改變,
優先權為 -o  >  -outdir  >  -outcurrentdir

以 example.i , example.c 為例(其中example.i<自建>為example.c 的SWIG interface file)
example.c如下:
double  My_variable  = 3.0;

int  fact(int n) {
  if (n <= 1) return 1;
  else return n*fact(n-1);
}

int my_mod(int n, int m) {
  return(n % m);
}

一.
建一example.i如下:(底下的%module後接自定模組名而%{...%}的作用如同yacc或bison的%{...%})
%module example
%{
/* Put headers and other declarations here */
extern double My_variable;
extern int fact(int n);
extern int my_mod(int n, int m);
%}

extern double My_variable;
extern int fact(int n);
extern int my_mod(int n, int m);

I. 在tcl中用其C函式:(如沒tcl.h須先安裝libtcl-devel之rpm)
step 1. swig -tcl example.i                     ;=> 產生能給tcl用的 example_wrap.c
step 2. g++ -shared -fpic example.c example_wrap.c -o example.so
上會產生example.so
之後,打tclsh進入tcl後:
% load ./example.so ;=> 載入 example.so
% fact 5 ;=> 120 (i.e. 5!)
% my_mod 26 7 ;=> 餘5
% expr $My_variable + 5.5 ;=> 3.0+5.5 = 8.5

II. 在perl中用其C函式:(如沒EXTERN.h須先安裝perl-devel之rpm)(底下perl版本5.12.3, 不同者自改step2.)
step 1. swig -perl5 example.i     ;=> 產生能給perl用的example_wrap.c與example.pm
step 2. g++ -shared -fpic example.c example_wrap.c -I/usr/lib/perl5/5.12.3/i386-linux-thread-multi/CORE -o example.so
上會產生example.so
之後,建perl之script如下:(執行結果同I.)
#!/usr/bin/perl
# 下行即載入上面於同目錄下所建之example.so
use example;
print example::fact(5), "\n";
print example::my_mod(26,7), "\n";
print $example::My_variable + 5.5, "\n";        ;因此處example::My_variable為變數,故加$

III. 在python中用其C函式: (底下python版本2.7, 不同者自改step2.)
step 1. swig -python example.i     ;=> 產生能給python用的example_wrap.c與example.py
step 2. g++ -shared -fpic example.c example_wrap.c -I/usr/include/python2.7 -o _example.so
上會產生 _example.so
之後,建python之script如下:(執行結果同I.)
#!/usr/bin/python
# 下行即載入上面於同目錄下所建之_example.so (可視module example為物件)
import example
print example.fact(5)
print example.my_mod(26,7)
print example.cvar.My_variable + 5.5


二.簡化法:
step 0. 把 example.i 減化如下:
%module example
%{
#include "example.h"
%}
%include "example.h"

I.在perl中用其C函式(header files): (底下的example.h內容與example.c一模一樣)
step 1. swig -perl5 example.i ;=> 產生能給perl用的example_wrap.c與example.pm
step 2. g++ -shared -fpic example.h example_wrap.c -I/usr/lib/perl5/5.12.3/i386-linux-thread-multi/CORE -o example.so
上會產生example.so 之後步驟同 一II.

II.在php中用其C函式 (如沒php-config須先安裝 php-devel)
step 1. swig -php example.i ;=> 產生能給php用的example.php  example_wrap.c  php_example.h 三個檔案
step 2. g++ `php-config --includes` -shared -fpic example_wrap.c -o example.so
其中step1.會產生給php用的 example.php  example_wrap.cpp php_example.h 三個檔案
而step2.會產生example.so
之後,建一php script如下:(如沒/usr/bin/php須先安裝php-cli)
<?php
include("example.php");
echo fact(5) . "\n";
echo my_mod(26,7) . "\n";
echo example::My_variable_get() + 5.5 . "\n";
?>

且尚須在當前目錄下建一php.ini檔如右(僅一行): extension=./example.so
然後執行:php --php-ini ./php.ini ./ex_php        執行結果同一.


三.cmake  (底下以python為例)
一目錄中除有與一同example.i, example.cxx(亦與上述之example.c一模一樣)外,
尚須建一CMakeLists.txt檔案如下:
# 只需稍改後面四行即可用於其他的python程式
# 例如若是與二同之example.i與example.h 則把底下的example.cxx改成example.h

FIND_PACKAGE(SWIG REQUIRED)
INCLUDE(${SWIG_USE_FILE})

FIND_PACKAGE(PythonLibs)
INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_PATH})

INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})

SET(CMAKE_SWIG_FLAGS "")

SET_SOURCE_FILES_PROPERTIES(example.i PROPERTIES CPLUSPLUS ON)
SET_SOURCE_FILES_PROPERTIES(example.i PROPERTIES SWIG_FLAGS "-includeall")
SWIG_ADD_MODULE(example python example.i example.cxx)
SWIG_LINK_LIBRARIES(example ${PYTHON_LIBRARIES})

然後在此目錄下打: cmake .    (.表當前目錄)
其後再打:make
上會產生 _example.so  (若在Windows下會產生 _example.pyd)
其餘步驟同 一III.

四.c++ wrap (以python為例)(example.h與example.i同二.)(swig未完全支援c++,故預設為c)
(下面的step1產生能給python用的example.py與example_wrap.cpp<用-c++才可產生此.cpp,不然預設是.c>)
step 1. swig -c++ -python -o example_wrap.cpp example.i
step 2. g++ -shared -fpic example_wrap.cpp -I/usr/include/python2.7 -o _example.so
上會產生 _example.so 之後步驟同 一III.


五.於其他語言中使用C語言常用函式庫(以sin()與strcmp()為例)
建一檔example.h,把欲用到的函式其header files納進此檔,
由於我們這裡只用到sin()與strcmp(),故此處example.h內容只有下面兩行:
#include <math.h>
#include <strings.h>

而 example.i 如下:
%module example
%inline %{
#include "example.h"
extern double sin(double x);
extern int strcmp(const char *, const char *);
%}
#define PI 3.14159265359

I. 在tcl中用C函式:(如沒tcl.h須先安裝libtcl-devel之rpm)
step 1. swig -tcl example.i                     ;=> 產生能給tcl用的 example_wrap.c
step 2. g++ -shared -fpic example_wrap.c -o example.so
上會產生example.so
之後,打tclsh進入tcl後:
% load ./example.so ;=> 載入 example.so
% puts PI ;=> 輸出 3.14159265359
% sin [expr {$PI/2}] ;=> 輸出 1.0
% strcmp He He ;=> 輸出 0  (因兩字串相等)
% strcmp He She ;=> 輸出 -1 (因兩字串不相等)

II. 在python中用C函式: (底下python版本2.7, 不同者自改step2.)
step 1. swig -python example.i     ;=> 產生能給python用的example_wrap.c與example.py
step 2. g++ -shared -fpic example_wrap.c -I/usr/include/python2.7 -o _example.so
上會產生 _example.so
之後,建python之script如下:(執行結果同I.)
#!/usr/bin/python
# 下行即載入上面於同目錄下所建之 _example.so (可視module example為物件)
import example
print example.PI
print example.sin( example.PI / 2 )
print example.strcmp('He','He')
print example.strcmp('He','She')

III.在clisp中用C函式: (注意:step 2.可能因版本不同而需更改)
step 1. swig -clisp example.i     ;=> 產生能給clisp用的example.lisp
step 2. 在 example.lisp 中 的 (default-foreign-language :stdc) 之下增加一行:
        (setf +library-name+ "libstdc++.so.6")
        且為避免與clisp內建sin函數衝突,其下一行處之(ffi:def-call-out sin 的 sin 改成 sin2  
        然因(:name "sin") 保留沒更動,故仍以example:sin 方式呼叫之
之後,打clisp進入lisp後照以下進行,結果會同上:
(load "./example.lisp")
(print example:PI)
(example:sin (/ example:PI 2))        ;因clisp有內建PI, 故於此處...(/ PI 2)) 亦可
(example:strcmp "He" "He")
(example:strcmp "He" "She")


六.於其他語言中使用C++/C語言常用函式庫(以sin()與strcmp()為例)
example.h 與 example.i 同 五.

I.在Java中用C++/C函式: (注意:須先安裝java sdk, 而step 2.可能因java sdk版本不同而需更改)
下行會產生 exampleConstants.java  example.java  exampleJNI.java  example_wrap.cxx 四個檔案
step 1. swig -c++ -java example.i
step 2. g++ -shared -fpic example_wrap.cxx -I/usr/lib/jvm/java/include -I/usr/lib/jvm/java/include/linux -o example.so
上會產生 libexample.so
之後,建一java檔如下:(設檔名為runme.java)
public class runme {
  static {
    System.loadLibrary("example");
  }

  public static void main(String argv[]) {
    System.out.println(example.PI);
    System.out.println(example.sin(example.PI / 2));
    System.out.println(example.strcmp("He","He"));
    System.out.println(example.strcmp("He","She"));
  }
}

以 javac *.java 編譯成*.class後,再用 java runme 來執行,輸出結果同 五.

註:出現錯誤常是因為java.library.path中不含當前目錄,
    這時先export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH 後再跑java runme 即可


II.在Ruby中用C++/C函式:(如沒ruby.h須先安裝ruby-devel之rpm)(注意:step 2.可能因版本不同而需更改)
step 1. swig -c++ -ruby example.i           ;=> 產生能給Ruby用的 example_wrap.cxx
step 2. g++ -shared -fpic example_wrap.cxx -I/usr/lib/ruby/1.8/i586-linux/  -o example.so
上會產生example.so
之後,建一ruby程式檔如下:(執行結果同 五.)
#!/usr/bin/ruby -w
#下行即載入上面於同目錄下所建之example.so之module
require 'example'
puts Example::PI
puts Example.sin( Example::PI / 2 )
puts Example.strcmp('He','He')
puts Example.strcmp('He','She')


III.在Lua中用C++/C函式:(如沒lua.h等須先安裝liblua-devel之rpm)(注意:step 2.可能因版本不同而需更改)
step 1. swig -c++ -lua example.i           ;=> 產生能給Lua用的 example_wrap.cxx
step 2. g++ -shared -fpic example_wrap.cxx -o example.so
上會產生example.so
之後,建一lua程式檔如下:(執行結果同 五.)
#!/usr/bin/lua
-- 下行即載入上面於同目錄下所建之example.so之module
require "example"
print( example.PI )
print( example.sin( example.PI / 2) )
print( example.strcmp("He","He") )
print( example.strcmp("He","She") )


ref.
http://www.swig.org/tutorial.html
file:///usr/share/doc/swig/Manual/SWIGDocumentation.html
/usr/share/doc/swig-doc/Examples/ 中有不少其他語言的程式例


沒有留言:

張貼留言