2012-12-17 21 views
7

Niewiele wiadomo na temat ctypów, niedawno zaczęto z nim pracować.Jak przekazać wskaźnik z powrotem w ctypes?

Mam prostą funkcję w C-like dll, która zwraca wskaźnik do dynamicznie generowanego łańcucha.
Działa poprawnie, ale ponieważ ręcznie przydzielono pamięć dla ciągu znaków, powinienem zwolnić ją po użyciu.

mam coś takiego:

extern "C" char* DLL_EXPORT func(const char* str1, const char* str2) 
{ 
    return getSomeString(str1, str2); 
} 

// Goal is to call this function correctly from Python.  
extern "C" void DLL_EXPORT freeMem(void *mem) 
    { 
     if(mem!=NULL) 
      delete mem; 
    } 

Ale nie mam pojęcia, w jaki sposób można przekazać otrzymane wskaźnik z powrotem do usunięcia w Pythonie?

+0

Zobacz http://stackoverflow.com/questions/7398575/deallocating-memory-for-objects-returned-to-python-through-ctypes. –

Odpowiedz

7

Jesteś na dobrej drodze.

// TestDLL.cpp 
#include <string.h> // strcpy 

extern "C" __declspec(dllexport) char* stringdup(const char* str) { 
    char* p = new char[strlen(str)+1]; 
    strcpy(p,str); 
    return p; 
} 

// if you have no good reason to use void*, use the type 
// you've allocated. while it usually works for built-in 
// types, it wouldn't work for classes (it wouldn't call 
// the destructor) 
extern "C" __declspec(dllexport) void stringfree(char* ptr) { 
    // you don't need to check for 0 before you delete it, 
    // but if you allocate with new[], free with delete[] ! 
    delete [] ptr; 
} 

A w Pythonie:

# Test.py 
import ctypes 

lib = ctypes.cdll.TestDLL 

# this creates a c-style char pointer, initialized with a string whose 
# memory is managed by PYTHON! do not attempt to free it through the DLL! 
cstr = ctypes.c_char_p("hello ctypes") 

# call the dll function that returns a char pointer 
# whose memory is managed by the DLL. 
p = lib.stringdup(cstr) 

# p is just an integer containing the memory address of the 
# char array. therefore, this just prints the address: 
print p 

# this prints the actual string 
print ctypes.c_char_p(p).value 

# free the memory through the DLL 
lib.stringfree(p) 
+2

'stringdup.restype' musi być ustawiony na typ wskaźnika, taki jak' POINTER (c_char) ', ponieważ domyślny typ wyniku to 32-bitowy' int'. Aby uzyskać ciąg znaków w języku Python, można użyć opcji 'cast (p, c_char_p) .value' lub ' c_char_p.from_buffer (p) .value'. – eryksun

8

Normalnie każda funkcja użyć w ctypes powinien mieć argumenty i typ zwracany ogłosił więc Python można sprawdzić za prawidłowe liczby i typu argumentów i konwertować Python obiekt arguments do poprawnych obiektów danych C. Niestety w tym przypadku normalną wartością zwracaną dla func będzie c_char_p, ale ctypes próbuje być pomocne i przekształcić wartość zwracaną c_char_p na łańcuch Pythona, tracąc dostęp do surowej wartości wskaźnika C. Zamiast tego możesz zadeklarować typ zwracania jako c_void_p i użyć cast, aby pobrać wartość ciągu, która pozostawia wartość zwracaną obiektu c_char_p.

Oto przykład (Python 3):

import ctypes 

func = ctypes.cdll.TestDLL.func 
func.argtypes = [ctypes.c_char_p,ctypes.c_char_p] 
func.restype = ctypes.c_void_p 

freeMem = ctypes.cdll.TestDLL.freeMem 
freeMem.argtypes = [ctypes.c_void_p] 
freeMem.restype = None 

s = func(b'abcdef',b'ghijkl') 
s = ctypes.cast(s,ctypes.c_char_p) 
print(s.value) 
freeMem(s) 
Powiązane problemy