【问题标题】:Cython: How to make an array of functions with different signaturesCython:如何制作具有不同签名的函数数组
【发布时间】:2019-12-19 14:22:35
【问题描述】:

在 Cython 中,我想存储一个函数数组,但这些函数并不都有相同的签名。具体来说,有些有两个参数,有些有三个。

我定义了以下内容:

ctypedef long (*func2param)(long param1, long param2)
ctypedef long (*funct3param)(long param1, long param2, long param3)

但是,即使我只有一个这样的签名,我仍然不确定如何让它发挥作用。尝试将 cdef 函数分配给上述类型之一的数组会给我:

func_array2[i] = func_list[i][FUNCTION]

Cannot convert Python object to 'func2param'

尝试施放:

func_array2[i] = <func2param>func_list[i][FUNCTION]

Python objects cannot be cast to pointers of primitive types

【问题讨论】:

  • 您想知道为什么您的代码无法构建 - 那么func_listfunc_array2 是什么?或者您想知道“如何制作具有不同签名的函数数组?” - 有一些方法可以实现这一点(最容易将所有内容转换为uintptr_t),但取决于你想对数组中的这些指针做什么,并不是所有的都对你有帮助——这里还需要更多信息。还有一个问题,你是否应该做,你想做什么......
  • 是的,我想知道“如何制作具有不同签名的函数数组?”根据我上面的解释,我有两个签名,我想要一个可以存储这两种函数的数组。为什么它没有构建的示例只是我尝试做对我来说直观的事情以及我得到的错误。

标签: python python-3.x cython function-pointers


【解决方案1】:

我想不出你可以用一组未知类型的函数指针做些什么有用的东西——调用它们是不安全的(因为你不知道签名)而且真的没有别的东西了与函数指针有关。因此,您至少需要找到某种方法来存储它是什么类型的指针。

一种选择是存储一个包含两个指针的结构:

cdef struct FuncPtrStruct:
    func2param f2p
    func3param f3p

然后您将一个设置为NULL,存储在另一个中,并且只调用非NULL 一个数组规范将类似于cdef FuncPtrStruct array[10]

但是,我可能会使用 C Union 来将两个指针存储在同一个内存位置(+ 一个枚举来标识类型)。这需要更多的设置工作(您需要一个enum 来定义union 的类型,union 本身,以及一个包含enumunionstruct);但是优点是您可以添加更多不同类型的函数指针而无需使用更多内存(对于“两种类型”的情况,内存可能相等):

# cpdef if you want to use the enum from Python too
cdef enum FuncType:
    twoArg, threeArg

cdef union FuncPtrUnion:
    func2param f2p
    func3param f3p

cdef struct FuncPtrHolder:
    FuncType type_
    FuncPtrUnion value

只是为了说明你将如何使用它:

cdef long f(long x1, long x2):
    print("f",x1,x2)
    return 0

cdef long g(long x1, long x2, long x3):
    print("g",x1,x2,x3)
    return 1

def example():
    cdef FuncPtrHolder fArray[10]
    for i in range(10):
        if i%2:
            fArray[i].type_ = twoArg
            fArray[i].value.f2p = &f
        else:
            fArray[i].type_ = threeArg
            fArray[i].value.f3p = &g
    # use
    for i in range(10):
        if fArray[i].type_ == twoArg:
            fArray[i].value.f2p(i,i+1)
        elif fArray[i].type_ == threeArg:
            fArray[i].value.f3p(i,i+1,i+2)

看起来您的代码中有一些 list 的 Python 对象,这就是您遇到编译错误的原因。没有代码就不可能知道为什么,但我认为 Cython 可以自动为 cdef 函数生成 Python 包装器,所以我猜你已经以某种方式列出了这些包装器。如果您想处理 FuncPtrHolder 的 Python 列表(或以其他方式从 Python 中使用它),您必须将其包装在 cdef class 中。


在这种情况下,我首选的更简单的解决方案可能是只使用func3param 并让“双参数”函数简单地忽略第三个参数。这意味着所有函数指针都将具有一致的签名,这对我来说更有意义。

【讨论】:

  • 您在这里有一些有趣的想法,我想尝试一下。当然,有一个未使用的参数会起作用,但我不想那样做,因为以后添加更多函数需要我总是更改所有以前的函数以使用最大数量的参数。出于同样的原因,我也不想使用结构。
  • 我不清楚你为什么说“我想不出任何有用的东西可以用一组未知类型的函数指针做”我的问题是关于如何用两种已知类型处理这个问题.我将存储参数的数量,然后根据该数量切换到使用正确的签名。所以我会知道类型。我只是不知道如何让它转换为所需的类型。我会查看您对上述内容的解释,然后重试。
  • @BruceNielson 我认为我们可能同意“未知类型的函数指针数组”,因为您说您计划存储参数的数量。我只是建议将其与功能点一起存储的方法,但您当然可以将其存储在不同的数组中。
  • 您传递(int num_args, long* array) 的接口会代替“未使用的参数”来支持任意数量的参数吗?
  • 我仍然想知道如何处理一组函数,但是,为了我自己的学习。 (以及为什么铸造不起作用。)