-
Notifications
You must be signed in to change notification settings - Fork 19
[python] calling C functions from Python in OS X
Myungchul Shin edited this page Apr 25, 2018
·
15 revisions
- libpacx는 c로 개발된 모듈을 python에서 사용 가능하도록 만든 c extension이다.
: 다른 platform에서는 정상 동작
- import
sys.path.append('.libs')
import libpacx as pacx
....
- libpacx.dylib을 libpacx.so로 rename한 다음에 import하면 segmentation fault가 발생한다.
관련된 내용을 찾아봐도 해결이 힘든 상황.
- otool -L
libpacx.dylib:
/Users/Nandaro/Desktop/develop/ac_x-1.0.0/install/lib/libpacx.dylib (compatibility version 0.0.0, current version 0.0.0)
/System/Library/Frameworks/Python.framework/Versions/2.7/Python (compatibility version 2.7.0, current version 2.7.6)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)
- python2.7 ac_test.py
Fatal Python error: PyThreadState_Get: no current thread
Abort trap: 6
- call stacks
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libsystem_kernel.dylib 0x00007fff8bba3282 __pthread_kill + 10
1 libsystem_c.dylib 0x00007fff8d9ddb73 abort + 129
2 org.python.python 0x0000000102922a57 Py_FatalError + 49
3 org.python.python 0x0000000102921f39 PyThreadState_Get + 28
4 org.python.python 0x000000010291ed96 Py_InitModule4_64 + 62
5 libpacx.dylib 0x00000001006f0e56 initlibpacx + 54 (pacx.c:400)
....
- 혹시나 해서 ctypes를 사용해 봤지만
import ctypes
pacx = ctypes.cdll.LoadLibrary(".libs/libpacx.dylib")
...
aho = pacx.Index()
<-- 이 부분에서 'AttributeError: dlsym(0x100355480, Index): symbol not found' 오류가 발생
어째서 symbol을 못찾는걸까?
- 드디어 찾은 해결책
- 문제의 힌트는 otool -L 결과에 있었다.
/System/Library/Frameworks/Python.framework/Versions/2.7/Python (compatibility version 2.7.0, current version 2.7.6)
- 뭔가 이상하다. 어째서 버전이 다를까?
- 그래서 python 2.7.9 버전을 새롭게 설치해봤다. 설치 방법은 좀 까다롭긴 하지만 못할 정도는 아니다.
http://wolfpaulus.com/jounal/mac/installing_python_osx/
설치 방법에 따라 설치한 다음 최종적으로 아래와 같이 연결 복구
sudo ln -s /System/Library/Frameworks/Python.framework/Versions/2.7 /Library/Frameworks/Python.framework/Versions/2.7
- 설치 이후 python c extention을 import하면 정상동작
libpacx.dylib:
/Users/Nandaro/Desktop/develop/ac_x/trunk/install/lib/libpacx.dylib (compatibility version 0.0.0, current version 0.0.0)
/Library/Frameworks/Python.framework/Versions/2.7/Python (compatibility version 2.7.0, current version 2.7.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)
-
위 해결책보다 더 깔끔한 방법
- please use -undefined dynamic_lookup
- use .so instead of .dylib
- sample Makefile.am
python_inc = ${python} lib_LTLIBRARIES = libpyxxx.la libpyxxx_la_SOURCES = pyxxx.c libpyxxx_la_LDFLAGS = -all -avoid-version -undefined dynamic_lookup -fPIC libpyxxx_la_LIBADD = libpyxxx_la_CFLAGS = -Wall -fno-strict-aliasing -I$(python_inc)
- copy libpyxxx.dylib to libpyxxx.so and import
-
using ctypes for c++
c++로 구현한 라이브러리를 ctypes를 이용해서
python에서 사용하려고 할 때,
'extern C'를 사용해서 c interface를 만드는데,
여기서 parameter에 ‘c++ reference type’이
있는 경우가 있다. 언듯 보면 ctypes에서는
사용이 안될것 같은데도 byref로 넘기면 정상적으로
동작하는 것을 알 수 있다.
어째서? ctypes는 c로 구현되어 있는데?
아래와 같이 ctypes의 byref()에 대한 설명이 있지만,
의문은 해소되지 않고 있다. 뭔가 이상하다.
Sometimes a C api function expects a pointer to a data type as parameter,
probably to write into the corresponding location, or if the data is too large to be passed by value.
This is also known as passing parameters by reference.
ctypes exports the byref() function which is used to pass parameters by reference.
The same effect can be achieved with the pointer() function,
although pointer() does a lot more work since it constructs a real pointer object,
so it is faster to use byref() if you don’t need the pointer object in Python itself.
그렇다면, c++의 reference type을 내부적으로 어떻게 구현했을까?
https://stackoverflow.com/a/2323402/2289445
https://stackoverflow.com/a/2323203/2289445
본질적으로 pointer와는 다르다는 것을
알 수 있다. 하지만, 실제 구현에서는
pointer를 사용했을 것이라고 유추는 가능하다.
reference는 compile time feature이며 사실상 포인터라고 보면,
/*
* We must return something which can be converted to a parameter,
* but still has a reference to self.
*/
static PyObject *
byref(PyObject *self, PyObject *obj)
{
PyCArgObject *parg;
if (!CDataObject_Check(obj)) {
PyErr_Format(PyExc_TypeError,
"byref() argument must be a ctypes instance, not '%s'",
obj->ob_type->tp_name);
return NULL;
}
parg = new_CArgObject();
if (parg == NULL)
return NULL;
parg->tag = 'P';
parg->pffi_type = &ffi_type_pointer;
Py_INCREF(obj);
parg->obj = obj;
parg->value.p = ((CDataObject *)obj)->b_ptr;
return (PyObject *)parg;
}
ctypes에서 byref()로 넘겨진 python object의 pointer가
c++의 reference parameter로 바인딩되고,
동작되었다고 볼 수 있다.
즉, c++ reference parameter에 대해서 ctypes에서 뭔가
특별하게 처리한게 없지만, pointer로 넘겨줘도 c++에서는
runtime에 적절하게 처리되었다는 의미.
-
c extention for python3
python3(unicode) -> c extension(utf8) -> python3(unicode) 이 변환 과정은 자동이므로 기존에 python2를 위해 개발된 c extension code를 그대로 활용 가능