📜  将函数指针转为可调用

📅  最后修改于: 2022-05-13 01:54:53.920000             🧑  作者: Mango

将函数指针转为可调用

好了,获得了编译函数的内存地址,但是如何将其转换为可以用作扩展的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