파이썬 마을 게시판 인덱스 파이썬 마을
우리나라 파이썬 사용자들의 이야기 마을
 
 FAQFAQ   검색검색   멤버리스트멤버리스트   사용자 그룹사용자 그룹   사용자 등록하기사용자 등록하기 
 개인 정보개인 정보   비공개 메시지를 확인하려면 로그인하십시오비공개 메시지를 확인하려면 로그인하십시오   로그인로그인 
Google
python.or.kr Web

[SWIG강좌] 3.3 C++에서의 사용 (1)

 
글 쓰기   답변 달기    파이썬 마을 게시판 인덱스 -> 파이썬 팁/강좌/모듈소개 모음
이전 주제 보기 :: 다음 주제 보기  
글쓴이 메시지
jrcho



가입:
올린 글: 56

올리기올려짐: 2004 5월 12 7:03 pm    주제: [SWIG강좌] 3.3 C++에서의 사용 (1) 인용과 함께 답변

3.3. C++에서의 사용

3.3.1. Class Basics

SWIG 실행결과로 생성되는 파일은 C/C++ wrapper code를 담은 low level wrapper와 Python 코드를 담은 high level wrapper 두가지이다. 이 두 파일을 쓰는 이유는 structure, union, class 등을 처리할 때 편리하게 하기 위해서이다. 여기에서는 구조체와 클래스 두가지 예를 들어 어떻게 SWIG가 파일을 생성하고 사용하는 지를 설명하겠다. 먼저 다음과 같은 간단한 구조체의 예를 들어보자.

struct Vector{
double x,y,z;
};

SWIG 인터페이스파일은


코드:

// example.i
%module example
%{
struct Vector{
  double x,y,z;
  };
%}

struct Vector{
  double x,y,z;
  };


이다. SWIG의 실행은

> swig -python example.i

이고, 실행결과 example.py, example_wrap.c이다. 다음과 같은 setup.py를 이용해

코드:

# setup.py
from distutils.core import setup, Extension

setup(name="example",
      py_modules = ["example"],
      ext_modules=[Extension("_example",["example_wrap.c"])]
     )


Python 인터프리터에서 사용할 수 있도록 다음과 같이 인스톨한다.

> python setup.py install

이러한 인스톨을 통해 Python에 인스톨되는 파일은 example.py와 _example.pyd이다. 이중에서 example.py는 SWIG실행 결과로 나오는 high level wrapper이고, _example.pyd는 low level wrapper를 DLL로 만든 것이다. 둘다 모듈로 Python에서 임포트가 가능하다. 먼저 example.py를 임포트해서 사용하는 예는 다음과 같다.

>>> from example import *
>>> a = Vector()
>>> print a
<C Vector instance at _a8228700_p_Vector>
>>> a.x = 1.
>>> print a.x
1.0
>>> del a
>>>

Low level wrapper를 직접 임포트해서 사용하는 예는 다음과 같다.

>>> from _example import *
>>> a = new_Vector()
>>> print a
'_d01d7f00_p_Vector'
>>> Vector_x_set(a,2)
>>> print Vector_x_get(a)
2.0
>>> delete_Vector(a)
>>>

위 예들에서 example.py를 임포트한 경우 마치 Python 클래스를 사용하듯이 사용할 수 있음을 알 수 있을 것이다. 실제로 SWIG는 Wrapper 코드를 생성할 때 low level wrapper를 사용한 예와 같이 구조체의 각 멤버를 억세스하는 함수(accessor 함수, 즉, get/set 함수)와 메모리 할당/해제와 관련된 함수들을 생성한다. 그리고 SWIG에서 생성된 high level wrapper를 이들 함수를 이용해 Python 클래스와 같이 사용할 수 있도록 해준다. 이렇게 생성된 클래스를 Proxy class라 한다. 다음은 lower level wrapper의 일부와 high leve wrapper의 일부 코드를 나타낸 것이다. 자세히 살펴보면, SWIG에서 구조체를 처리하는 원리에 대해 이해할 수 있을 것이다.

코드:

/* example_wrap.c : low level wrapper */
#include <Python.h>
...
struct Vector{
  double x,y,z;
  };

