【问题标题】:C++ & Python: Pass and return a 2D double pointer array from python to c++C++ 和 Python:将二维双指针数组从 python 传递并返回到 C++
【发布时间】:2019-10-03 20:58:05
【问题描述】:

我想将一个二维数组从 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 的sn-p 都会有很大帮助。

【问题讨论】:

  • 由于您正在分配内存,因此您需要通过缓冲区接口将内存传递出去。在 pybind11 中这相当容易,请参阅 NumPy Buffer protocol
  • 除非您真的需要指向指针的指针,否则请考虑使用指向大小为 MxN 的单个数组的指针来简化事情,该数组可通过 row * N + column 访问

标签: python c++ multidimensional-array ctypes swig


【解决方案1】:

上市[Python.Docs]: ctypes - A foreign function library for Python

几个想法:

  • 双指针数组”具有误导性:
    • 没有数组
    • "double pointer" 可能意味着 pointer to doublepointer to pointer 指向某物(包括 double )
  • 使用双指针(指向 double :) 的解决方案似乎有点复杂(在 cmets 中也有说明)。我倾向于认为这是一个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: %.3f\n", 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: free\n");
        for (int i = 0; i < m; i++) {
            free(ppMat[i]);
            ret++;
            ppMat[i] = NULL;
        }
        free(ppMat);
    }
    return ++ret;
}

code00.py

#!/usr/bin/env python3

import sys
import ctypes as ct
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 = ct.CDLL(DLL_NAME)
    init = dll00.init
    cleanup = dll00.cleanup

    rows = 4
    cols = 6

    DblPtr = ct.POINTER(ct.c_double)
    DblPtrPtr = ct.POINTER(DblPtr)

    init.argtypes = [DblPtrPtr, ct.c_int, ct.c_int]
    init.restype = DblPtrPtr
    cleanup.argtypes = [DblPtrPtr, ct.c_int]
    cleanup.restype = ct.c_int

    DblPtrArr = DblPtr * rows

    DblArr = ct.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 = ct.cast(DblPtrArr(*(ct.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.")
    sys.exit(rc)

输出

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q058226790]> sopr.bat
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###

[prompt]> "c:\Install\x86\Microsoft\Visual Studio Community\2017\VC\Auxiliary\Build\vcvarsall.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:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.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]: Problems with passing and getting arrays for a C function using ctypes (@CristiFati's answer),它与这个非常相似(我会说几乎相同)。

【讨论】:

  • 非常感谢!我最终放弃了,因为我没有找到任何结论。事实上,我已经很长时间以来一直在处理这个问题,并删除了其中的 2 个,因为我的理解有所进步,而且我注意到我的问题没有被很好地提出。再次,非常感谢
猜你喜欢
  • 2015-05-30
  • 2020-02-28
  • 2018-07-28
  • 2012-02-05
  • 1970-01-01
  • 2015-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多