为什么这个 `ctypes` 包装函数的 return 值是 `c_long(0)` 而不是 `c_long(3)`?
Why is the return value of this `ctypes` wrapper function `c_long(0)` and not `c_long(3)`?
为什么这个 ctypes
包装函数的 return 值是 c_long(0)
而不是 c_long(3)
?
// main.cpp
#include <iostream>
class AComplicatedCPPObj {
int *d_;
public:
explicit AComplicatedCPPObj(int *d)
: d_(d) {
}
int *getD() const {
return d_;
}
void setD(int *d) {
d_ = d;
}
};
#ifdef CTYPESTEST_EXPORTS
#if defined(_WIN64)
#define CTYPESTEST_API __declspec(dllexport)
#else
#define CTYPESTEST_API __declspec(dllimport)
#endif
#endif
extern "C" {
CTYPESTEST_API AComplicatedCPPObj *AComplicatedCPPObj_new(int *d) {
return new AComplicatedCPPObj(d);
}
CTYPESTEST_API int *AComplicatedCPPObj_getD(AComplicatedCPPObj *aComplicatedCppObj) {
return aComplicatedCppObj->getD();
}
CTYPESTEST_API void AComplicatedCPPObj_setD(AComplicatedCPPObj *aComplicatedCppObj, int *d) {
aComplicatedCppObj->setD(d);
}
}
编译成共享库的cmake脚本
cmake_minimum_required(VERSION 3.15)
project(ctypesTest)
set(CMAKE_CXX_STANDARD 14)
add_library(ctypesTest SHARED main.cpp)
set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/INSTALL)
add_definitions("-DCTYPESTEST_EXPORTS=TRUE")
install(TARGETS ctypesTest)
在 python 端:
import ctypes as ct
import os, glob, sys
WORKING_DIRECTORY = os.path.dirname(__file__)
# find the shared library
if sys.platform == "linux":
so_path = os.path.join(WORKING_DIRECTORY, "INSTALL/lib/libctypesTest.so")
else:
so_path = os.path.join(WORKING_DIRECTORY, "INSTALL/bin/ctypesTest.dll")
if not os.path.isfile(so_path):
raise FileNotFoundError(so_path)
# load sharedd library into python
lib = ct.CDLL(so_path)
class AComplicatedCPPObj:
def __init__(self, d):
self.set_function_types("AComplicatedCPPObj_new", [ct.POINTER(ct.c_int)], ct.c_void_p)
# checking some ctyeps functions
print("d", d, ct.c_int(d), ct.pointer(ct.c_int(d)), ct.pointer(ct.c_int(d)).contents)
self.obj = lib.AComplicatedCPPObj_new(ct.pointer(ct.c_int(d)))
self.set_function_types("AComplicatedCPPObj_getD", [ct.c_void_p], ct.POINTER(ct.c_long))
self.set_function_types("AComplicatedCPPObj_setD", [ct.c_void_p, ct.POINTER(ct.c_long)], None)
def set_function_types(self, func_name, argtypes, restype):
f = lib.__getattr__(func_name)
f.argtypes = argtypes
f.restype = restype
def getD(self):
return lib.AComplicatedCPPObj_getD(self.obj).contents
def setD(self, d):
if isinstance(d, str):
d = d.encode()
return lib.AComplicatedCPPObj_setD(self.obj, d)
if __name__ == '__main__':
o = AComplicatedCPPObj(3)
print(o.getD())
这个returns:
d 3 c_long(3) <__main__.LP_c_long object at 0x000001F66F0E8248> c_long(3)
c_long(0)
我期待的时候
d 3 c_long(3) <__main__.LP_c_long object at 0x000001F66F0E8248> c_long(3)
c_long(3)
编辑 - 一个错误
我不确定我现在做了什么不同的事情,但现在看来我在重新运行此代码时得到的输出是这样的:
d 3 c_long(3) <__main__.LP_c_long object at 0x000002320B8A52C8> c_long(3)
c_long(193626992)
此外,根据评论中的一个问题,当我 运行:
x = 3
o = AComplicatedCPPObj(x)
print(o.getD())
输出为
d 3 c_long(3) <__main__.LP_c_long object at 0x000002320B8A5248> c_long(3)
c_long(193627312)
还不是
d 3 c_long(3) <__main__.LP_c_long object at 0x000002320B8A52C8> c_long(3)
c_long(3)
?
您在这一行中创建了一个指向临时对象的临时指针:
self.obj = lib.AComplicatedCPPObj_new(ct.pointer(ct.c_int(d)))
这一行执行后,pointer
和c_int
对象都被释放了。所以你有未定义的行为。传递给函数的指针不再存在。
我不知道你为什么要传递指向周围对象的指针,但要修复你当前正在做的事情,你需要保留对 c_int
对象的引用,只要 C++ 代码包含一个指向它的指针:
import ctypes as ct
lib = ct.CDLL('./ctypesTest')
class AComplicatedCPPObj:
def __init__(self, d):
self.set_function_types("AComplicatedCPPObj_new", [ct.POINTER(ct.c_int)], ct.c_void_p)
self.set_function_types("AComplicatedCPPObj_getD", [ct.c_void_p], ct.POINTER(ct.c_int))
self.set_function_types("AComplicatedCPPObj_setD", [ct.c_void_p, ct.POINTER(ct.c_int)], None)
# keep reference to object and pass by reference to C++
self.param = ct.c_int(d)
self.obj = lib.AComplicatedCPPObj_new(ct.byref(self.param))
def set_function_types(self, func_name, argtypes, restype):
f = lib.__getattr__(func_name)
f.argtypes = argtypes
f.restype = restype
def getD(self):
return lib.AComplicatedCPPObj_getD(self.obj).contents
def setD(self, d):
# update local object and pass again by reference
self.param = ct.c_int(d)
return lib.AComplicatedCPPObj_setD(self.obj, ct.byref(self.param))
if __name__ == '__main__':
o = AComplicatedCPPObj(3)
print(o.getD())
但为什么不直接按值传递整数,这样就不必跟踪内存了??
已更新 .cpp:
#include <iostream>
class AComplicatedCPPObj {
int d_;
public:
explicit AComplicatedCPPObj(int d)
: d_(d) {
}
int getD() const {
return d_;
}
void setD(int d) {
d_ = d;
}
};
// FYI, _WIN32 is defined for both 32-bit and 64-bit windows, unlike _WIN64.
#if defined(_WIN32)
# define CTYPESTEST_API __declspec(dllexport)
#else
# define CTYPESTEST_API __declspec(dllimport)
#endif
extern "C" {
CTYPESTEST_API AComplicatedCPPObj *AComplicatedCPPObj_new(int d) {
return new AComplicatedCPPObj(d);
}
CTYPESTEST_API int AComplicatedCPPObj_getD(AComplicatedCPPObj *aComplicatedCppObj) {
return aComplicatedCppObj->getD();
}
CTYPESTEST_API void AComplicatedCPPObj_setD(AComplicatedCPPObj *aComplicatedCppObj, int d) {
aComplicatedCppObj->setD(d);
}
}
已更新Python:
import ctypes as ct
lib = ct.CDLL('./ctypesTest')
def set_function_types(func_name, argtypes, restype):
f = lib.__getattr__(func_name)
f.argtypes = argtypes
f.restype = restype
class AComplicatedCPPObj:
def __init__(self, d):
set_function_types("AComplicatedCPPObj_new", [ct.c_int], ct.c_void_p)
set_function_types("AComplicatedCPPObj_getD", [ct.c_void_p], ct.c_int)
set_function_types("AComplicatedCPPObj_setD", [ct.c_void_p, ct.c_int], None)
self.obj = lib.AComplicatedCPPObj_new(d)
def getD(self):
return lib.AComplicatedCPPObj_getD(self.obj)
def setD(self, d):
return lib.AComplicatedCPPObj_setD(self.obj, d)
if __name__ == '__main__':
o = AComplicatedCPPObj(3)
print(o.getD())
为什么这个 ctypes
包装函数的 return 值是 c_long(0)
而不是 c_long(3)
?
// main.cpp
#include <iostream>
class AComplicatedCPPObj {
int *d_;
public:
explicit AComplicatedCPPObj(int *d)
: d_(d) {
}
int *getD() const {
return d_;
}
void setD(int *d) {
d_ = d;
}
};
#ifdef CTYPESTEST_EXPORTS
#if defined(_WIN64)
#define CTYPESTEST_API __declspec(dllexport)
#else
#define CTYPESTEST_API __declspec(dllimport)
#endif
#endif
extern "C" {
CTYPESTEST_API AComplicatedCPPObj *AComplicatedCPPObj_new(int *d) {
return new AComplicatedCPPObj(d);
}
CTYPESTEST_API int *AComplicatedCPPObj_getD(AComplicatedCPPObj *aComplicatedCppObj) {
return aComplicatedCppObj->getD();
}
CTYPESTEST_API void AComplicatedCPPObj_setD(AComplicatedCPPObj *aComplicatedCppObj, int *d) {
aComplicatedCppObj->setD(d);
}
}
编译成共享库的cmake脚本
cmake_minimum_required(VERSION 3.15)
project(ctypesTest)
set(CMAKE_CXX_STANDARD 14)
add_library(ctypesTest SHARED main.cpp)
set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/INSTALL)
add_definitions("-DCTYPESTEST_EXPORTS=TRUE")
install(TARGETS ctypesTest)
在 python 端:
import ctypes as ct
import os, glob, sys
WORKING_DIRECTORY = os.path.dirname(__file__)
# find the shared library
if sys.platform == "linux":
so_path = os.path.join(WORKING_DIRECTORY, "INSTALL/lib/libctypesTest.so")
else:
so_path = os.path.join(WORKING_DIRECTORY, "INSTALL/bin/ctypesTest.dll")
if not os.path.isfile(so_path):
raise FileNotFoundError(so_path)
# load sharedd library into python
lib = ct.CDLL(so_path)
class AComplicatedCPPObj:
def __init__(self, d):
self.set_function_types("AComplicatedCPPObj_new", [ct.POINTER(ct.c_int)], ct.c_void_p)
# checking some ctyeps functions
print("d", d, ct.c_int(d), ct.pointer(ct.c_int(d)), ct.pointer(ct.c_int(d)).contents)
self.obj = lib.AComplicatedCPPObj_new(ct.pointer(ct.c_int(d)))
self.set_function_types("AComplicatedCPPObj_getD", [ct.c_void_p], ct.POINTER(ct.c_long))
self.set_function_types("AComplicatedCPPObj_setD", [ct.c_void_p, ct.POINTER(ct.c_long)], None)
def set_function_types(self, func_name, argtypes, restype):
f = lib.__getattr__(func_name)
f.argtypes = argtypes
f.restype = restype
def getD(self):
return lib.AComplicatedCPPObj_getD(self.obj).contents
def setD(self, d):
if isinstance(d, str):
d = d.encode()
return lib.AComplicatedCPPObj_setD(self.obj, d)
if __name__ == '__main__':
o = AComplicatedCPPObj(3)
print(o.getD())
这个returns:
d 3 c_long(3) <__main__.LP_c_long object at 0x000001F66F0E8248> c_long(3)
c_long(0)
我期待的时候
d 3 c_long(3) <__main__.LP_c_long object at 0x000001F66F0E8248> c_long(3)
c_long(3)
编辑 - 一个错误
我不确定我现在做了什么不同的事情,但现在看来我在重新运行此代码时得到的输出是这样的:
d 3 c_long(3) <__main__.LP_c_long object at 0x000002320B8A52C8> c_long(3)
c_long(193626992)
此外,根据评论中的一个问题,当我 运行:
x = 3
o = AComplicatedCPPObj(x)
print(o.getD())
输出为
d 3 c_long(3) <__main__.LP_c_long object at 0x000002320B8A5248> c_long(3)
c_long(193627312)
还不是
d 3 c_long(3) <__main__.LP_c_long object at 0x000002320B8A52C8> c_long(3)
c_long(3)
?
您在这一行中创建了一个指向临时对象的临时指针:
self.obj = lib.AComplicatedCPPObj_new(ct.pointer(ct.c_int(d)))
这一行执行后,pointer
和c_int
对象都被释放了。所以你有未定义的行为。传递给函数的指针不再存在。
我不知道你为什么要传递指向周围对象的指针,但要修复你当前正在做的事情,你需要保留对 c_int
对象的引用,只要 C++ 代码包含一个指向它的指针:
import ctypes as ct
lib = ct.CDLL('./ctypesTest')
class AComplicatedCPPObj:
def __init__(self, d):
self.set_function_types("AComplicatedCPPObj_new", [ct.POINTER(ct.c_int)], ct.c_void_p)
self.set_function_types("AComplicatedCPPObj_getD", [ct.c_void_p], ct.POINTER(ct.c_int))
self.set_function_types("AComplicatedCPPObj_setD", [ct.c_void_p, ct.POINTER(ct.c_int)], None)
# keep reference to object and pass by reference to C++
self.param = ct.c_int(d)
self.obj = lib.AComplicatedCPPObj_new(ct.byref(self.param))
def set_function_types(self, func_name, argtypes, restype):
f = lib.__getattr__(func_name)
f.argtypes = argtypes
f.restype = restype
def getD(self):
return lib.AComplicatedCPPObj_getD(self.obj).contents
def setD(self, d):
# update local object and pass again by reference
self.param = ct.c_int(d)
return lib.AComplicatedCPPObj_setD(self.obj, ct.byref(self.param))
if __name__ == '__main__':
o = AComplicatedCPPObj(3)
print(o.getD())
但为什么不直接按值传递整数,这样就不必跟踪内存了??
已更新 .cpp:
#include <iostream>
class AComplicatedCPPObj {
int d_;
public:
explicit AComplicatedCPPObj(int d)
: d_(d) {
}
int getD() const {
return d_;
}
void setD(int d) {
d_ = d;
}
};
// FYI, _WIN32 is defined for both 32-bit and 64-bit windows, unlike _WIN64.
#if defined(_WIN32)
# define CTYPESTEST_API __declspec(dllexport)
#else
# define CTYPESTEST_API __declspec(dllimport)
#endif
extern "C" {
CTYPESTEST_API AComplicatedCPPObj *AComplicatedCPPObj_new(int d) {
return new AComplicatedCPPObj(d);
}
CTYPESTEST_API int AComplicatedCPPObj_getD(AComplicatedCPPObj *aComplicatedCppObj) {
return aComplicatedCppObj->getD();
}
CTYPESTEST_API void AComplicatedCPPObj_setD(AComplicatedCPPObj *aComplicatedCppObj, int d) {
aComplicatedCppObj->setD(d);
}
}
已更新Python:
import ctypes as ct
lib = ct.CDLL('./ctypesTest')
def set_function_types(func_name, argtypes, restype):
f = lib.__getattr__(func_name)
f.argtypes = argtypes
f.restype = restype
class AComplicatedCPPObj:
def __init__(self, d):
set_function_types("AComplicatedCPPObj_new", [ct.c_int], ct.c_void_p)
set_function_types("AComplicatedCPPObj_getD", [ct.c_void_p], ct.c_int)
set_function_types("AComplicatedCPPObj_setD", [ct.c_void_p, ct.c_int], None)
self.obj = lib.AComplicatedCPPObj_new(d)
def getD(self):
return lib.AComplicatedCPPObj_getD(self.obj)
def setD(self, d):
return lib.AComplicatedCPPObj_setD(self.obj, d)
if __name__ == '__main__':
o = AComplicatedCPPObj(3)
print(o.getD())