Cython等效于c定义#define myfunc(node x,...)SetNode(x.getattributeNode(),__ VA_ARGS__)

时间:2015-06-20 12:23:46

标签: c cython wrapping variadic-functions

Cython等同于c define

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

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

这是一个解决这种问题的例子

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替代方案而不是上面的例子 因为我有一个C-API,它有一个SetNode函数,它接受可变数量的参数,我想把它包装在cython中并从python中调用

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

这里Node是一个在cython中定义的类,它将c stuct保存为属性,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,那就太棒了。

2 个答案:

答案 0 :(得分:2)

我不认为它很容易通过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来绕过特定限制。

答案 1 :(得分:1)

这可能不是正确的答案,因为我不确定我完全理解这个问题。我会在评论中回复,但代码格式太差了。 在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