社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

使用Cype调用带有Python参数的C++方法导致“不知道如何转换参数”错误

Tom Spargo • 3 年前 • 1387 次点击  

我试图从Python 3使用下面的C++类。7,但无法让第一个方法“设置”工作,更不用说运算符重载方法了。我尝试了Python包装器和extern块的许多变体,但我要么遇到了“不知道如何转换参数5”错误,要么遇到了分段错误。网上的例子和我找到的答案都太基本了,或者是解决了其他问题。

我的argtypes第一个参数应该是指向对象的指针吗?我不知道用什么语法来表示。

CMatrix。h:

#include <stdio.h>

class CMatrix
{
public:
    CMatrix(int d1);
    CMatrix(int d1, int d2);
    CMatrix(int d1, int d2, int d3);
    CMatrix (float f1, float f2, float f3);
    ~CMatrix();
    
    void Set(int x, int y, int z, float f);
    void operator=(const float f);
    CMatrix& operator=(const CMatrix &cm);
    inline float& operator()(const int i) {
        return m[i];}
    inline float& operator()(const int i, const int j) {
        return m[i*s2+j];}
    inline float& operator()(const int i, const int j, const int k) {
        return m[i*s23+j*s3+k];}
    int s1, s2, s3;     // dimensions of array
    int s23;            // s2*s3;
    float *m;           // pointer to first element of matrix.
    int dimensions;     // 1, 2, or 3.
};

extern "C" {
    CMatrix* CMatrix_new1(int i) {return new CMatrix(i); }
    CMatrix* CMatrix_new2(int i, int j) {return new CMatrix(i, j); }
    CMatrix* CMatrix_new3(int i, int j, int k) {return new CMatrix(i, j, k); }
    void CMatrix_Set(CMatrix* cm, int x, int y, int z, float f) {cm->Set(x, y, z, f); }
}

cmWrapper。py:

import ctypes as c
lib = c.cdll.LoadLibrary('./libCMatrix.o')
lib.CMatrix_Set.argtypes = [c.c_int, c.c_int, c.c_int, c.c_float]

class CMatrix(object):
    def __init__(self, i, j, k):
        if j==0 and k==0:
            self.obj = lib.CMatrix_new1(i)
        elif k==0:
            self.obj = lib.CMatrix_new2(i, j)
        else:
            self.obj = lib.CMatrix_new3(i, j, k)

    def Set(self, x, y, z, f):
        lib.CMatrix_Set(self.obj, x, y, z, f)

cm = CMatrix(2, 3, 0)
cm.Set(1, 2, 0, 99.0)

追溯:

>>> import cmWrapper
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/.../ctypes/cmWrapper.py", line 18, in <module>
    cm.Set(1, 2, 0, 99.0)
  File "/.../ctypes/cmWrapper.py", line 15, in Set
    lib.CMatrix_Set(self.obj, x, y, z, f)
ctypes.ArgumentError: argument 5: <class 'TypeError'>: Don't know how to convert parameter 5

如果有问题,我用C++编译了C++代码:

g++ -c -fPIC CMatrix.cpp -o CMatrix.o
g++ -shared -Wl -o libCMatrix.o CMatrix.o

这是在运行10.15.7的Mac上。

来自lldb:

Executable module set to "/Library/Frameworks/Python.framework/Versions/3.7/Resources/Python.app/Contents/MacOS/Python".
Architecture set to: x86_64h-apple-macosx-.
(lldb) 
There is a running process, detach from it and attach?: [Y/n] n
(lldb) thread list
Process 57460 stopped
* thread #1: tid = 0x47364c, 0x00007fff202f5656 libsystem_kernel.dylib`__select + 10, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
(lldb) thread continue
Resuming thread 0x47364c in process 57460
Process 57460 resuming
Process 57460 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x13835f20)
    frame #0: 0x00000001015f19b2 libCMatrix.o`CMatrix::Set(int, int, int, float) + 34
