15.12 将函数指针转�为�调用对象¶

问题¶

你已�获得了一个被编译函数的内存地�,想将它转��一个Python�调用对象, 这样的�你就�以将它作为一个扩展函数使用了。

解决方案¶

ctypes 模��被用�创建包装任�内存地�的Python�调用对象。 下�的例�演示了怎样获�C函数的原始�底层地�,以�如何将其转�为一个�调用对象:

>>> import ctypes
>>> lib = ctypes.cdll.LoadLibrary(None)
>>> # Get the address of sin() from the C math library
>>> addr = ctypes.cast(lib.sin, ctypes.c_void_p).value
>>> addr
140735505915760

>>> # Turn the address into a callable function
>>> functype = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double)
>>> func = functype(addr)
>>> func
<CFunctionType object at 0x1006816d0>

>>> # Call the resulting function
>>> func(2)
0.9092974268256817
>>> func(0)
0.0
>>>

讨论¶

�构建一个�调用对象,你首先需�创建一个 CFUNCTYPE 实例。 CFUNCTYPE() 的第一个�数是返回类型。 接下�的�数是�数类型。一旦你定义了函数类型,你就能将它包装在一个整型内存地�上�创建一个�调用对象了。 生�的对象被当�普通的�通过 ctypes 访问的函数�使用。

本节看上去�能有点神秘,�底层一点。 但是,但是它被广泛使用于��高级代�生�技术比如�时编译,在LLVM函数库中�以看到。

例如,下�是一个使用 llvmpy 扩展的简�例�,用�构建一个�的�集函数,获�它的函数指针, 并将其转�为一个Python�调用对象。

>>> from llvm.core import Module, Function, Type, Builder
>>> mod = Module.new('example')
>>> f = Function.new(mod,Type.function(Type.double(), \
                     [Type.double(), Type.double()], False), 'foo')
>>> block = f.append_basic_block('entry')
>>> builder = Builder.new(block)
>>> x2 = builder.fmul(f.args[0],f.args[0])
>>> y2 = builder.fmul(f.args[1],f.args[1])
>>> r = builder.fadd(x2,y2)
>>> builder.ret(r)
<llvm.core.Instruction object at 0x10078e990>
>>> from llvm.ee import ExecutionEngine
>>> engine = ExecutionEngine.new(mod)
>>> ptr = engine.get_pointer_to_function(f)
>>> ptr
4325863440
>>> foo = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double, ctypes.c_double)(ptr)

>>> # Call the resulting function
>>> foo(2,3)
13.0
>>> foo(4,5)
41.0
>>> foo(1,2)
5.0
>>>

并�是说在这个层�犯了任何错误就会导致Python解释器挂掉。 �记得的是你是在直接跟机器级别的内存地�和本地机器�打交�,而�是Python函数。