ctypes를 통해 공유 라이브러리의 기존 함수 포인터를 Python에 정의 된 콜백으로 바꾸려고합니다.
C에서 공유 라이브러리의 소스 :
#include <assert.h>
#include <stdio.h>
void (*plot)();
int c_main(int argc, void** argv) {
printf("plot is %p\n", (void*)plot);
assert(plot != NULL);
plot();
return 0;
}
Python 스크립트의 소스 :
from sys import platform
from pathlib import Path
import ctypes
import _ctypes
FUNCTYPE = ctypes.WINFUNCTYPE if platform == 'win32' else ctypes.CFUNCTYPE
def dlclose(obj):
if platform == "win32":
_ctypes.FreeLibrary(obj._handle)
else:
_ctypes.dlclose(obj._handle)
def enc_args(args):
C_ARGS = (ctypes.POINTER(ctypes.c_char) * len(args))()
for idx, arg in enumerate(args):
C_ARGS[idx] = ctypes.create_string_buffer(arg.encode("utf-8"))
return C_ARGS
@FUNCTYPE(None)
def plotxy():
print("plotxy")
C_ARGS = enc_args([str(Path(__file__))])
CAUXDLL = ctypes.CDLL("./test.so")
print(plotxy)
print(CAUXDLL.plot)
CAUXDLL.plot = plotxy
print(CAUXDLL.plot)
print(CAUXDLL.c_main(len(C_ARGS), C_ARGS))
테스트 할 스크립트 :
gcc -fPIC -shared -o test.so test.c
python3 test.py
내가 얻는 출력 :
# ./test.sh
<CFunctionType object at 0x7fb1f0abb1c0>
<_FuncPtr object at 0x7fb1f0abb640>
<CFunctionType object at 0x7fb1f0abb1c0>
plot is (nil)
python3: test.c:8: c_main: Assertion `plot != NULL' failed.
./test.sh: line 3: 21171 Aborted python3 test.py
따라서 Python (plotxy)에서 정의 된 함수는 type CFunctionType
이고 C로 정의 된 함수 포인터 는 type 인 것으로 보입니다 _FuncPtr
. 대체 CAUXDLL
가 적용 되어 있지만 주 함수가 호출되면 효과가없는 것 같습니다.
https://docs.python.org/3/library/ctypes.html#module-ctypes 를 읽는 것 외에도 다른 질문을 발견했습니다 (예 : ctypes에서 typedef를 사용하는 방법 또는 콜백 함수를 만드는 파이썬 cytpes-세분화 오류 (코어 덤프)) ),하지만 CFunctionType
(plotxy)를 _FuncPtr
.
편집하다
나는 이것이 ctypes의 일반적인 사용에 문제가되지 않을 것이라고 생각합니다. 그것은 내가 성공적으로 성취 한 것이며 문서에 충분히 설명되어 있습니다. 이 질문은 그 이상입니다. C 함수를 실행하고 싶지 않습니다. Python으로 기존 함수 포인터를 Python으로 작성된 콜백으로 바꾸고 싶습니다. 도우미 C 함수를 사용하여 수행 할 수 있습니다 ( https://github.com/ghdl/ghdl-cosim/blob/master/vhpidirect/shared/pycb/caux.c#L32-L40 참조 ). 따라서이 질문은 (가능한 경우) 도우미 기능없이이를 달성하는 방법에 관한 것입니다.
에서 전역 변수에 액세스하는 방법은를 ctypes
사용하는 in_dll
것이지만 함수 포인터를 변경하는 노출 된 방법은없는 것 같습니다. 나는 그것을 읽고 호출 할 수 밖에 없었기 때문에 도우미 기능 없이는 불가능하다고 생각합니다.
아래 예제는 int
전역 변수를 변경 하지만 CFUNCTYPE
인스턴스에는이 value
를 변경할 멤버 가 없습니다 . 문제를 해결하기 위해 전역을 설정하는 C 도우미를 추가하고 변경하기 전에 올바르게 액세스되었는지 확인하기 위해 콜백의 기본값을 추가했습니다.
test.c :
#include <stdio.h>
#define API __declspec(dllexport)
typedef void (*CB)();
void dllplot() {
printf("default\n");
}
API CB plot = dllplot;
API int x = 5;
API int c_main() {
printf("x=%d (from C)\n",x);
plot();
return 0;
}
API void set_cb(CB cb) {
plot = cb;
}
test.py :
from ctypes import *
PLOT = CFUNCTYPE(None)
dll = CDLL('./test')
dll.c_main.argtypes = ()
dll.c_main.restype = c_int
dll.set_cb.argtypes = PLOT,
dll.set_cb.restype = None
@PLOT
def plotxy():
print("plotxy")
x = c_int.in_dll(dll,'x')
plot = PLOT.in_dll(dll,'plot')
print(f'x={x.value} (from Python)')
x.value = 7
print('calling plot from Python directly:')
plot()
print('calling c_main():')
dll.c_main()
dll.set_cb(plotxy)
print('calling plot from Python after setting callback:')
plot()
print('calling plot from C after setting callback:')
dll.c_main()
산출:
x=5 (from Python)
calling plot from Python directly:
default
calling c_main():
x=7 (from C)
default
calling plot from Python after setting callback:
plotxy
calling plot from C after setting callback:
x=7 (from C)
plotxy
전역 포인터 .contents
는 값에 액세스하는 데 사용 되므로 a POINTER(CFUNCTYPE(None))
및 using 을 사용하여 실험 plot.contents = plotxy
했지만 전역 변수를 올바르게 할당하지 않고 C가 충돌했습니다.
함수 포인터에 실제 전역 포인터를 추가하려고 시도했습니다.
API CB plot = dllplot;
API CB* pplot = &plot;
그리고 다음을 사용합니다.
PLOT = CFUNCTYPE(None)
PPLOT = POINTER(PLOT)
plot = PPLOT.in_dll(dll,'pplot')
plot.contents = plotxy
을 통해 함수를 할당 할 수 .contents
있었지만 c_main
여전히 기본 플롯 값이라고합니다. 따라서 CFUNCTYPE
함수 매개 변수 이외의 다른 용도 로 사용하는 기능은 구현되지 않은 것으로 보입니다.
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다