일단 swig를 설치한다.
Python에서도 쓰고싶은 C++ 클래스를 준비한다. 이미 만들어져 있다고 치자.
필자의 경우는 mo_solver.h 란 직접 만든 헤더파일이다. 이 안엔 클래스가 여러개 들어있다.
우선은 swig를 위해 Interface 파일을 작성해야 한다.
mo_solver.h 니까 mo_solver.i 라고 하지 뭐.
// mo solver wrapper for Python 3
%module MoSolver
%{
#include "mo_solver.h"
%}
/**
* MO solver class
*
* r denotes the dimension of edge weight vector.
*/
class MoSolver
{
public:
void printGraph( std::ostream& o ) const;
public:
MoSolver( size_t num_nodes, size_t r );
~MoSolver();
public:
void init( size_t num_nodes, size_t r );
void free();
void addEdge( size_t par, size_t child, ... );
void addEdge( size_t par, size_t child, const moInts& weights );
void solve_approx( const double epsilon );
void show_sol( std::ostream& o );
std::tuple< std::vector<size_t>, std::vector<int> > get_sol() const;
int verbose;
};
이런 내용으로 되어있다.
% 로 시작하는 행은 SWIG에게 특별히 내려줄 명령을 담은 것이다.
%module MoSolver 는 파이선에서 보일 모듈/클래스의 이름이다.
%{
#include "mo_solver.h"
%}
는 출력된 Wrapper Class의 해더 부분에 들어갈 코드를 적는다. mo_solver.h가 필요할 것이 뻔하니 적어줬다.
밑에 있는 클래스스러운 것은 C/C++ 로 된 부분으로서 SWIG가 보고 잘 인식할 수 있다. 여기에 적은 부분만 Python과 연동하게 해준다... private 함수까지 연동할 필요는 없으니 원본 클래스에서 public 부분만 따왔다.
귀찮으면 그냥
%include "mo_solver.h"
로 이미 있는 헤더파일을 몽땅 파이선과 연동하라고 해도 된다.
사실 인터페이스 파일을 어떻게 작성해야하는진 잘 모르는 상태다. moInts 라고 std::vector<int> 를 typedef 해놓은 것도 있는데 SWIG가 잘 인식하기나 하려나 -_-;; 이런 세세한 것 보다는 일단은 Python에 클래스가 인식이나 되게 하는 것이 1차 목표이다. 실제로 addEdge() 라든지 이런 함수들이 Python에서 잘 작동하게 되는 것은 2차 목표.
http://www.swig.org/Doc1.3/Python.html#Python_nn10
매뉴얼을 컨닝해보니까 우선은 SWIG를 이용해 wrapper를 만들고, 그 wrapper와 원본 소스는 컴파일 해서 .so (dll파일 같은거)로 합체시켜야 한다고 하네.
swig -c++ -python mo_solver.i
우선 이렇게 wrapper를 생성한다. Python 2든, Python 3이든 호환되는 코드가 나오니 -python 이라고만 하면 된다. 그 외 펄도 지원한다고 하지만 아직은 필요 없다.
mo_solver_wrap.cxx 파일이 생성되었다. 그리고 MoSolver.py 파일도 생겼다. MoSolver.py 쪽이 Python쪽에서 보게되는 인터페이스이다. 이 파일은 직접 수정하지 말고, 수정할 일이 생기면 mo_solver.i 를 수정해야 한다 (고 적혀있기도 하다... 열어보면.).
이제 .so파일을 만들자.
g++ -O2 -fPIC -c mo_solver.cpp
-> mo_solver.o 생성
g++ -O2 -fPIC -c -I/usr/include/python3.2mu -c mo_solver_wrap.cxx
-> mo_solver_wrap.o 생성
주의사항은. /usr/include/python3.2mu 라고 된 부분은 바뀔 수 있다는 것이다. 각자 시스템을 잘 뒤져서 Python.h 가 있는 디렉토리를 적어주도록 하자.
마지막으로 저 두 파일을 묶어서 .so 파일을 만들자.
g++ -shared mo_solver.o mo_solver_wrap.o -o _MoSolver.so
주의사항. MoSolver 클래스를 만들던 중이었기 때문에 _MoSolver.so 라고 이름지은 것임!
이걸 Makefile로 만들면 아래와 같다.
PYTHON_INC=-I/usr/include/python3.2mu
all: MoSolver.py _MoSolver.so_MoSolver.so: mo_solver.o mo_solver_wrap.og++ -shared mo_solver.o mo_solver_wrap.o -o $@mo_solver_wrap.cxx: MoSolver.pyMoSolver.py: mo_solver.i mo_solver.h mo_solver.cppswig -c++ -python mo_solver.imo_solver.o: mo_solver.cpp mem_pool.hppg++ -std=c++0x -O2 -fPIC -c mo_solver.cppmo_solver_wrap.o: mo_solver_wrap.cxxg++ -std=c++0x -O2 -fPIC $(PYTHON_INC) -c mo_solver_wrap.cxxclean:rm -f mo_solver_wrap.c mo_solver_wrap.cxx MoSolver.pyrm -f mo_solver.o mo_solver_wrap.o _MoSolver.so
테스트 파일을 만들어보자.
mo_test.py 다.
#!/usr/bin/python3
import sys
sys.path.append('./cxx')
from MoSolver import MoSolver
solver = MoSolver( 8, 3 )
solver.verbose = True
sys.path.append('./cxx') 이건 왜 있냐면, 필자가 mo_solver.cpp 파일을 cxx 디렉토리 안에다 따로 분리했기 때문이다.
즉, mo_test.py 가 있는 곳에 cxx란 디렉토리도 있고, cxx안에 _MoSolver.so, MoSolver.py 파일이 들어있다.
저걸 적지 않으면 그 다음 줄의 from MoSolver import MoSolver 가 성공하지 못한다.
저기까진 성공했다... 화면엔 아무것도 안 나온다. 다른 작업은 아무것도 안 시켰으니까. 만약
Traceback (most recent call last):
File "./cxx/MoSolver.py", line 16, in swig_import_helper
fp, pathname, description = imp.find_module('_MoSolver', [dirname(__file__)])
ImportError: No module named _MoSolver
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "./mo_test.py", line 5, in <module>
from MoSolver import MoSolver
File "./cxx/MoSolver.py", line 26, in <module>
_MoSolver = swig_import_helper()
File "./cxx/MoSolver.py", line 18, in swig_import_helper
import _MoSolver
ImportError: No module named _MoSolver
#!/usr/bin/python3
import sys
sys.path.append('./cxx')
from MoSolver import MoSolver
solver = MoSolver( 8, 3 )
solver.verbose = True
solver.addEdge( 0, 1, [2, 7] )
solver.addEdge( 0, 2, [1, 7] )
solver.addEdge( 1, 3, [7, 4] )
원래의 C++ 클래스에는 두개의 addEdge 함수가 있었다.
void addEdge( size_t par, size_t child, ... );
void addEdge( size_t par, size_t child, const moInts& weights );
두번째 것을 염두에 둔 것인데...
이런 에러가 난다:
Traceback (most recent call last):
File "./mo_test.py", line 10, in <module>
solver.addEdge( 0, 1, [2, 7] )
File "./cxx/MoSolver.py", line 85, in addEdge
def addEdge(self, *args): return _MoSolver.MoSolver_addEdge(self, *args)
NotImplementedError: Wrong number or type of arguments for overloaded function 'MoSolver_addEdge'.
Possible C/C++ prototypes are:
MoSolver::addEdge(size_t,size_t,...)
MoSolver::addEdge(size_t,size_t,moInts const &)
Traceback (most recent call last):File "./mo_test.py", line 10, in <module>solver.addEdge( 0, 1, [2, 7] )File "./cxx/MoSolver.py", line 85, in addEdgedef addEdge(self, *args): return _MoSolver.MoSolver_addEdge(self, *args)TypeError: in method 'MoSolver_addEdge', argument 4 of type 'moInts const &'
// mo solver wrapper for Python 3%module MoSolver%include "std_vector.i"%template(moInts) std::vector<int>;%{#include "mo_solver.h"%}
#!/usr/bin/python3import syssys.path.append('./cxx')from MoSolver import MoSolver, moIntssolver = MoSolver( 8, 3 )solver.verbose = Truee = moInts( [2,7] )print( e[0] )print( e[1] )solver.addEdge( 0, 1, e )
27Traceback (most recent call last):File "./mo_test.py", line 13, in <module>solver.addEdge( 0, 1, e )File "./cxx/MoSolver.py", line 161, in addEdgedef addEdge(self, *args): return _MoSolver.MoSolver_addEdge(self, *args)TypeError: in method 'MoSolver_addEdge', argument 4 of type 'moInts const &'
// mo solver wrapper for Python 3%include "std_vector.i"%template(moInts) std::vector<int>;%module MoSolver%{#include "mo_solver.h"%}typedef std::vector<int> moInts;class MoSolver{public:void printGraph( std::ostream& o ) const;public:MoSolver( size_t num_nodes, size_t r );~MoSolver();public:void init( size_t num_nodes, size_t r );void free();void addEdge( size_t par, size_t child, const moInts &weights );void solve_approx( const double epsilon );void show_sol( std::ostream& o );std::tuple< std::vector<size_t>, std::vector<int> > get_sol() const;int verbose;};
#!/usr/bin/python3import syssys.path.append('./cxx')from MoSolver import MoSolver, moIntssolver = MoSolver( 8, 3 )solver.verbose = Truesolver.addEdge( 0, 1, [2, 7] )
// For python...void MoSolver::get_sol(std::vector<size_t> *path, std::vector<int> *cost) const{tie( *path, *cost ) = get_sol();}
path = []cost = []solver.get_sol( path, cost )
Traceback (most recent call last):File "./mo_test.py", line 28, in <module>solver.get_sol( path, cost )File "./cxx/MoSolver.py", line 164, in get_soldef get_sol(self, *args): return _MoSolver.MoSolver_get_sol(self, *args)TypeError: in method 'MoSolver_get_sol', argument 2 of type 'std::vector< size_t,std::allocator< size_t > > *'
path = moSizes()cost = moInts()solver.get_sol( path, cost )print( "Path:" )for p in path :print( p )print( "Cost:" )for c in cost :print( c )