【问题标题】:ctypes giving error on calling function with pointer to class objectctypes在使用指向类对象的指针调用函数时出错
【发布时间】:2020-07-21 04:13:49
【问题描述】:

我的 C++ DLL 中有两个包装函数。

函数CreateObj()创建一个其他类的对象并返回一个指向该对象的指针 在第二个函数DoSomething 中,我将先前创建的指针作为第一个参数传递,第二个参数是无符号字符的 RGB 图像 (1200, 1200,3)。

在 python 端,我加载 DLL 并希望调用两个包装函数对我在 python 端加载的 RGB 图像进行一些处理。在从 python 调用 DoSomething 的最后一步中,我得到了一个 OSError。

任何帮助将不胜感激。

//The C++ library code

class Obj
{
    /*..../*
};

extern "C"
{
#ifdef BUILD_AS_DLL
    __declspec(dllexport)
#endif
    Obj* CreateObj();
    int DoSomething( Obj* ptr,  uint8_t*  t[1200][1200][]);  //ptr was created using CreateObj and second argument is an image array of unsigned char of shape [1200][1200][3]
#ifdef BUILD_AS_DLL
    __declspec(dllexport)
#endif
}

调用上述DLL的Python代码

from ctypes import *
import numpy as np
import numpy.ctypeslib as npct
import cv2
    
path = os.path.join(os.getcwd(), "mwe.dll") 
lib = ctypes.CDLL(path) 

create_func = lib.CreateObj
create_func.restype = c_void_p
obj = c_void_p(create_func())

dosomething_func = lib.DoSomething
ucharPtr = npct.ndpointer(dtype=np.uint8, 
                          ndim=3, 
                          shape = (1200,1200,3),
                          flags='CONTIGUOUS') 
dosomething_func.argtype = (c_void_p,
                            ucharPtr)
dosomething_func.restype = c_int

img = cv2.imread("path/to/image")  
img_ctype = byref(img.ctypes.data_as(c_void_p))
dosomething_func(byref(obj), img_ctype)   // ERROR on this line

我收到错误

File "mwe.py", line 24, in <module>
    res = dosomething_func(
OSError: exception: access violation reading 0xFFFFFFFFFFFFFFFF

【问题讨论】:

  • 创建一个minimal reproducible example。您可以实现接受所需参数的小型虚拟 C 函数和实际重现故障的 Python 代码,并删除不相关的代码,如导入图像......只需创建一个您想要直接传递的大小的数组。您的 Python 代码引用了 seg 并且没有定义它。
  • 我已经处理了您的评论.. seg 是一个错字
  • 这是一个最小的工作示例。我制作的 DLL 包含两个公开的函数 lib.CreateObjlib.DoSomething。请重新考虑否决
  • 并非如此,重现错误消息不起作用。理想情况下,将您实际编写的 DLL 代码发布为最少的代码以重现确切的问题。重新输入语法不正确的部分代码没有帮助。

标签: python python-3.x numpy ctypes


【解决方案1】:

这是一个最小可重现的解决方案示例,演示将一个小的 3D numpy 数组传入和传出函数:

test.cpp:

请注意,uint8_t* t[1200][1200][] 在语法上不正确。可变数组维度必须是第一个,而不是最后一个,这是一个指针数组而不是 8 位数据数组。

#include <cstdint>
#include <iostream>

using namespace std;

#define API __declspec(dllexport)

class Obj
{
    int m_x;
public:
    Obj(int x): m_x(x) {}
    int get_x() const { return m_x; }
};

extern "C" {

API Obj* CreateObj() {
    return new Obj(5);
}

API int DoSomething(Obj* ptr, uint8_t t[][3][3]) {
    for(uint8_t i=0; i < 3; ++i) {
        for(uint8_t j=0; j < 3; ++j) {
            for(uint8_t k=0; k < 3; ++k) {
                cout << static_cast<uint32_t>(t[i][j][k]) << ' ';
                t[i][j][k] *= 2;
            }
            cout << endl;
        }
        cout << endl;
    }
    return ptr->get_x();
}

}

test.py:

假设cv2.imread() 返回与声明的ndpointer 类型相同类型和形状的np.array,您只需将其直接传递给函数即可。

from ctypes import *
import numpy as np
import numpy.ctypeslib as npct

lib = CDLL('./test')

class OBJ(Structure):
    _fields_ = []

ARRAY = npct.ndpointer(dtype=np.uint8,shape=(3,3,3))

lib.CreateObj.argtypes = c_int,
lib.CreateObj.restype = POINTER(OBJ)
lib.DoSomething.argtypes = POINTER(OBJ),ARRAY
lib.DoSomething.restype = c_int

arr = np.arange(27,dtype=np.uint8).reshape((3,3,3))
obj = lib.CreateObj(5)
result = lib.DoSomething(obj,arr)
print(f'result = {result}')
print(arr)

输出:

0 1 2
3 4 5
6 7 8

9 10 11
12 13 14
15 16 17

18 19 20
21 22 23
24 25 26

result = 5
[[[ 0  2  4]
  [ 6  8 10]
  [12 14 16]]

 [[18 20 22]
  [24 26 28]
  [30 32 34]]

 [[36 38 40]
  [42 44 46]
  [48 50 52]]]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-04
    • 1970-01-01
    • 1970-01-01
    • 2021-06-06
    • 2017-01-22
    相关资源
    最近更新 更多