你有些混淆了:
弱符号是在链接时处理的,而你正在运行时“操作”。链接器不可能知道你的
Python回调函数。虽然这不是官方来源,请查看
[维基百科]: 弱符号获取更多详细信息。
此外,
[Python.Docs]: ctypes - 用于Python的外部函数库。
这里有一个小演示。
libtest00.c:
#include <stdio.h>
int __attribute__((weak)) custom_callback(int a) {
printf("Callback not redefined, a = %d\n", a);
return 0;
}
int multiply(int a, int b) {
custom_callback(a);
return a * b;
}
callback00.c:
#include <stdio.h>
int custom_callback(int a) {
printf("C callback redefinition, a = %d\n", a);
return 0;
}
code00.py:
import sys
import ctypes as ct
DLL_NAME = "./libtest000.so"
CALLBACKFUNCTYPE = ct.CFUNCTYPE(ct.c_int, ct.c_int)
def py_callback_func(a):
print("PY callback redefinition", a)
return 0
def main(*argv):
dll_name = argv[0] if argv else DLL_NAME
dll = ct.CDLL(dll_name)
multiply = dll.multiply
multiply.argtypes = [ct.c_int, ct.c_int]
multiply.restype = ct.c_int
res = multiply(5, 10)
print("{:} returned {:d}".format(multiply.__name__, res))
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)
Output:
[cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q055357490]> ~/sopr.sh
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***
[064bit prompt]> ls
callback00.c code00.py code01.py libtest00.c libtest01.c
[064bit prompt]> gcc -o libtest000.so -fPIC -shared libtest00.c
[064bit prompt]> gcc -o libtest001.so -fPIC -shared libtest00.c callback00.c
[064bit prompt]> ls
callback00.c code00.py code01.py libtest00.c libtest000.so libtest001.so libtest01.c
[064bit prompt]>
[064bit prompt]> python code00.py ./libtest000.so
Python 3.8.5 (default, Jan 27 2021, 15:41:15) [GCC 9.3.0] 64bit on linux
Callback not redefined, a = 5
multiply returned 50
Done.
[064bit prompt]>
[064bit prompt]> python code00.py ./libtest001.so
Python 3.8.5 (default, Jan 27 2021, 15:41:15) [GCC 9.3.0] 64bit on linux
C callback redefinition, a = 5
multiply returned 50
Done.
如上所述,
__attribute__((weak))的效果是可见的。此外,关于已注释的代码(试图设置
.dll函数):它只在
CTypes代理级别操作,不会改变
.dll代码(这将是荒谬的)。
至于你的问题,有几种解决方法。以下是其中一种使用指针持有此类回调函数的方法。该指针可以由一个setter函数(也被导出)从外部设置。
libtest01.c:
#include <stdio.h>
#define EXEC_CALLBACK(X) \
if (pCallback) { \
pCallback(X); \
} else { \
fallbackCallback(X); \
}
typedef int (*CustomCallbackPtr)(int a);
static CustomCallbackPtr pCallback = NULL;
static int fallbackCallback(int a) {
printf("Callback not redefined, a = %d\n", a);
return 0;
}
int multiply(int a, int b) {
EXEC_CALLBACK(a);
return a * b;
}
void setCallback(CustomCallbackPtr ptr) {
pCallback = ptr;
}
code01.py:
import sys
import ctypes as ct
DLL_NAME = "./libtest01.so"
CALLBACKFUNCTYPE = ct.CFUNCTYPE(ct.c_int, ct.c_int)
def py_callback_func(a):
print("PY callback redefinition", a)
return 0
def main(*argv):
dll = ct.CDLL(DLL_NAME)
multiply = dll.multiply
multiply.argtypes = [ct.c_int, ct.c_int]
multiply.restype = ct.c_int
set_callback = dll.setCallback
set_callback.argtypes = [CALLBACKFUNCTYPE]
multiply(5, 10)
set_callback(CALLBACKFUNCTYPE(py_callback_func))
multiply(5, 10)
set_callback(ct.cast(ct.c_void_p(), CALLBACKFUNCTYPE))
multiply(5, 10)
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)
Output:
[064bit prompt]> ls
callback00.c code00.py code01.py libtest00.c libtest000.so libtest001.so libtest01.c
[064bit prompt]> gcc -o libtest01.so -fPIC -shared libtest01.c
[064bit prompt]>
[064bit prompt]> ls
callback00.c code00.py code01.py libtest00.c libtest000.so libtest001.so libtest01.c libtest01.so
[064bit prompt]>
[064bit prompt]> python code01.py
Python 3.8.5 (default, Jan 27 2021, 15:41:15) [GCC 9.3.0] 64bit on linux
Callback not redefined, a = 5
PY callback redefinition 5
Callback not redefined, a = 5
Done.