libCMatrix.o`CMatrix::Set:
->  0x1015f19b2 <+34>: movl   (%rax), %esi
    0x1015f19b4 <+36>: movl   0x4(%rax), %edx
    0x1015f19b7 <+39>: movl   0x8(%rax), %ecx
    0x1015f19ba <+42>: movl   0xc(%rax), %r8d
Target 0: (Python) stopped.
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/130722
 
1387 次点击  
文章 [ 2 ]  |  最新文章 3 年前
Joseph Sible-Reinstate Monica
Reply   •   1 楼
Joseph Sible-Reinstate Monica    3 年前

问题是:

lib.CMatrix_Set.argtypes = [c.c_int, c.c_int, c.c_int, c.c_float]

你忘了那本书的词条了 self.obj 论点将其更改为:

lib.CMatrix_Set.argtypes = [c.c_void_p, c.c_int, c.c_int, c.c_int, c.c_float]

此外,您可能应该将其设置为 restype 调用的其他函数,以便在 int 窄于 void * .

另一个选项是创建一个从 ctypes.Structure 对应于C++类,然后使用一个指针作为类型,但是之后你必须非常小心C++代码,或者布局可能会不同于C,Python所期望的。

Mark Tolonen
Reply   •   2 楼
Mark Tolonen    3 年前

你必须完全匹配论点。设定 .argtypes .restype 你使用的每一个函数 ctypes 可以正确地将参数封送到C并再次封送回来。如果你不设定 .重新键入 ctypes 假设返回值为 c_int (通常是有符号的32位整数)而不是(可能是64位)指针。

下面是一个有效的例子。我并没有充实每个函数,因为一个函数应该足够了。在32位和64位Python上进行了测试。

测验cpp(使用MS编译器cl/LD/EHsc/W4 test.cpp构建):

#include <stdio.h>

// Needed to export functions on Windows
#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

class CMatrix
{
public:
    CMatrix(int d1) : s1(d1) { m = new float[d1]; }
    ~CMatrix() { delete [] m; }
    const float* Get(int& s) { s = s1; return m; }
    void Set(int x, float f) { m[x] = f; }
    int s1;
    float *m;
};

extern "C" {
    API CMatrix* CMatrix_new(int i) {return new CMatrix(i); }
    API const float* CMatrix_Get(CMatrix* cm, int& x) { return cm->Get(x); }
    API void CMatrix_Set(CMatrix* cm, int x, float f) { cm->Set(x, f); }
    API void CMatrix_delete(CMatrix* cm) { delete cm; }
}

测验py

import ctypes as ct

# For type checking the returned pointer.
class _CMatrix(ct.c_void_p) : pass
PCMatrix = ct.POINTER(_CMatrix)

class CMatrix:

    _dll = ct.CDLL('./test')
    _dll.CMatrix_new.argtypes = ct.c_int, 
    _dll.CMatrix_new.restype = PCMatrix
    _dll.CMatrix_Get.argtypes = PCMatrix, ct.POINTER(ct.c_int)
    _dll.CMatrix_Get.restype = ct.POINTER(ct.c_float)
    _dll.CMatrix_Set.argtypes = PCMatrix, ct.c_int, ct.c_float
    _dll.CMatrix_Set.restype = None
    _dll.CMatrix_delete.argtypes = PCMatrix, 
    _dll.CMatrix_delete.restype = None

    def __init__(self, i):
        self.obj = self._dll.CMatrix_new(i)

    def Set(self, x, f):
        self._dll.CMatrix_Set(self.obj, x, f)

    def Get(self):
        size = ct.c_int()
        m = self._dll.CMatrix_Get(self.obj, ct.byref(size))
        return m[:size.value]

    def __del__(self):
        self._dll.CMatrix_delete(self.obj)

cm = CMatrix(2)
cm.Set(0, 1.5)
cm.Set(1, 2.5)
print(cm.Get())

输出:

[1.5, 2.5]