【问题标题】:Cython equivalent of c define #define myfunc(node x,...) SetNode(x.getattributeNode(),__VA_ARGS__)Cython 等效于 c 定义 #define myfunc(node x,...) SetNode(x.getattributeNode(),__VA_ARGS__)
【发布时间】:2015-06-20 12:23:45
【问题描述】:

Cython 等价于 c 定义

#define myfunc(Node x,...) SetNode(x.getattributeNode(),__VA_ARGS__)

我有一个 c api SetNode,它的第一个参数是结构类型节点的节点和 N 个变量(N 是从 0-N 的变量号)

这是一个解决此类问题的 c 示例

exampleAPI.c

#include<stdarg.h>
float sumN(int len,...){
    va_list argp;
    int i;
    float s=0;
    va_start(argp,len);
    for(i=0;i<len;i++){
       s+=va_arg(argp,int);
    }
    va_end(argp);
}

exampleAPI.h

#include<stdarg.h>
float sumN(int len,...)

examplecode.c

#include<stdarg.h>
#include"exampleAPI.h"

int len(float first,...){
    va_list argp;
    int i=1;
    va_start(argp,first);
    while(1){
       if(va_arg(argp,float)==NULL){
            return i
       }
       else{
            i++;
       }
    }
    va_end(argp);
}

#define sum(...) sumN(len(__VA_ARGS__),__VA_ARGS__)

正在调用

sum(1,2,3,4);

将返回 10.000000

sum(1.5,6.5);

将返回 8.00000

我需要一个 cython 替代 bellow c 定义而不是上面的例子 因为我有一个 C-API,它具有 SetNode 函数,它接受可变数量的参数,我想将它包装在 cython 中并从 python 调用

 #define myfunc(Node x,...) SetNode(x.getattributeNode(),__VA_ARGS__)

这里 Node 是在 cython 中定义的一个类,它拥有一个 c 结构作为属性,getattributeNode() 是 Node 类的一个函数,它返回需要传递给 C-API 的 c 结构。

cdef extern "Network.h":

    ctypedef struct node_bn:
         pass

    node_bn* SetNode(node_bn* node,...)


cdef class Node:

    cdef node_bn *node

    cdef getattributeNode(self):
        return self.node     

    def setNode(self,*arg):
        self.node=SetNode(self.node,*arg) # Error cannot convert python objects to c type

我尝试过的替代方法

cdef extern from "stdarg.h":
   ctypedef struct va_list:
      pass
   ctypedef struct fake_type:
      pass
   void va_start(va_list, void* arg)
   void* va_arg(va_list, fake_type)
   void va_end(va_list)
   fake_type int_type "int" 

cdef extern "Network.h":

    ctypedef struct node_bn:
         pass

    node_bn* VSetNode(node_bn* node,va_list argp)


cdef class Node:

    cdef node_bn *node

    cdef getattributeNode(self):
        return self.node     

    cpdef _setNode(self,node_bn *node,...):
        cdef va_list agrp
        va_start(va_list, node)       
        self.node=VSetNode(node,argp) 
        va_end(va_list)

    def setNode(self,*arg):
        self._setNode(self.node,*arg) 

当参数列表为空时工作正常

n = Node()
n.setNode() #This works
n.SetNode("top",1) # error takes exactly one argument given 3 in  self._setNode(self.node,*arg)

如果有人可以建议与它等效的 cython,那就太好了。

【问题讨论】:

    标签: c cython word-wrap variadic-functions


    【解决方案1】:

    我认为虽然 Cython 并不容易做到(问题是告诉 Cython 对任意数量的参数进行什么类型转换)。我能建议的最好方法是针对这种特定情况使用标准库 ctypes 库,并将其余部分包装在 Cython 中。

    为了举例,我使用了一个非常简单的求和函数。 va_sum.h 包含:

    typedef struct { double val; } node_bn;
    
    node_bn* sum_va(node_bn* node,int len, ...);
    /* on windows this must be:
    __declspec(dllexport) node_bn* sum_va(node_bn* node,int len, ...);
    */
    

    并且 va_sum.c 包含:

    #include <stdarg.h>
    
    #include "va_sum.h"
    
    node_bn* sum_va(node_bn* node,int len, ...) {
        int i;
        va_list vl;
        va_start(vl,len);
        for (i=0; i<len; ++i) {
            node->val += va_arg(vl,double);
        }
        va_end(vl);
        return node;
    }
    

    我已经编写了它,因此它将所有内容添加到结构中的字段中,只是为了证明您可以轻松地将指针传递给结构。

    Cython 文件是:

    # definition of a structure
    cdef extern from "va_sum.h":   
    
        ctypedef struct node_bn:
            double val;
    
    # obviously you'll want to wrap things in terms of Python accessible classes, but this atleast demonstrates how it works
    def test_sum(*args):
        cdef node_bn input_node;
        cdef node_bn* output_node_p;
        input_node.val = 5.0 # create a node, and set an initial value
    
        from ctypes import CDLL, c_double,c_void_p
        import os.path
        # load the Cython library with ctypes to gain access to the "sum_va" function
        # Assuming you've linked it in when you build the Cython module
        full_path = os.path.realpath(__file__)
        this_file_library = CDLL(full_path)
    
        # I treat all my arguments as doubles - you may need to do
        # something more sophisticated, but the idea is the same:
        # convert them to the c-type the function is expecting
        args = [ c_double(arg) for arg in args ]
        sum_va = this_file_library.sum_va
        sum_va.restype = c_void_p # it returns a pointer
    
        # pass the pointers as a void pointer
        # my c compiler warns me if I used int instead of long
        # but which integer type you have to use is likely system dependent
        # and somewhere you have to be careful
        output_node_p_as_integer = sum_va(c_void_p(<long>&input_node),len(args),
                               *args)
    
        # unfortunately getting the output needs a two stage typecast - first to a long, then to a pointer
        output_node_p = <node_bn*>(<long>(output_node_p_as_integer))
        return output_node_p.val
    

    您需要将您的 va_sum.c 与您的 Cython 文件一起编译(例如,通过 setup.py 中的adding sources = ['cython_file.pyx','va_sum.c']

    Ctypes 可能比 Cython 慢一点(我认为每次调用都有合理的开销),混合它们很奇怪,但这至少应该让您在 Cython 中编写主包装器,并使用 ctypes 来绕过具体限制。

    【讨论】:

      【解决方案2】:

      这可能不是正确的答案,因为我不确定我是否完全理解了这个问题。我会在评论中回复,但代码格式太差了。 在 Python 中,函数 sumlen 可用:

      def my_len(*args):
          return len(args)
      
      def my_sum(*args):
          return sum(args)
      
      print "len =", my_len("hello", 123, "there")
      print "sum =", my_sum(6.5, 1.5, 2.0)
      

      输出:

      len = 3
      sum = 10.0
      

      【讨论】:

      • 我认为您的答案的重要一点是 python(以及 Cython)允许通过 *args 语法(而不是我猜的本机 sumlen只是例子)。但是,我也不太明白这个问题......
      • sum 函数和 len 函数只是展示如何在 c it-self 中包装变量 arg 函数的示例,因为实际需要是 cython #define myfunc(node x,...) SetNode(getatributeNode(x),VA_ARGS)
      猜你喜欢
      • 2014-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多