살다보면 파이썬에서 C언어로 만들어진 DLL파일을 써야 할 일이 있을 수도 있다. 적어도 난 그랬다. 그래서 그걸 하기 위해 많은 해법들을 찾아보았지만, SWIG라든가, 그런것들은 나에게 도움이 되지 않았다. 그래서 ctypes를 공부했다.
일단 사용하기 위해서는 import ctypes로 불러와야 한다. 그러면 ctypes가 메모리에 적재된다.
다음 순서는 내가 사용할 DLL파일을 알려주는 것이다.
myDLL = ctypes.WinDLL(‘myDLLfile’)
위와 같이 쓰면 된다. 이 때, 이걸 사용하고 있는 파이썬 프로그램 파일(.py)의 경로에는 myDLLfile.dll 파일이 존재해야 한다. 이렇게 하면 이제 파이썬은 내가 무슨 라이브러리를 가져다 쓰고싶은건지 알게 되었다.
그 다음, DLL파일이랑 같이 주어진 헤더 파일(.h)을 살펴보고서, DLL파일에 있는 여러 함수 중 뭘 가져다 쓸 것인지 정해야 한다.
사실 헤더파일의 내용을 파이썬의 내용으로 번역하는 것과 별다른게 없는데, C언어 헤더파일에 정의된 내용은 함수의 이름, 함수가 받을 변수의 수와 형, 함수가 되돌려줄 변수의 형이 있다.
mypythonfunc= myDLL[‘MY_C_FUNC’]
위와 같은 한 줄을 적어 줘서, DLL파일의 라이브러리 중 어떤 함수를 쓰고 싶은지 적어주고, 그 함수를 내가 어떤 이름으로 쓸건지 알려주면 된다. 하지만 이것만으로는 저 함수의 모든 것을 알려준 것이 아니다.
mypythonfunc.argtypes = (ctypes.c_int,ctypes.c_double)
위와 같이 argtypes를 지정해 주는데, 이것은 이 함수가 변수로써 어떤 형식을 받을 건지 알려주는 변수이다. 근데 내가잘못 사용한건지는 모르겠지만, 변수의 수가 1개일 때는 에러가 나서 지정해주지 않았다. 지정해주지 않더라도 헤더 파일에 정의된 형으로 잘 넘어가기만 한다면 에러는 나지 않는다.
imct_initialize.restype = ctypes.c_int
restype은 함수가 되돌려주는 값의 형, 즉 반환형이다.
이렇게 하고 나면, 이제 mypythonfunc(a, b)를 가져다 쓰기만 하면 된다.
하지만, 문제가 있다. C언어에는 포인터라는 녀석이 있는데, 함수가 포인터를 통해서 자료를 주고받는 경우에는 restype을 아무리 써봐야 어쩔 수 없다. 포인터를 선언해줘야 한다.
이런 경우 argtypes에는 ctypes.c_void_p를 쓰거나, ctypes.POINTER(ctypes.c_int)를쓴다. ctypes.POINTER의 안쪽에 들어가는 변수는 실제 포인터 선언에 사용한 변수의 형을 써주면 된다.
이렇게 하고서, 만약 다음과 같이 써져 있다고 해 보자.
mypythonfunc.argtypes = (ctypes.POINTER(ctypes.c_int),ctypes.c_double)
그럼 mypythonfunc(a, b)처럼 사용할 때 a에는 포인터 변수를 넣어줘야 한다. 하지만 파이썬에는 포인터라는 개념이 없다. 그래서 만들어 줘야 한다.
일단 포인터 형이 ctypes.c_int니까 x=ctypes.c_int(0) 처럼 선언한다. 0은 초기값이다. 그런 다음 mypythonfunc(ctypes.byref(a), b) 처럼 사용하면된다. byref는 ctypes에서 “참조형 변수”로 값을 전달하는 함수이다. 이렇게 한 후 함수를 실행시키면 a의 값이 함수의 실행 결과에 따라 바뀌어 있음을 알 수 있다.
예를 들어 ctypes.c_int(4)를 4와 비교하고 싶다고 해 보자. 이걸 ctypes.c_int(4) == 4 이렇게 비교를 하자고 하면 False가 뜰 것이다. ctypes.c_int(4).value 로 그 내부의 값을 불러와야 한다. 만약 m = ctypes.c_int(4)로 선언되어 있었다면 m.value를 써줘야 파이썬에서 사용하는 값이 빠져나온다.
배열을 통째로 넘길 때는 어떻게 해야 할까?
mypythonfunc.argtypes = (ctypes.c_void_p, ctypes.c_double)
배열은 argtypes에 들어가는 경우 ctypes.c_void_p로 형을 지정해 주면 된다. 그리고 파이썬의 배열을 넣어주면 되는데, 파이썬의 배열이 C의 배열과 같을리가 없다. 그래서 이렇게 해줘야 한다
myarray = ctypes.c_int * 8
이렇게 하면 myarray는 int를 8개 가지는 type이 된다. 여기서 중요한건 myarray는 변수가 아니라 types이라는 것이다. 그 다음 함수에 값을 넣을 때는
mya = myarray(0,1,2,3,4,5,6,7) 처럼 변수를 하나 새로 만들어서
mypythonfunc(mya, b) 처럼 함수를 불러와야 한다. 이 경우에는 byref를 쓰지 않아도 참조형으로 변수가 넘어가기 때문에 mya는 함수의 실행 결과에 따라 바뀌게 된다. 만약 그 결과로 나온 mya를 다시 파이썬에서 사용하는 배열로 고치고 싶다면 list(mya)를 써서 파이썬의 배열로 바꿔줄 수 있다.
https://docs.python.org/3.6/library/ctypes.html
https://docs.python.org/2/library/ctypes.html
위의 참고문헌을 잘 읽어보고 사용하도록하자.
댓글 남기기