Python ctypes cdll.LoadLibrary, crea una instancia de un objeto, ejecuta su método, se trunca la dirección de la variable privada
windows (1)
Escribí una biblioteca dll en c, compilo con vs2017 de 64 bits, y trato de cargarla con python3.6 de 64 bits. Sin embargo, la dirección de la variable miembro del objeto se truncó a 32 bits.
Aquí está mi archivo sim.c, que se compila a 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);
}
}
Aquí está mi script de python:
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)
Aquí está la dirección de la member_var en addr_debug.txt:
member_var init address: 0000025259E123C4
member_var process address: 0000000059E123C4
Así que accediendo a ella desencadena error de acceso a memoria:
OSError: exception: access violation reading 0000000059E123C4
Algunos intentos intenté entender el problema:
- Defina member_var como público en lugar de privado, no ayuda, la dirección aún está truncada.
- Defina member_var como variable global, entonces la dirección está bien. Así que supongo que el truncamiento de la dirección member_var ocurre cuando se devuelve el objeto a Python, o se devuelve el objeto a dll.
Siempre (CORRECTAMENTE) especifique argtypes y vuelva a escribir para las funciones definidas en C , de lo contrario (estilo C89 ) se predeterminarán a int ( 32 bits ), generando !!! Comportamiento indefinido !!! . En 64 bits , puede provocar un truncamiento (que es exactamente lo que está experimentando).
Además, cuando tenga problemas, no se olvide de [Python 3.Docs]: ctypes - Una biblioteca de funciones ajena a Python .
A continuación se muestra una versión adaptada de su código.
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()
Notas :
-
Como dije al principio, el problema fue que no se especificaron los
argumentos
y los
tipos de reingreso
(por ejemplo, para
DetectorNew
: comment
detector_new_func.restype = c_void_p
, y volverá a encontrar el problema) - En el código de la pregunta faltan partes ( #include s, import s, ...), también hay algunos errores de sintaxis, por lo que no se compila, y por lo tanto no sigue [SO]: Cómo crear un Mínimo, Ejemplo completo, y verificable (mcve) . Por favor, cuando esté seguro de tener mcve al preguntar
-
El objeto que asigne (
new Detector()
), también debe ser desasignado (de lo contrario, generará una pérdida de memoria ), así que agregué una función ( DetectorDelete - para hacer eso), que se llama desde ( Python ) Detector ''s incinerador de basuras - Otros cambios (no críticos) (cambio de nombre de identificadores, un poco de refactorización, impresión en stdout , ...)
Salida :
(py35x64_tes1) e:/Work/Dev//q052268294>"c:/Install/x86/Microsoft/Visual Studio Community/2015/vc/vcvarsall.bat" x64 (py35x64_test) e:/Work/Dev//q052268294>dir /b code.py detector.cpp (py35x64_test) e:/Work/Dev//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//q052268294>dir /b code.py detector.cpp detector.obj sim.dll sim.exp sim.lib (py35x64_test) e:/Work/Dev//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