Python ctypes:通过引用错误传递参数

时间:2018-09-06 13:13:30

标签: python ctypes

我有一个C ++函数,希望您在Python 2.7.12中调用,如下所示:

extern "C" {
    double* myfunction(double* &y, double* &z, int &n_y, int &n_z, int a, int b)
    {
        vector<double> _x;
        vector<double> _y;
        vector<double> _z;

        // Call some external C++ function
        cpp_function(_x, _y, _z, a, b);

        // Convert vectors back to arrays
        double* x = &_x[0]; // or x = _x.data();
        y = &_y[0];
        z = &_z[0];
        n_y = static_cast<int>(_y.size());
        n_z = static_cast<int>(_z.size());
        return x;
    }
}

基本上,此函数将两个整数a,b(以及为清晰起见而省略的其他一些数据)作为输入,并进行一些计算,然后将结果放入两个数组y, z中,并将其各自的大小放入{ {1}},并返回大小为n_y, n_z的数组x

在将此函数构建到共享库a*b之后,我在Python中按如下方式调用它:

myfunction.so

运行此脚本时出现错误:

  

ctypes.ArgumentError:参数3::错误   输入

因此from ctypes import * libc = CDLL('myfunction.so') myfunction = libc.myfunction myfunction.restype = POINTER(c_double) myfunction.argtypes = [POINTER(c_double), POINTER(c_double), c_int, c_int, c_int, c_int] y = POINTER(c_double)() z = POINTER(c_double)() n_y = c_int() n_z = c_int() a = 18 b = 18 x = myfunction(byref(y), byref(z), byref(n_y), byref(n_z), c_int(a), c_int(b)) 的{​​{1}}类型不正确。我应该放什么呢?

非常感谢您的帮助!


更新

按照@GiacomoAlzetta和@CristiFati的建议,我将代码更改为使用指针而不是通过引用传递,如下所示。

({c_intn_y相似,所以我省略y

z

现在在C ++中,我按以下方式调用上述函数:

z

有效。在Python中:

extern "C" {
    double* myfunction(double** y, int* n_y, int a, int b)
    {
        vector<double> _x;
        vector<double> _y;

        // Call some external C++ function
        cpp_function(_x, _y, a, b);

        // Convert vectors back to arrays
        double* x = &_x[0]; // or x = _x.data();
        *y = &_y[0];
        *n_y = static_cast<int>(_y.size());
        return x;
    }
}

产生了double* y; int n_y; int a = 18; int b = 18; double* x = myfunction(&y, &n_y, a, b); 错误,发生在该行

from ctypes import *

libc = CDLL('myfunction.so')
myfunction = libc.myfunction

myfunction.restype = POINTER(c_double)
myfunction.argtypes = [POINTER(POINTER(c_double)), POINTER(c_int),
                       c_int, c_int]

y = POINTER(POINTER(c_double))()
n_y = POINTER(c_int)()

a = 18
b = 18
x = myfunction(y, n_y, c_int(a), c_int(b))

谢谢您的帮助!

2 个答案:

答案 0 :(得分:3)

您快到了。
同时,请靠近[Python 2]: ctypes - A foreign function library for Python

请记住,无论您身在何处,都应以相同的方式处理指针参数(实际上,它适用于所有参数,但对于非指针参数,则很简单)。

换句话说,您在 C 中做的事情(实例化一个变量,并将其指针传递给该函数),您还应该这样做在 Python 中( 代替 实例化变量指针,并将其传递给函数 )。

翻译成代码后,您应该修改初始化 y n_y 和函数( myfunction )调用的方式:

y = POINTER(c_double)()
n_y = c_int()
a = 18
b = 18
x = myfunction(pointer(y), pointer(n_y), a, b)

注意

  • 我在注释中声明的内容(未定义行为,因为向量存在于堆栈中,并且在退出函数时将被销毁)仍然存在。要解决此问题,请执行以下操作:
    • 在返回数据之前先分配堆上的数据( malloc / new )(完成后,还需要释放它( free < / em> / 删除),以避免内存泄漏
    • 使它们为静态

答案 1 :(得分:1)

您可以使用引用,因为引用只是另一级指针的语法。

向量是局部变量,在函数返回时将其释放,因此您需要保留内存。

这是重新编写您的C ++代码以保留内存的地方。因为您的示例还不完整,所以我刚刚使用一些数据创建了一些局部变量:

#define API __declspec(dllexport)  // Windows-specific export
#include <cstdlib>
#include <vector>

using namespace std;

extern "C" {
    API double* myfunction(double* &y, double* &z, int &n_x, int &n_y, int &n_z)
    {
        vector<double> _x {1.1,2.2,3.3};
        vector<double> _y {4.4,5.5};
        vector<double> _z {6.6,7.7,8.8,9.9};

        // Allocate some arrays to store the vectors.
        double* x = new double[_x.size()];
        y = new double[_y.size()];
        z = new double[_z.size()];
        memcpy(x,_x.data(),_x.size() * sizeof(double));
        memcpy(y,_y.data(),_y.size() * sizeof(double));
        memcpy(z,_z.data(),_z.size() * sizeof(double));
        n_x = static_cast<int>(_x.size());
        n_y = static_cast<int>(_y.size());
        n_z = static_cast<int>(_z.size());
        return x;
    }

    // A function to free up the memory.
    API void myfree(double* x, double* y, double* z)
    {
        delete [] x;
        delete [] y;
        delete [] z;
    }
}

Python:

from ctypes import *

dll = CDLL('test')
dll.myfunction.argtypes = (POINTER(POINTER(c_double)),
                           POINTER(POINTER(c_double)),
                           POINTER(c_int),
                           POINTER(c_int),
                           POINTER(c_int))
dll.myfunction.restype = POINTER(c_double)

dll.myfree.argtypes = POINTER(c_double),POINTER(c_double),POINTER(c_double)
dll.myfree.restype = None

# Helper function to allocate storage for return arrays
def myfunction():
    y = POINTER(c_double)() # create an instance of a C double*
    z = POINTER(c_double)()
    n_x = c_int()           # and instances of C int
    n_y = c_int()
    n_z = c_int()

    # Pass them all by reference so new values can be returned
    x = dll.myfunction(byref(y),byref(z),byref(n_x),byref(n_y),byref(n_z))

    # Copies the data into Python lists
    a = x[:n_x.value]
    b = y[:n_y.value]
    c = z[:n_z.value]

    # Free the C arrays and return the Python lists.
    dll.myfree(x,y,z)
    return a,b,c

x,y,z = myfunction()
print(x,y,z)

输出:

[1.1, 2.2, 3.3] [4.4, 5.5] [6.6, 7.7, 8.8, 9.9]

请注意,正在进行大量复制。看一下numpy,它以C可以直接访问的格式创建数组,并具有内置的ctypes接口。