使用 ctypes 调用参数来自 Python 的 C++ 方法导致 "Don't know how to convert parameter" 错误
Using ctypes to call a C++ method with parameters from Python results in "Don't know how to convert parameter" error
我正在尝试使用 Python3.7 中的以下 C++ class,但无法使第一个方法 'Set' 起作用,更不用说运算符重载方法了.我已经尝试了 Python 包装器和 extern 块的多种变体,但我得到了“不知道如何转换参数 5”错误或分段错误。我发现的在线示例和 SO 答案过于基础或解决了其他问题。
我的 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++ 代码:
g++ -c -fPIC CMatrix.cpp -o CMatrix.o
g++ -shared -Wl -o libCMatrix.o CMatrix.o
这是在 Mac 运行 10.15.7.
来自 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.
这是问题所在:
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
,这样 return 值就不会在 int
较窄的系统上被截断比 void *
.
你的另一个选择是创建一个从 ctypes.Structure
继承的 Python class 对应于你的 C++ class,然后使用指向它的指针作为类型,但是你必须非常小心你的 C++ 代码,否则布局最终可能会与 C 中的布局不同,这也是 Python 所期望的。
您必须完全匹配参数。设置您使用的每个函数的 .argtypes
和 .restype
,以便 ctypes
可以正确地将参数编组到 C 并再次返回。如果你没有设置 .restype
ctypes
假设 return 值是 c_int
(通常是带符号的 32 位整数)而不是(可能是 64 位)指针。
这是一个工作示例。我没有充实每一个功能,因为一个应该足够了。在 32 位和 64 位 Python.
上测试
test.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; }
}
test.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]
我正在尝试使用 Python3.7 中的以下 C++ class,但无法使第一个方法 'Set' 起作用,更不用说运算符重载方法了.我已经尝试了 Python 包装器和 extern 块的多种变体,但我得到了“不知道如何转换参数 5”错误或分段错误。我发现的在线示例和 SO 答案过于基础或解决了其他问题。
我的 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++ 代码:
g++ -c -fPIC CMatrix.cpp -o CMatrix.o
g++ -shared -Wl -o libCMatrix.o CMatrix.o
这是在 Mac 运行 10.15.7.
来自 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.
这是问题所在:
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
,这样 return 值就不会在 int
较窄的系统上被截断比 void *
.
你的另一个选择是创建一个从 ctypes.Structure
继承的 Python class 对应于你的 C++ class,然后使用指向它的指针作为类型,但是你必须非常小心你的 C++ 代码,否则布局最终可能会与 C 中的布局不同,这也是 Python 所期望的。
您必须完全匹配参数。设置您使用的每个函数的 .argtypes
和 .restype
,以便 ctypes
可以正确地将参数编组到 C 并再次返回。如果你没有设置 .restype
ctypes
假设 return 值是 c_int
(通常是带符号的 32 位整数)而不是(可能是 64 位)指针。
这是一个工作示例。我没有充实每一个功能,因为一个应该足够了。在 32 位和 64 位 Python.
上测试test.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; }
}
test.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]