|
5 | 5 | ----------
|
6 | 6 | 问题
|
7 | 7 | ----------
|
8 |
| -You have (somehow) obtained the memory address of a compiled function, but want |
9 |
| -to turn it into a Python callable that you can use as an extension function. |
| 8 | +你已经获得了一个被编译函数的内存地址,想将它转换成一个Python可调用对象, |
| 9 | +这样的话你就可以将它作为一个扩展函数使用了。 |
10 | 10 |
|
11 | 11 | |
|
12 | 12 |
|
13 | 13 | ----------
|
14 | 14 | 解决方案
|
15 | 15 | ----------
|
16 |
| -The ctypes module can be used to create Python callables that wrap around arbitrary |
17 |
| -memory addresses. The following example shows how to obtain the raw, low-level ad‐ |
18 |
| -dress of a C function and how to turn it back into a callable object: |
| 16 | +``ctypes`` 模块可被用来创建包装任意内存地址的Python可调用对象。 |
| 17 | +下面的例子演示了怎样获取C函数的原始、底层地址,以及如何将其转换为一个可调用对象: |
19 | 18 |
|
20 |
| ->>> import ctypes |
21 |
| ->>> lib = ctypes.cdll.LoadLibrary(None) |
22 |
| ->>> # Get the address of sin() from the C math library |
23 |
| ->>> addr = ctypes.cast(lib.sin, ctypes.c_void_p).value |
24 |
| ->>> addr |
25 |
| -140735505915760 |
| 19 | +:: |
26 | 20 |
|
27 |
| ->>> # Turn the address into a callable function |
28 |
| ->>> functype = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double) |
29 |
| ->>> func = functype(addr) |
30 |
| ->>> func |
31 |
| -<CFunctionType object at 0x1006816d0> |
| 21 | + >>> import ctypes |
| 22 | + >>> lib = ctypes.cdll.LoadLibrary(None) |
| 23 | + >>> # Get the address of sin() from the C math library |
| 24 | + >>> addr = ctypes.cast(lib.sin, ctypes.c_void_p).value |
| 25 | + >>> addr |
| 26 | + 140735505915760 |
32 | 27 |
|
33 |
| ->>> # Call the resulting function |
34 |
| ->>> func(2) |
35 |
| -0.9092974268256817 |
36 |
| ->>> func(0) |
37 |
| -0.0 |
38 |
| ->>> |
| 28 | + >>> # Turn the address into a callable function |
| 29 | + >>> functype = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double) |
| 30 | + >>> func = functype(addr) |
| 31 | + >>> func |
| 32 | + <CFunctionType object at 0x1006816d0> |
| 33 | + |
| 34 | + >>> # Call the resulting function |
| 35 | + >>> func(2) |
| 36 | + 0.9092974268256817 |
| 37 | + >>> func(0) |
| 38 | + 0.0 |
| 39 | + >>> |
39 | 40 |
|
40 | 41 | |
|
41 | 42 |
|
42 | 43 | ----------
|
43 | 44 | 讨论
|
44 | 45 | ----------
|
45 |
| -To make a callable, you must first create a CFUNCTYPE instance. The first argument to |
46 |
| -CFUNCTYPE() is the return type. Subsequent arguments are the types of the arguments. |
47 |
| -Once you have defined the function type, you wrap it around an integer memory address |
48 |
| -to create a callable object. The resulting object is used like any normal function accessed |
49 |
| -through ctypes. |
50 |
| -This recipe might look rather cryptic and low level. However, it is becoming increasingly |
51 |
| -common for programs and libraries to utilize advanced code generation techniques like |
52 |
| -just in-time compilation, as found in libraries such as LLVM. |
53 |
| -For example, here is a simple example that uses the llvmpy extension to make a small |
54 |
| -assembly function, obtain a function pointer to it, and turn it into a Python callable: |
| 46 | +要构建一个可调用对象,你首先需要创建一个 ``CFUNCTYPE`` 实例。 |
| 47 | +``CFUNCTYPE()`` 的第一个参数是返回类型。 |
| 48 | +接下来的参数是参数类型。一旦你定义了函数类型,你就能将它包装在一个整型内存地址上来创建一个可调用对象了。 |
| 49 | +生成的对象被当做普通的可通过 ``ctypes`` 访问的函数来使用。 |
| 50 | + |
| 51 | +本节看上去可能有点神秘,偏底层一点。 |
| 52 | +但是,但是它被广泛使用于各种高级代码生成技术比如即时编译,在LLVM函数库中可以看到。 |
| 53 | + |
| 54 | +例如,下面是一个使用 ``llvmpy`` 扩展的简单例子,用来构建一个小的聚集函数,获取它的函数指针, |
| 55 | +并将其转换为一个Python可调用对象。 |
| 56 | + |
| 57 | +:: |
55 | 58 |
|
56 |
| ->>> from llvm.core import Module, Function, Type, Builder |
57 |
| ->>> mod = Module.new('example') |
58 |
| ->>> f = Function.new(mod,Type.function(Type.double(), \ |
59 |
| - [Type.double(), Type.double()], False), 'foo') |
60 |
| ->>> block = f.append_basic_block('entry') |
61 |
| ->>> builder = Builder.new(block) |
62 |
| ->>> x2 = builder.fmul(f.args[0],f.args[0]) |
63 |
| ->>> y2 = builder.fmul(f.args[1],f.args[1]) |
64 |
| ->>> r = builder.fadd(x2,y2) |
65 |
| ->>> builder.ret(r) |
66 |
| -<llvm.core.Instruction object at 0x10078e990> |
67 |
| ->>> from llvm.ee import ExecutionEngine |
68 |
| ->>> engine = ExecutionEngine.new(mod) |
69 |
| ->>> ptr = engine.get_pointer_to_function(f) |
70 |
| ->>> ptr |
71 |
| -4325863440 |
72 |
| ->>> foo = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double, ctypes.c_double)(ptr) |
| 59 | + >>> from llvm.core import Module, Function, Type, Builder |
| 60 | + >>> mod = Module.new('example') |
| 61 | + >>> f = Function.new(mod,Type.function(Type.double(), \ |
| 62 | + [Type.double(), Type.double()], False), 'foo') |
| 63 | + >>> block = f.append_basic_block('entry') |
| 64 | + >>> builder = Builder.new(block) |
| 65 | + >>> x2 = builder.fmul(f.args[0],f.args[0]) |
| 66 | + >>> y2 = builder.fmul(f.args[1],f.args[1]) |
| 67 | + >>> r = builder.fadd(x2,y2) |
| 68 | + >>> builder.ret(r) |
| 69 | + <llvm.core.Instruction object at 0x10078e990> |
| 70 | + >>> from llvm.ee import ExecutionEngine |
| 71 | + >>> engine = ExecutionEngine.new(mod) |
| 72 | + >>> ptr = engine.get_pointer_to_function(f) |
| 73 | + >>> ptr |
| 74 | + 4325863440 |
| 75 | + >>> foo = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double, ctypes.c_double)(ptr) |
73 | 76 |
|
74 |
| ->>> # Call the resulting function |
75 |
| ->>> foo(2,3) |
76 |
| -13.0 |
77 |
| ->>> foo(4,5) |
78 |
| -41.0 |
79 |
| ->>> foo(1,2) |
80 |
| -5.0 |
81 |
| ->>> |
| 77 | + >>> # Call the resulting function |
| 78 | + >>> foo(2,3) |
| 79 | + 13.0 |
| 80 | + >>> foo(4,5) |
| 81 | + 41.0 |
| 82 | + >>> foo(1,2) |
| 83 | + 5.0 |
| 84 | + >>> |
82 | 85 |
|
83 |
| -It goes without saying that doing anything wrong at this level will probably cause the |
84 |
| -Python interpreter to die a horrible death. Keep in mind that you’re directly working |
85 |
| -with machine-level memory addresses and native machine code—not Python |
86 |
| -functions. |
| 86 | +并不是说在这个层面犯了任何错误就会导致Python解释器挂掉。 |
| 87 | +要记得的是你是在直接跟机器级别的内存地址和本地机器码打交道,而不是Python函数。 |
0 commit comments