Python package Swig interface

Swig는 C/C++로 작성된 코드를 다른 언어에서 사용할 수 있도록 래핑해주는 툴입니다.

C/C++와 다른 언어의 관계

  • Wrapper functions
    • 매개변수나 리턴 값의 타입을 언어에 맞게 변경해 줍니다.
    • 자동 변경되지 않는 값의 경우 인터페이스 파일에서 설정해야합니다.
  • Variable linking
  • Pointer
    • 포인터 사용의 경우 다른 언어에서 지원하는 함수와 혼용하기 어려울 수 있습니다.
  • Constants
    • 언어에 따라 상수는 변수로 변경될 수 있습니다.
    • #define에 의해 선언된 상수도 반영됩니다.
  • Structures and classes
    • structure나 class의 경우 C 함수 스타일로 변경될 수 있습니다. 예를 들어 A 클래스의 func함수를 호출할 때, A.func()가 아니라 A_func()를 사용해야할 수도 있습니다.
  • Proxy classes(shadow class)
    • 제한된 조건에서 다른 언어에서도 class를 C++ 스타일과 유사하게 사용할 수 있게 해줍니다.

Swig interface file

Swig interface 파일은 다른 언어로 변환할 때, 해당 언어에대한 맞춤 설정을 위해 필요합니다. .i 또는 .swg 확장자를 사용합니다.

%module <module name>
%{
// Swig에 의해 생성된 wrapper 파일에 그대로 복사되는 부분입니다.
%}
// 사용할 함수, 변수 등을 선언하는 부분입니다.
// %include 를 통해 헤더를 호출하면 헤더에 선언된 함수, 변수 등을 참조합니다. 내부에 선언된 헤더는 확장되지 않습니다.

-includeall 옵션을 사용하면 %include를 사용해 호출한 헤더 안에 선언된 헤더까지 확장됩니다. stdio.h 등의 헤더까지 확장되어 모듈에 포함될 수 있기 때문에 조심해야합니다.

Rename

%rename(<to>) <from>: 이후에 선언된 <from> 이름의 함수를 <to>로 변경합니다. Advanced renaming support 기능을 통해 특정 규칙을 통해 이름을 변경할 수 있습니다.

Adding member functions to C structures

%extend { ... }를 통해 다른 언어에서 필요한 함수를 추가할 수 있습니다.

Ignore

%ignore <function|variable>을 통해 함수, 변수 등을 래핑하지 않도록 지정할 수 있습니다.

Default arguments

int func( int var = 10 );으로 선언된 경우 다른 언어에서 func() 또는 func(20) 등으로 사용할 수 있습니다.

C++ 오버로드 함수와 함께 사용되는 경우 문제가 발생할 수 있습니다.

Overloading

void foo( int x );
void foo( long x );
void foo( double x );

foo(int)와 foo(long)은 변환 과정에서 같은 타입의 매개변수가 될 수 있습니다. 따라서 %ignore foo(long) 또는 %rename(foo_l) foo(long) 등을 사용하여 충돌을 피해야합니다.

매개변수의 타입에 따라 함수 호출의 우선순위가 있습니다.

type Precedence
TYPE * 0 (HIGH)
void * 20
Integers 40
Floating point 60
char 80
Strings 100 (LOW)

Typemap

%typemap(method [, modifiers]) typelist code;
  • method: in, out, typecheck, check, arginit, argout, freearg, default, ...
  • modifiers: name="value"
  • typelist: typepattern[, typepattern, ...]
    • typepattern
      • type [(parameter)]
      • type name [(parameter)]
      • ( typelist ) [(parameter)]
  • code
    • { ... }
    • " ... "
    • %{ ... %}

code 없이 선언하면, 선언 이후로 해당 typelist에 적용된 typemap이 적용되지 않습니다.

Examples

/* Convert from Python --> C */
%typemap(in) int {
    $1 = PyInt_AsLong($input);
}
/* Convert from C --> Python */
%typemap(out) int {
    $result = PyInt_FromLong($1);
}

// int에 적용된 map을 size_t에도 동일하게 적용
%apply int { size_t };
%typemap(in) double nonnegative {
    $1 = PyFloat_AsDouble($input);
    if ( $1 < 0 ) {
        PyErr_SetString( PyExc_ValueError, "argument must be nonnegative." );
        SWIG_fail;
    }
}

double sqrt( double nonnegative );
%typemap(in) (char *str, int len) {
    $1 = PyString_AsString( $input );   /* char *str */
    $2 = PyString_Size( $input );       /* int len   */
}
...
int count( char *str, int len, char c );
// In Python, num_a = count("asdfasdf", "a")
%typemap(in) float value[4] (float temp[4]) {
    int i;
    if ( !PySequence_Check($input) ) {
        PyErr_SetString( PyExc_ValueError, "Expected a sequence" );
        SWIG_fail;
    }
    if ( PySequence_Length($input) != 4 ) {
        PyErr_SetString( PyExc_ValueError, "Size mismatch. Expected 4 elements" );
        SWIG_fail;
    }
    for ( i = 0; i < 4; i++ ) {
        PyObject *o = PySequence_GetItem( $input, i );
        if ( PyNumber_Check(o) ) {
            temp[i] = (float) PyFloat_AsDouble(o);
        } else {
            PyErr_SetString( PyExc_ValueError, "Sequence elements must be numbers" );
            SWIG_fail;
        }
    }
    $1 = temp;
}

void set_vector(int type, float value[4]);
// In Python, set_vector(type, [ 1, 2.5, 5, 20 ])

Variables

Global variables

int g_var;
import example

example.cvar.g_var

Constants and enums

#define CONST_1 1
enum PinStatus
{
    LOW = 0,
    HIGH
}
%constant int CONST_2 = 2;
import example

example.CONST_1
example.LOW
example.HIGH
example.CONST_2

Typemap

Swig interface 파일에서 typemap은 최대한 위쪽에 선언해주는 것이 좋습니다.

enum

typedef enum {...} A;
%typemap(in) A = int;
%typemap(out) A = int;