Python ctypes:按引用传递参数错误

问题描述 投票:0回答:2

我有一个 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
并将它们各自的大小放入
n_y, n_z
之前进行一些计算,并返回数组
x
,大小为
a*b

将此函数构建到共享库后

myfunction.so
,我在Python中调用它,如下所示:

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))

运行此脚本时出现错误:

ctypes.ArgumentError:参数 3: : 错误 类型

所以

c_int
n_y
类型是不正确的。我应该放什么来代替?

非常感谢您的帮助!


更新

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

y
z
很相似,所以让我省略
z

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

现在在C++中,我按如下方式调用上面的函数:

double* y;
int n_y;
int a = 18;
int b = 18;
double* x = myfunction(&y, &n_y, a, b);

有效。在 Python 中:

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))

产生了

Segmentation fault
错误,发生在该行

*y = &_y[0];

谢谢您的帮助!

python ctypes
2个回答
5
投票

你就快到了。
同时,请密切关注 [Python.Docs]:ctypes - Python 的外部函数库

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

换句话说,您在 C 中所做的操作(实例化变量将其指针传递给函数),您也应该在 Python 中执行操作(而不是 实例化变量指针并将其传递给函数)。

翻译成代码,你应该修改初始化

yn_y以及函数(myfunction)调用的方式:

>>> #from ctypes import * # Anti-pattern. Don't use it >>> import ctypes as cts >>> >>> y = cts.POINTER(cts.c_double)() # Instantiate simple pointer >>> n_y = cts.c_int() >>> a = 18 >>> b = 18 >>> x = myfunction(cts.pointer(y), cts.pointer(n_y), a, b)

注释

    我在评论中所说的(
  • 未定义的行为,因为向量存在于堆栈中,并且在退出函数时将被销毁)仍然有效。要修复它:
      在返回之前在堆上分配数据(
    • malloc / new)(完成后,您还需要释放它(free / delete),以避免内存泄漏
    • 使它们
    • 静态
一些(远程连接)示例:

  • [SO]:通过 ctypes 从 Python 调用的 C 函数返回错误的值(@CristiFati 的答案)

  • [SO]:如何将双指针 ctype 转换为 numpy 数组? (@CristiFati 的回答)


4
投票
您可以使用引用,因为引用只是另一层指针的语法。

向量是局部变量,当函数返回时会被释放,因此你需要保留内存。

这是为了保留内存而重新编写的 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 接口。

© www.soinside.com 2019 - 2024. All rights reserved.