C++ & Python:传递并返回从 python 到 c++ 的 2D 双指针数组

C++ & Python: Pass and return a 2D double pointer array from python to c++

本文关键字:c++ 2D 数组 指针 python Python 返回 C++      更新时间:2023-10-16

我想将一个二维数组从Python传递给C++函数,然后将一个相同类型,相同维度的数组返回给Python。我知道这个问题已经被问过好几次了,但我无法找到我的问题的相关答案。对于我的问题,我必须使用双指针数组并让函数返回双指针数组(不像许多示例所示void(。

我的C++函数是:

#include <stdio.h>      
#include <stdlib.h> 
extern "C" double** dot(double **a, int m, int n){
double **arr = (double **)malloc(m * sizeof(double *)); 
for (int i=0; i<m; i++) 
arr[i] = (double*)malloc(n * sizeof(double));
for (int i=0; i < m; i++){
for (int j=0; j < n; j++){
arr[i][j] = a[i][j];
}
}
return arr;
}  

目前,我使用了Ctypes.我知道我可以使用Swig界面,但我宁愿避免它,因为我不太了解它。但是,我仍然愿意接受任何建议。如果我不得不使用Swig,我的问题是,如果我没有记错的话,我将不得不使用Typemap来分解指针结构,这是我不太理解的部分。

我目前在Python中尝试的是:

import ctypes as c
import numpy as np
ty_ = np.ctypeslib._ctype_ndarray(c.POINTER(c.POINTER(c.c_double)), (3,3))
x = np.arange(9.).reshape(3,3)
_dll = ctypes.CDLL('./double_2D.so')
_foobar = _dll.dot
_foobar.argtype = type(y)
_foobar.restype = type(y)
d = _foobar(y, 3, 3) #I would like d to be a nice matrix like x 

我也试过

c.cast(_foobar(y,3,3), c.POINTER(c.POINTER(c.c_double)))

但是上面的例子都不起作用。因此,任何关于定义argtyperestype的建议,或SwigTypemap的片段都将有很大帮助。

列出 [Python.Docs]: ctypes - Python 的外部函数库。

几个想法:

  • ">双指针数组"具有误导性:

    • 没有数组

    • "双指针
    • "可能意味着指向双指针或指针指向某物(包括双指针(

  • 带有双指针的解决方案(:)(似乎有点复杂(如评论中也指定的那样(。我倾向于认为这是一个XY问题。通常,人们应该只处理简单的指针,特别是如果他们在这方面的知识不是很强(这似乎适用于这里,正如我从您提交然后删除的其他问题中注意到的那样,与这个问题相同(或非常相似(

无论如何,这里有一个简单的演示示例。

dll00.c

#include <stdlib.h>
#include <stdio.h>
#if defined(_WIN32)
#  define DLL00_EXPORT_API __declspec(dllexport)
#else
#  define DLL00_EXPORT_API
#endif

#if defined(__cplusplus)
extern "C" {
#endif
DLL00_EXPORT_API double** init(double **ppMat, int m, int n);
DLL00_EXPORT_API int cleanup(double **ppMat, int m);
#if defined(__cplusplus)
}
#endif
DLL00_EXPORT_API double** init(double **ppMat, int m, int n)
{
const double factor = 7.0;
printf("n----- FROM C: Multiplying input matrix by: %.3fn", factor);
double **ret = malloc(m * sizeof(double*));
for (int i = 0; i < m; i++) {
ret[i] = malloc(n * sizeof(double));
for (int j = 0; j < n; j++) {
ret[i][j] = ppMat[i][j] * factor;
}
}
return ret;
}
DLL00_EXPORT_API int cleanup(double **ppMat, int m)
{
int ret = 0;
if (ppMat) {
printf("n----- FROM C: freen");
for (int i = 0; i < m; i++) {
free(ppMat[i]);
ret++;
ppMat[i] = NULL;
}
free(ppMat);
}
return ++ret;
}

code00.py

#!/usr/bin/env python
import ctypes as cts
import sys
from pprint import pprint as pp

DLL_NAME = "./dll00.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so")

def ptr2d_to_mat(ptr, rows, cols):
return tuple(tuple(ptr[i][j] for j in range(cols)) for i in range(rows))

def main(*argv):
dll00 = cts.CDLL(DLL_NAME)
init = dll00.init
cleanup = dll00.cleanup
rows = 4
cols = 6
DblPtr = cts.POINTER(cts.c_double)
DblPtrPtr = cts.POINTER(DblPtr)
init.argtypes = (DblPtrPtr, cts.c_int, cts.c_int)
init.restype = DblPtrPtr
cleanup.argtypes = (DblPtrPtr, cts.c_int)
cleanup.restype = cts.c_int
DblPtrArr = DblPtr * rows
DblArr = cts.c_double * cols
DblArrArr = DblArr * rows
first_value = 6
in_mat = tuple(tuple(range(cols * i + first_value, cols * (i + 1) + first_value)) for i in range(rows))
print("Input matrix:")
pp(in_mat)
in_arr = DblArrArr(*in_mat)
in_ptr = cts.cast(DblPtrArr(*(cts.cast(row, DblPtr) for row in in_arr)), DblPtrPtr)  # Cast each row and the final array to (corresponding) pointers
out_ptr = init(in_ptr, rows, cols)
out_mat = ptr2d_to_mat(out_ptr, rows, cols)
cleanup(out_ptr, rows)
print("nOutput matrix:")
pp(out_mat)

if __name__ == "__main__":
print("Python {:s} {:03d}bit on {:s}n".format(" ".join(item.strip() for item in sys.version.split("n")),
64 if sys.maxsize > 0x100000000 else 32, sys.platform))
rc = main(*sys.argv[1:])
print("nDone.n")
sys.exit(rc)

输出

[cfati@CFATI-5510-0:e:WorkDevStackOverflowq058226790]> sopr.bat
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###
[prompt]> "c:Installx86MicrosoftVisual Studio Community2017VCAuxiliaryBuildvcvarsall.bat" x64>nul
[prompt]> dir /b
code00.py
dll00.c
[prompt]> cl /nologo /DDLL dll00.c  /link /NOLOGO /DLL /OUT:dll00.dll
dll00.c
Creating library dll00.lib and object dll00.exp
[prompt]> dir /b
code00.py
dll00.c
dll00.dll
dll00.exp
dll00.lib
dll00.obj
[prompt]> "e:WorkDevVEnvspy_064_03.07.03_test0Scriptspython.exe" code00.py
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 064bit on win32
Input matrix:
((6, 7, 8, 9, 10, 11),
(12, 13, 14, 15, 16, 17),
(18, 19, 20, 21, 22, 23),
(24, 25, 26, 27, 28, 29))
----- FROM C: Multiplying input matrix by: 7.000
----- FROM C: free
Output matrix:
((42.0, 49.0, 56.0, 63.0, 70.0, 77.0),
(84.0, 91.0, 98.0, 105.0, 112.0, 119.0),
(126.0, 133.0, 140.0, 147.0, 154.0, 161.0),
(168.0, 175.0, 182.0, 189.0, 196.0, 203.0))
Done.

你也可以看看[SO]:使用ctypes传递和获取C函数数组的问题(@CristiFati的答案(,这与这个非常相似(几乎相同,我会说(。