Cython equivalent of c define #define myfunc(node x,…) SetNode(x.getattributeNode(),__VA_ARGS__)

99封情书 提交于 2019-11-30 23:47:20

I don't think it's easily done though Cython (the problem is telling Cython what type conversions to do for an arbitrary number of arguments). The best I can suggest is to use the standard library ctypes library for this specific case and wrap the rest in Cython.

For the sake of an example, I've used a very simple sum function. va_sum.h contains:

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, ...);
*/

and va_sum.c contains:

#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;
}

I've written it so it adds everything to a field in a structure just to demonstrate that you can pass pointers to structures without too much trouble.

The Cython file is:

# 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

You need to compile your va_sum.c together with your Cython file (e.g. by adding sources = ['cython_file.pyx','va_sum.c'] in setup.py)

Ctypes is probably a bit slower than Cython (I think there's a reasonable overhead on each call), and it's odd to mix them, but this should at least let you write the main wrapper in Cython, and use ctypes to get round the specific limitation.

This is probably not the proper answer, since I am not sure I understand the question fully. I would have replied in a comment, but the code formatting is too poor. In Python the functions sum and len are available:

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)

outputs:

len = 3
sum = 10.0
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!