Assigning a new value to instances of the pointer types c_char_p, c_wchar_p, and c_void_p changes the memory location they point to, not the contents of the memory block (of course not, because Python strings are immutable):
>>> s = "Hello, World" >>> c_s = c_char_p(s) >>> print c_s c_char_p('Hello, World') >>> c_s.value = "Hi, there" >>> print c_s c_char_p('Hi, there') >>> print s # first string is unchanged Hello, World >>>You should be careful, however, not to pass them to functions expecting pointers to mutable memory. If you need mutable memory blocks, ctypes has a create_string_buffer function which creates these in various ways. The current memory block contents can be accessed (or changed) with the raw property, if you want to access it as NUL terminated string, use the string property:
Says the ctypes tutorial. What I gather from this is that only if the function would work with a const char*, would passing in the python string be valid. Keep in mind, it won't have a null termination.
I'd suggest using create_string_buffer anyhow.
Assigning a new value to instances of the pointer types c_char_p, c_wchar_p, and c_void_p changes the memory location they point to, not the contents of the memory block (of course not, because Python strings are immutable):
>>> s = "Hello, World" >>> c_s = c_char_p(s) >>> print c_s c_char_p('Hello, World') >>> c_s.value = "Hi, there" >>> print c_s c_char_p('Hi, there') >>> print s # first string is unchanged Hello, World >>>You should be careful, however, not to pass them to functions expecting pointers to mutable memory. If you need mutable memory blocks, ctypes has a create_string_buffer function which creates these in various ways. The current memory block contents can be accessed (or changed) with the raw property, if you want to access it as NUL terminated string, use the string property:
Says the ctypes tutorial. What I gather from this is that only if the function would work with a const char*, would passing in the python string be valid. Keep in mind, it won't have a null termination.
I'd suggest using create_string_buffer anyhow.
The type ctypes.c_char_p represents a nul-terminated string. If a C function takes a const char* you can pass a Python string to it and it will receive a nul-terminated version.
A Windows example DLL:
// test.c - cl /LD test.c
#include <string.h>
__declspec(dllexport)
char* func(char* a, size_t len, const char* b) {
if(strlen(b) * 2 >= len)
return NULL;
strcpy_s(a, len, b);
strcat_s(a, len, b);
return a;
}
Python:
Python 2.7.1 (r271:86832, Nov 27 2010, 18:30:46) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from ctypes import *
>>> x = CDLL('test')
>>> x.func.restype = c_char_p
>>> x.func.argtypes = c_char_p, c_int, c_char_p
>>> s = create_string_buffer(10)
>>> x.func(s, len(s), 'abcd')
'abcdabcd'
from ctypes import *
charptr = POINTER(c_char)
test = CDLL('test.so')
test.initializetest.argtypes = []
test.initializetest.restype = charptr
test.searchtest.argtypes = [charptr]
test.searchtest.restype = c_int
buf = test.initializetest()
test.searchtest(buf)
print cast(buf, c_char_p).value
# TODO Release the "buf" memory or it will leak.
EDIT
Initially I used c_char_p to pass the buffer between the functions but c_char_p is like a const pointer. If used as a restype, you will actually get a Python str back. So for initializetest it will create a string from the allocated memory (by copying data) and throw the pointer away.
Now we're creating a new type, a POINTER to c_char. This is then used in both functions.
For Python, this type points to a single char so we have to cast it to get the whole string after searchtest is done. We cast to c_char_p because we just want to read the value so a const pointer is OK.
As a side note, this illustrates the disastrous effect of using c_char_p with functions that modify the array (as searchtest above does):
>>> libc.memset.argtypes = [c_char_p, c_int, c_int]
>>> foo = 'Python'
>>> foo
'Python'
>>> libc.memset(foo, ord('x'), 3)
44808532
>>> foo
'xxxhon'
>>>
Note how we've managed to change an immutable Python string!
The argtypes setup line isn't even needed because ctypes assumes c_char_p if Python str is used as argument.
Maybe by using restype like described here
class Test(object):
def __init__(self):
self.test_library=CDLL("./test.so")
self.test_initialize = self.test_library.initializetest
self.test_initialize.argtypes = []
self.test_initialize.restype = c_char_p # c_char_p is a pointer to a string
self.test_search = self.test_library.searchtest
self.test_search.restype = c_int
self.test_search.argtypes = [c_char_p]
self.m = c_char_p(self.test_initialize())
def search(self):
return self.test_search(self.m).value
r = Test()
print r.search()
EDIT: corrected after test :)
There are a number of problems:
- Define
.argtypesfor your functions. It will catch errors passing incorrect parameters. Add the line below and note it is plural and is a tuple of argument types. The comma makes a 1-tuple:
python_p_printme.argtypes = c_char_p,
Once you make that change, you'll get an error because this code:
c_sends = pointer(c_char_p(CHAR.encode('utf-8')
is actually sending a C char** (a pointer to a c_char_p). Once you've set the argtypes properly, you can just call the function with a byte string and it will work. Your function becomes:
def printmeP(CHAR):
print("In Print function")
print(CHAR)
python_p_printme(CHAR.encode())
print("DONE function - Python")
- There is one more subtle problem. While the program may appear to work at this point Python strings are immutable so if the function being called requires a mutable string, you must allocate a mutable buffer using either
create_unicode_buffer(for c_wchar_p) orcreate_string_buffer(for c_char_p); otherwise, thestrcatin your C code is going to corrupt the Python string.
Here's a full example:
test.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// For Windows compatibility
#ifdef _WIN32
# define API __declspec(dllexport)
#else
# define API
#endif
// For C++ compiler compatibility
#ifdef __cplusplus
extern "C" {
#endif
API void alter_me(char* param, size_t length) {
// truncates if additional info exceeds length
strncat_s(param, length, " adding some info", length - 1);
}
#ifdef __cplusplus
}
#endif
test.py
from ctypes import *
lib = CDLL('./test')
alter_me = lib.alter_me
alter_me.argtypes = c_char_p,c_size_t
alter_me.restype = None
data = create_string_buffer(b'test',size=10)
alter_me(data,sizeof(data))
print(data.value)
data = create_string_buffer(b'test',size=50)
alter_me(data,sizeof(data))
print(data.value)
Output:
b'test addi'
b'test adding some info'
Note you do not need to use create_string_buffer if the C function does not alter the buffer, such as if the C parameter is const char*. Then you could just call printme(b'test string').
You could use create_string_buffer.
The documentation can be found here: https://docs.python.org/3/library/ctypes.html#ctypes.create_string_buffer
ctypes.create_string_buffer(init_or_size, size=None)
This function creates a mutable character buffer. The returned object is a ctypes array of c_char.
init_or_size must be an integer which specifies the size of the array, or a bytes object which will be used to initialize the array items.
With buf.value.decode("utf-8") you can convert the buffer back to a UTF-8 python string.
A small example with your C code library might look like this:
from ctypes import *
mylib_lib = cdll.LoadLibrary("<your-lib>")
mylib_lib.printme.argtypes = c_char_p,
buf = create_string_buffer(128)
buf.value = b'Hello World'
mylib_lib.printme(buf)
print("print on python side:", buf.value.decode("utf-8"))
It would output:
Start c Function!
Hello World
...
print on python side: Hello World!
on the console.
The C printf function is expecting byte strings. In Python 3 all strings are unicode so you'll have to encode to bytes:
>>> msvcrt.printf("Testing: %s".encode('ascii'), message_string.encode('ascii'))
Testing: Hello world!
22
If you have any non-ascii characters then encode to the relevant windows codepage instead.
bleh, using "".encode('ascii') is ugly. You can often get away with just doing this:
TTF_OpenFont(b"assets/droid.ttf", 10)
^^
Note the 'b' type for the string. This is portable to python 2.7 as well.
Thanks to @ErykSun the solution:
Python code
string1 = "my string 1"
string2 = "my string 2"
# create byte objects from the strings
b_string1 = string1.encode('utf-8')
b_string2 = string2.encode('utf-8')
# send strings to c function
my_c_function.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
my_c_function(b_string1, b_string2)
I think you just need to use c_char_p() instead of create_string_buffer().
string1 = "my string 1"
string2 = "my string 2"
# create byte objects from the strings
b_string1 = string1.encode('utf-8')
b_string2 = string2.encode('utf-8')
# send strings to c function
my_c_function(ctypes.c_char_p(b_string1),
ctypes.c_char_p(b_string2))
If you need mutable strings then use create_string_buffer() and cast those to c_char_p using ctypes.cast().
You think correctly. That code works. Call with, for example:
setParameters(['abc','def','ghi'])
I haven't looked at the source, but ctypes on Python 2.7 (32-bit) does pass the internal Python buffer (tested by modifying the passed string in the C function and printing it after the call in Python), which is why you should only pass Python strings as above to functions that take const char* or at least are known not to modify the string. Since Python strings are immutable writing to them under C is a no-no. Use ctypes.create_string_buffer to create a writable string to pass to non-const char*.
I needed to make the following adjustments to the for loop in the setParameters python function
for i, param in enumerate(strParamList):
if isinstance(param, bytes):
strArray[i] = c_char_p(param)
else:
strArray[i] = c_char_p(param.encode('utf-8'))
This worked if the array argument to setParameters was [b'abc', b'def'] or ['abc','def'].
def call_c(L):
arr = (ctypes.c_char_p * len(L))()
arr[:] = L
lib.external_C(len(L), arr)
Thank you very much; that worked like charm. I also did an alternative variation like this:
def call_c( L ):
arr = (ctypes.c_char_p * (len(L) + 1))()
arr[:-1] = L
arr[ len(L) ] = None
lib.external_C( arr )
And then in C-function I iterated through the (char **) list until I found a NULL.