static PyObject *_wrap_Vector_x_set(PyObject *self, PyObject *args) { ... }
static PyObject *_wrap_Vector_x_get(PyObject *self, PyObject *args) { ... }
static PyObject *_wrap_Vector_y_set(PyObject *self, PyObject *args) { ... }
static PyObject *_wrap_Vector_y_get(PyObject *self, PyObject *args) {...}
static PyObject *_wrap_Vector_z_set(PyObject *self, PyObject *args) {...}
static PyObject *_wrap_Vector_z_get(PyObject *self, PyObject *args) {...}
static PyObject *_wrap_new_Vector(PyObject *self, PyObject *args) {...}
static PyObject *_wrap_delete_Vector(PyObject *self, PyObject *args) {...}

# example.py : high level wrapper
import _example
...
class Vector(_object):
    def __repr__(self):
        return "<C Vector instance at %s>" % (self.this,)
    __swig_setmethods__["x"] = _example.Vector_x_set
    __swig_getmethods__["x"] = _example.Vector_x_get
    if _newclass:x = property(_example.Vector_x_get, _example.Vector_x_set)
    __swig_setmethods__["y"] = _example.Vector_y_set
    __swig_getmethods__["y"] = _example.Vector_y_get
    if _newclass:y = property(_example.Vector_y_get, _example.Vector_y_set)
    __swig_setmethods__["z"] = _example.Vector_z_set
    __swig_getmethods__["z"] = _example.Vector_z_get
    if _newclass:z = property(_example.Vector_z_get, _example.Vector_z_set)
    def __init__(self, *args):
        _swig_setattr(self, Vector, 'this', _example.new_Vector(*args))
        _swig_setattr(self, Vector, 'thisown', 1)
    def __del__(self, destroy=_example.delete_Vector):
        try:
            if self.thisown: destroy(self)
        except: pass


기본적으로 C++ 클래스는 C 구조체에 멤버함수가 추가하여 확장된 것이다. 따라서 acccesor 함수, 생성/소멸과 관련된 함수를 구조체와 동일하게 low level wrapper에 생성하고, 또한 멤버함수와 연관된 함수를 추가로 생성한다. 이러한 low level wrapper를 이용해 Python클래스와 같이 사용하게 할 수 있는 Proxy 클래스를 high level wrapper에 생성해 사용할 수 있도록 한다. 다음예를 보기 바란다. 앞서의 Vector구조체를 클래스로 선언하고, norm() 멤버함수를 추가한 경우이다.

코드:

// example.i
%module example
%{
#include <math.h>

class Vector
{
public:
  double norm(){ return sqrt(x*x+y*y+z*z);}
  double x,y,z;
};

%}

class Vector
{
public:
  double norm();
  double x,y,z;
};


SWIG의 실행 및 인스톨은

> swig -python -c++ example.i
> python setup.py install

이고, setup.py를 다음과 같다.

코드:

# setup.py
from distutils.core import setup, Extension

setup(name="example",
      py_modules = ["example"],
      ext_modules=[Extension("_example",["example_wrap.cxx"])]
     )


low level wrapper인 example.wrap.cxx 파일에서 norm() 에 대한 wrapper 코드가 추가되었음을 확인할 수 있을 것이다:

static PyObject *_wrap_Vector_norm(PyObject *self, PyObject *args) {...}

그리고 다음은 Python에서 멤버함수를 이용한 예이다.

>>> a = Vector()
>>> a.x = 1.
>>> a.norm()
1.0

3.3.2. Constructors

SWIG에서 생성자를 처리하는 방법에 대해 설명하고자 한다. C++ 클래스는 보통 public으로 선언된 생성자를 하나이상 갖고 있다. 예를 들어, 다음과 같은 간단한 클래스의 경우

생성자의 Wrapping
코드:

// example.i
%module example
%{
class A
{
public:
  A(int a) { n = a;}
  void write() { printf("class A: %d",n); }
  ~A() {}
private:
  int n;
};

%}

class A
{
public:
  A(int a);
  void write();
  ~A();
};


// Low level wrapper에서
코드:

class A
{
public:
  A(int a) { n = a;}
  void write() { printf("class A: %d",n); }
  ~A() {}
private:
  int n;
};

static PyObject *_wrap_new_A(PyObject *self, PyObject *args) {
    ...
    if(!PyArg_ParseTuple(args,(char *)"i:new_A",&arg1)) goto fail;
    result = (A *)new A(arg1);
    ...   
}

