首页 > 解决方案 > 如何动态调用将python方法分配给从共享库导出的C指针函数

问题描述

我想解决以下问题。共享库中的某些 C 代码将调用一个指针函数,该函数是共享库的导出符号

#include <stdio.h>

typedef void (*pCallBack)(const char *);

pCallBack one_pointer;

void errorPrint(const char * str)
{
   printf("my address = %p", one_pointer);
   fflush(stdout);
   (*one_pointer)(str);
}

让我们将该库编译为共享库

gcc -fPIC -g -shared -o  call_back.so call_back.c

现在我想从 Python 调用它,动态地重新定义pCallBack指针的实现

import ctypes
import sys

def myCallBack(str):
    print("inside python" % str)
    sys.stdout.flush()

libcallback = ctypes.CDLL("./call_back.so")
pointer_CallBack = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_char_p)
libcallback.one_pointer = pointer_CallBack(myCallBack)
libcallback.errorPrint(b"one_string")

不幸的是,似乎无法重新定义导出的数据指针。我也尝试in_dll了功能,ctypes但它不能解决问题

当尝试运行我的 Python 脚本时,我得到以下信息

LD_PRELOAD="./call_back.so" python3 error.py
my address = (nil)[1]    2871869 segmentation fault (core dumped) 

有谁知道这个问题?

标签: pythonshared-librariesctypes

解决方案


清单[Python.Docs]:ctypes - Python 的外部函数库

使用ct.in_dll修改.dll中的变量适用于简单类型 ( int )。我不知道如何(干净地)为(函数)指针做到这一点。因此,您需要一个简单地设置回调的新函数。

还有一些错误:

这是一个工作示例。

dll00.c

#include <stdio.h>

#if defined(_WIN32)
#  define DLL00_EXPORT_API __declspec(dllexport)
#else
#  define DLL00_EXPORT_API
#endif

typedef void (*CallbackFuncPtr)(const char*);

#if defined(__cplusplus)
extern "C" {
#endif

DLL00_EXPORT_API void setCallback(CallbackFuncPtr cb);
DLL00_EXPORT_API void errorPrint(const char *str);

#if defined(__cplusplus)
}
#endif


CallbackFuncPtr callback = NULL;


void setCallback(CallbackFuncPtr cb) {
    callback = cb;
}

void errorPrint(const char * str) {
    printf("C - Callback: %p\n", callback);
    fflush(stdout);
    if (callback) {
        (*callback)(str);
    }
    printf("C - done\n");
}

代码00.py

#!/usr/bin/env python

import sys
import ctypes as ct


DLL_NAME = "./dll00.so"

CallBackType = ct.CFUNCTYPE(None, ct.c_char_p)


def callback_func(s):
    print("PY - callback arg: {:}".format(s))
    sys.stdout.flush()


def main(*argv):
    dll00 = ct.CDLL(DLL_NAME)
    setCallback = dll00.setCallback
    setCallback.argtypes = (CallBackType,)
    setCallback.restype = None
    errorPrint = dll00.errorPrint
    errorPrint.argtypes = (ct.c_char_p,)
    errorPrint.restype = None

    cb = CallBackType.in_dll(dll00, "callback")
    print("Original callback: {:}".format(cb))

    callback = CallBackType(callback_func)
    print("New callback: {:}\n".format(callback))

    errorPrint(b"Before setting callback")
    setCallback(callback)
    errorPrint(b"After setting callback")


if __name__ == "__main__":
    print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.")
    sys.exit(rc)

输出

[cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q067079630]> ~/sopr.sh
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***

[064bit prompt]> ls
code00.py  dll00.c
[064bit prompt]> gcc -shared -fPIC -o dll00.so dll00.c
[064bit prompt]> ls
code00.py  dll00.c  dll00.so
[064bit prompt]>
[064bit prompt]> python code00.py
Python 3.8.5 (default, Jan 27 2021, 15:41:15) [GCC 9.3.0] 64bit on linux

Original callback: <CFunctionType object at 0x7f07ef740ac0>
New callback: <CFunctionType object at 0x7f07ef740e80>

C - Callback: (nil)
C - done
C - Callback: 0x7f07efffc010
PY - callback arg: b'After setting callback'
C - done

Done.

推荐阅读