将函数指针转为可调用
好了,获得了编译函数的内存地址,但是如何将其转换为可以用作扩展的Python callable。答案是使用ctypes 模块,它可以创建Python可调用并可以包装任意内存地址。
下面的代码显示了如何获取 C函数的原始低级地址以及如何将其转回可调用对象。
代码#1:
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
print ("addr : ", addr)
输出 :
addr : 140735505915760
代码 #2 :将地址转换为可调用函数
functype = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double)
func = functype(addr)
print ("Function : ", func)
输出 :
Function :
代码#3:调用结果函数
print ("func(2) : ", func(2))
print ("func(0) : ", func(0))
输出 :
func(2) : 0.9092974268256817
func(0) : 0.0
必须首先创建一个CFUNCTYPE实例以使其可调用。 CFUNCTYPE()
的第一个参数是返回类型。下一个参数是参数的类型。定义函数类型后,将其包裹在整数内存地址周围以创建可调用对象。结果对象的使用与通过ctypes访问的任何普通函数一样。
程序和库越来越普遍地利用诸如实时编译之类的高级代码生成技术,正如LLVM等库中所发现的那样(LLVM 本身不是首字母缩写词;它是项目的全名。)
下面的代码使用llvmpy扩展来制作汇编函数,获取指向它的函数指针,并将其转换为Python可调用的。
代码#4:
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)
输出 :
代码#5:
from llvm.ee import ExecutionEngine
engine = ExecutionEngine.new(mod)
ptr = engine.get_pointer_to_function(f)
ptr
输出 :
4325863440
代码#6:调用结果函数
foo = ctypes.CFUNCTYPE(ctypes.c_double,
ctypes.c_double,
ctypes.c_double)(ptr)
print (foo(2, 3))
print ("\n", foo(4, 5))
print ("\n", foo(1, 2))
输出 :
13.0
41.0
5.0