static PyMethodDef SwigMethods[] = {
    { (char *)"new_A", _wrap_new_A, METH_VARARGS },
            ...


// High level wrapper에서
코드:

class A(_object):
    ...생략
    def __init__(self, *args):
        _swig_setattr(self, A, 'this', _example.new_A(*args))
        _swig_setattr(self, A, 'thisown', 1)
    ... 생략


위 예에서 클래스 A의 생성자에 대한 wrapping은 low level wrapper의 handler 함수와 high level wrapper에서 __init__()로 이루어지게 된다. 만약, 클래스에 public 영역에 constructor가 하나도 없는 경우는 어떨까? 이 경우 역시 SWIG는 default constructor에 wrapper code를 작성한다. 이렇게 생성자가 없을 때 자동으로 default 생성자에 대한 wrapper code를 생성하는 것은 일반적으로 당연하지만, 경우에 따라서 문제를 유발할 수 있다. 여기에 대해서는 다음에 언급하기로 한다.
C++ 클래스 중에 하나 이상의 pure virtual function을 가지는 가상클래스(virtual class)는 객체를 직접 생성할 수 없고, 그 하위 클래스에서 객체를 생성해야만 한다. 이 경우 SWIG는 생성자에 대한 wrapper 코드를 만들지 않는다. 예를 들어

Virtual Class인 경우
코드:

// interface file
%module example
%{

class A
{
public:
  A();
  virtual void write() = 0;
  ~A() {}
};

%}

class A
{
public:
  A();
  virtual void write() = 0;
  ~A();
};


// Low level wrapper에서
코드:

static PyObject *_wrap_new_A(PyObject *self, PyObject *args)  함수 없음


// High level wrapper에서
코드:

class A(_object):
    ...생략
    def __init__(self): raise RuntimeError, "No constructor defined"
    ...생략


위와 같이 A 클래스는 가상 함수이다. 따라서 low level wrapper에 생성자에 대한 handler 함수가 없고, high level wrapper의 __init__() 함수에서는 에러를 발생시키게 된다. 이와 비슷한 이유로 다음과 같이 Protected나 Private 영역에 존재하는 생성자도 마찬가지다.

코드:

// interface file
class A
{
protected:
  A();
public:
  void write(){ printf("class A\n");
  ~A() {}
};

%}

class A
{
protected:
  A();
public:
  void write();
  ~A() {}
};


하지만 이 경우는 조금 더 주의를 기울여야 한다. C++에서 protected로 선언되었다는 것을 SWIG가 모른다면 에러를 발생한다. 즉, 다음과 같이 동일한 클래스에 대해 protected 선언을 숨겼다면,

코드:

// interface file
class A
{
protected:
  A();
public:
  void write(){ printf("class A\n");
  ~A() {}
};

%}

class A
{
public:
  void write();
  ~A();
};


이 경우는 default 생성자에 대한 wrapper code를 생성하기 때문에 compile에러를 발생시킨다. 즉, protected/private 영역에 선언된 생성자는 SWIG에 알려 주어야 이에 대한 wrapper code를 적절히 생성하는 것이다. 일반적으로 SWIG는 private/protected 영역에 선언된 내용을 무시하지만 이경우는 예외이다. 또 다른 해결책으로는 %nodefault 명령을 사용하는 것이다.

코드:

// interface file
class A
{
protected:
  A();
public:
  void write(){ printf("class A\n");
  ~A() {}
};

%}

%nodefault A;     // Disable default constructor wrapping for class A
class A
{
public:
  void write();
  ~A() ;
};



생성자와 관련된 또 다른 이슈는 복사생성자(copy constructor)에 대한 내용이다. 이전 버전(SWIG-1.3.12.까지)의 SWIG에서 복사생성자를 지원하지 않아 %name 명령 등을 이용해 이름을 바꾸어 사용하였으나, 이제는 자연스럽게 지원하고 있다.

코드:

class A
{
public:
  A(int i);
  A(const A& a);
  ...
};


Python에서의 사용
a = A();
b = A(a);
위로
사용자 정보 보기 비밀 메시지 보내기    
이전 글 표시:   
글 쓰기   답변 달기    파이썬 마을 게시판 인덱스 -> 파이썬 팁/강좌/모듈소개 모음 시간대: GMT + 9 시간(한국)
페이지 11

 
건너뛰기:  
새로운 주제를 올릴 수 없습니다
답글을 올릴 수 없습니다
주제를 수정할 수 없습니다
올린 글을 삭제할 수 없습니다
투표를 할 수 없습니다



Powered by phpBB © 2001, 2005 phpBB Group
회선/장비: Daum DNA , 관리: 장혜식,서상현