Don't know much about ctypes, just recently began working with it.
I have a simple function in C-like dll which returns a pointer to the dynamically generated string.
It is working fine, but, because i manually allocated memory for the string, i should free it after use.
I have something like this:
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;
}
But i don't have any idea, how can i pass received pointer back for deletion in Python?
You're on the right track.
// 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;
}
And in python:
# 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)
Normally each function you use in ctypes
should have its arguments and return type declared so Python can check for the correct number and type of arguments and convert Python object arguments to the correct C data objects. Unfortunately in this case, the normal return value for func
would be c_char_p
, but ctypes
tries to be helpful and convert a c_char_p
return value to a Python string, losing access to the raw C pointer value. Instead, you can declare the return type as c_void_p
and use cast
to retrieve the string value, which leaves the return value a c_char_p
object.
Here's an example (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)