Python ctypes cdll.LoadLibrary ، إنشاء مثيل كائن ، تنفيذ طريقته ، اقتطاع عنوان متغير خاص
windows (1)
دائمًا (بشكل صحيح) حدد أنماط وإعادة كتابة للوظائف المحددة في C ، وإلا ( C89 style) ، فسيكون الافتراضي إلى int ( 32bit ) ، يولد !!! سلوك غير محدد !!! . على 64 بت ، قد يؤدي ذلك إلى اقتطاع (وهو بالضبط ما تعانيه).
أيضًا ، عند حدوث مشكلات ، لا تنسَ [Python 3.Docs]: ctypes - مكتبة الوظائف الأجنبية لـ Python .
أدناه هو نسخة معدلة من التعليمات البرمجية الخاصة بك.
detector.cpp :
#include <stdio.h>
#include <memory.h>
#include <fstream>
#define C_TAG "From C"
#define PRINT_MSG_2SP(ARG0, ARG1) printf("%s - [%s] (%d) - [%s]: %s: 0x%0p\n", C_TAG, __FILE__, __LINE__, __FUNCTION__, ARG0, ARG1)
using std::endl;
std::ofstream outFile;
class Detector {
public:
Detector();
void process(int *pIn, int *pOut, int n);
private:
int m_var;
};
Detector::Detector()
: m_var(0) {
outFile.open("addr_debug.txt");
outFile << "m_var init address: " << &m_var << endl;
PRINT_MSG_2SP("&m_var", &m_var);
}
void Detector::process(int *pIn, int *pOut, int n) {
outFile << "m_var process address: " << &m_var << endl;
outFile.close();
PRINT_MSG_2SP("&m_var", &m_var);
}
#define SIM_EXPORT __declspec(dllexport)
#if defined(__cplusplus)
extern "C" {
#endif
SIM_EXPORT Detector *DetectorNew() { return new Detector(); }
SIM_EXPORT void DetectorProcess(Detector *pDet, int *pIn, int *pOut, int n) {
pDet->process(pIn, pOut, n);
}
SIM_EXPORT void DetectorDelete(Detector *pDet) { delete pDet; }
#if defined(__cplusplus)
}
#endif
code.py :
import sys
from ctypes import CDLL, POINTER, \
c_int, c_void_p
import numpy as np
sim_dll = CDLL("./sim.dll")
detector_new_func = sim_dll.DetectorNew
detector_new_func.restype = c_void_p
detector_process_func = sim_dll.DetectorProcess
detector_process_func.argtypes = [c_void_p, POINTER(c_int), POINTER(c_int), c_int]
detector_delete_func = sim_dll.DetectorDelete
detector_delete_func.argtypes = [c_void_p]
class Detector():
def __init__(self):
self.obj = detector_new_func()
def process(self, pin, pout, n):
detector_process_func(self.obj, pin, pout, n)
def __del__(self):
detector_delete_func(self.obj)
def main():
detector = Detector()
n = 1024
a = np.arange(n, dtype=np.uint32)
b = np.zeros(n, dtype=np.int32)
aptr = a.ctypes.data_as(POINTER(c_int))
bptr = b.ctypes.data_as(POINTER(c_int))
detector.process(aptr, bptr, n)
if __name__ == "__main__":
print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
main()
ملاحظات :
-
كما ذكرت في البداية ، كانت المشكلة هي عدم تحديد
الأنواع
وإعادة
الكتابة
(على سبيل المثال بالنسبة إلى
DetectorNew
: comment
detector_new_func.restype = c_void_p
،detector_new_func.restype = c_void_p
المشكلة مرة أخرى) - تفتقد الشفرة في السؤال أجزاء ( # تتضمن s ، import s ، ...) ، وهناك أيضًا بعض أخطاء بناء الجملة ، لذلك لا يتم تجميعها ، وبالتالي لا تتبع [SO]: كيفية إنشاء الحد الأدنى ، إرشادات مثال كاملة وقابلة للتحقق (mcve) . يرجى عند التأكد من أن يكون mcve عند السؤال
-
يجب أيضًا إلغاء تخصيص الكائن الذي تقوم بتخصيصه (
new Detector()
) (وإلا ، فسيؤدي ذلك إلى حدوث تسرب للذاكرة ) ، لذا قمت بإضافة وظيفة ( DetectorDelete - للقيام بذلك) ، والتي يتم استدعاؤها من ( Python ) Detector 's المدمر - التغييرات الأخرى (غير الحرجة) (إعادة تسمية المعرفات ، القليل من إعادة البناء ، الطباعة إلى stdout ، ...)
الإخراج :
(py35x64_tes1) e:\Work\Dev\StackOverflow\q052268294>"c:\Install\x86\Microsoft\Visual Studio Community\2015\vc\vcvarsall.bat" x64 (py35x64_test) e:\Work\Dev\StackOverflow\q052268294>dir /b code.py detector.cpp (py35x64_test) e:\Work\Dev\StackOverflow\q052268294>cl /nologo /DDLL /EHsc detector.cpp /link /DLL /OUT:sim.dll detector.cpp Creating library sim.lib and object sim.exp (py35x64_test) e:\Work\Dev\StackOverflow\q052268294>dir /b code.py detector.cpp detector.obj sim.dll sim.exp sim.lib (py35x64_test) e:\Work\Dev\StackOverflow\q052268294>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" ./code.py Python 3.5.4 (v3.5.4:3f56838, Aug 8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32 From C - [detector.cpp] (28) - [Detector::Detector]: &m_var: 0x0000020CE366E270 From C - [detector.cpp] (34) - [Detector::process]: &m_var: 0x0000020CE366E270
كتبت مكتبة dll في c ، وقمت بالتجميع باستخدام vs2017 64-bit ، وحاول تحميلها باستخدام python3.6 64-bit. ومع ذلك ، يتم اقتطاع عنوان متغير العضو الخاص بالكائن إلى 32 بت.
فيما يلي ملف sim.c الخاص بي ، والذي تم ترجمته إلى sim.dll:
class Detector {
public:
Detector();
void process(int* pin, int* pout, int n);
private:
int member_var;
};
Detector::Detector()
{
memset(&member_var, 0, sizeof(member_var));
myfile.open("addr_debug.txt");
myfile << "member_var init address: " << &member_var << endl;
}
void Detector::process(int* pin, int* pout, int n);
{
myfile << "member_var process address: " << &member_var << endl;
myfile.close();
}
#define DllExport __declspec( dllexport )
extern "C" {
DllExport Detector* Detector_new() { return new Detector(); }
DllExport void Detector_process(Detector* det, int* pin, int* pout, int n)
{
det->process(pin, pout, n);
}
}
هنا سيناريو بيثون الخاص بي:
from ctypes import cdll
lib = cdll.LoadLibrary(r'sim.dll')
class Detector(object):
def __init__(self):
self.obj = lib.Detector_new()
def process(self,pin, pout, n):
lib.Detector_process(self.obj,pin, pout, n)
detector = Detector()
n = 1024
a = np.arange(n, dtype=np.uint32)
b = np.zeros(n, dtype=np.int32)
aptr = a.ctypes.data_as(ctypes.POINTER(ctypes.c_int))
bptr = b.ctypes.data_as(ctypes.POINTER(ctypes.c_int))
detector.process(aptr, bptr, n)
إليك عنوان member_var في addr_debug.txt:
member_var init address: 0000025259E123C4
member_var process address: 0000000059E123C4
الوصول إلى ذلك يؤدي إلى خطأ في الوصول إلى الذاكرة:
OSError: exception: access violation reading 0000000059E123C4
بعض المحاولات التي حاولت فهمها:
- تعريف member_var بأنه عام بدلاً من عنوان خاص وليس مساعدة ، لا يزال العنوان مبتوراً.
- حدد member_var كمتغير عام ، ثم العنوان على ما يرام. لذلك أعتقد أن اقتطاع عنوان member_var يحدث إما عند إرجاع الكائن إلى python ، أو تمرير الكائن مرة أخرى إلى dll.