用ctypes替换共享库中的函数指针

2024-04-20 13:57:37 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图通过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)中定义的函数类型为CFunctionType,而C中定义的函数指针类型为_FuncPtr。虽然应用了CAUXDLL中的替换,但在调用main函数时似乎没有效果

除了阅读https://docs.python.org/3/library/ctypes.html#module-ctypes,我还发现了其他问题(例如How to use typedef in ctypespython cytpes creating callback function - Segmentation fault (core dumped)),但我找不到如何将CFunctionType(plotxy)转换为_FuncPtr

编辑

我相信这可能不是常规使用ctypes的问题。我已经成功地做到了这一点,文档中对此做了充分的解释。这个问题超出了范围。我不想执行C函数。我希望Python用Python编写的回调函数替换现有的函数指针。请注意,可以通过使用helper C函数来实现这一点(请参见https://github.com/ghdl/ghdl-cosim/blob/master/vhpidirect/shared/pycb/caux.c#L32-L40)。因此,这个问题是关于如何在没有辅助函数的情况下实现它(如果可能的话)


Tags: 函数testimport定义plotmainargsctypes
1条回答
网友
1楼 · 发布于 2024-04-20 13:57:37

ctypes访问全局变量的方法是使用in_dll,但似乎没有公开的方法来更改函数指针。我只能读它并调用它,所以我认为没有辅助函数是不可能的

下面的示例更改了int全局变量,但是CFUNCTYPE实例没有value成员来更改它。我添加了一个C助手来设置全局函数来解决这个问题,并添加了一个回调的默认值来验证在更改它之前是否正确访问了它

测试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来访问它们的值,因此我尝试使用POINTER(CFUNCTYPE(None))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作为函数参数之外的任何东西的功能似乎都没有实现

相关问题 更多 >