Ctypes: Parse Arguments and Return Results

谁说胖子不能爱 提交于 2021-02-16 15:36:26

问题


I am experimenting for the first time with ctypes in Python. I have written a C file which contains methods to make calculations so as to rotate points (and curves). My code is:

#include "math.h"
double * rotatePoint(double P[2], double angle) {
static double Q[2];
Q[0] = P[0] * cos(angle * M_PI/180);
Q[1] = P[1] * sin(angle * M_PI/180);
return Q;
}

I have compiled it with GCC

gcc -lm -shared lib.c -o lib.so

In my Python Code:

import ctypes
lib = ctypes.CDLL('lib.so')

def cRotatePoint(P):
   #how I parse P in order to be understood by ctypes?
   #.....
   lib.rotatePoint(P) 
   #how can I return the array back?
   #....
   return P

Can you please help me on these:

  1. Parse the double[] from Python with ctypes

  2. Convert the result into double[] in Python and return it

Thanks


回答1:


So the trick lies in making sure that ctypes knows what the argument types are for your function.

The C side of things is fine as is, but in the Python side you need to specify the restype and argtypes for your function:

import ctypes
lib = ctypes.CDLL('lib.so')

# let ctypes know the argument types 
lib.rotatePoint.argtypes = [ctypes.POINTER(ctypes.c_double),ctypes.c_double]

# let ctypes know the return type
lib.rotatePoint.restype = ctypes.POINTER(ctypes.c_double)

def c_rotate_point(p,angle):

    # convert arguments to appropriate ctypes type
    p = (ctypes.c_double*2)(*p)
    angle = ctypes.c_double(angle)

    # call the function
    retval = lib.rotatePoint(p,angle)

    # here, retval has type LP_c_double object
    # indexing this will dereference the underlying pointer giving the results
    print retval[0],retval[1]


if __name__ == "__main__":
    c_rotate_point((0.2,0.3),0.4)

For these values, I get the output:

0.199995126141 0.00209437808939



回答2:


@ebarr provided a complete code example. Here's a slight variation.

To allow calling the function from multiple threads, don't use static variables:

#include "math.h"
void rotatePoint(double P[2], double angle, double Q[2]) {
  Q[0] = P[0] * cos(angle * M_PI/180);
  Q[1] = P[1] * sin(angle * M_PI/180);
}

You could pass double P[2] as array type c_double * 2:

import ctypes
from collections import namedtuple

ArrayType = ctypes.c_double * 2
Point = namedtuple('Point', 'x y')

lib = ctypes.CDLL('lib.so')    
lib.rotatePoint.argtypes = [ArrayType, ctypes.c_double, ArrayType]
lib.rotatePoint.restype = None # void

def rotate_point(p, angle):
    retval = ArrayType()
    lib.rotatePoint(ArrayType(*p), angle, retval)
    return Point(*retval)

if __name__ == "__main__":
    print(rotate_point((0.2,0.3), 0.4))

Notice that you don't need to convert angle (a scalar type) explicitely.



来源:https://stackoverflow.com/questions/22589487/ctypes-parse-arguments-and-return-results

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