ctypes.cast() is used to convert one ctype instance to another ctype datatype. You don't need it To convert it to python string. Just use ".value" to get it in python string.
>>> s = "Hello, World"
>>> c_s = c_char_p(s)
>>> print c_s
c_char_p('Hello, World')
>>> print c_s.value
Hello, World
More info here
Answer from Rajendran T on Stack Overflowctypes.cast() is used to convert one ctype instance to another ctype datatype. You don't need it To convert it to python string. Just use ".value" to get it in python string.
>>> s = "Hello, World"
>>> c_s = c_char_p(s)
>>> print c_s
c_char_p('Hello, World')
>>> print c_s.value
Hello, World
More info here
If you set the argtypes or restype attributes of ctypes functions, they will return the right Python object without the need for a cast.
Here's an example calling the C-runtime time and ctime functions:
>>> from ctypes import *
>>> m=CDLL('msvcrt')
>>> t=c_long(0)
>>> m.time(byref(t))
1326700130
>>> m.ctime(byref(t)) # restype not set
6952984
>>> m.ctime.restype=c_char_p # set restype correctly
>>> m.ctime(byref(t))
'Sun Jan 15 23:48:50 2012\n'
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().
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.
As you said, use a POINTER(c_char) to get a pointer to the array of binary data. To put that together into a string, you can just take a slice of it, since array indexing works as expected with ctypes pointers:
clibblah = ctypes.cdll.LoadLibrary('clibblah.dylib')
get_pixels = clibblah.get_pixels
get_pixels.restype = ctypes.POINTER(ctypes.c_char)
pixels = get_pixels()
num_pixels = clibblah.get_pixel_length()
# Slice the ctypes array into a Python string
a = pixels[:num_pixels]
There are a few different methods. I like ctypes.string_at because it isn't finicky: it works regardless of whether you supply a c_char_p type, or a pointer-to-c_char, or a void pointer type, or even just an int address.
s = b'hello\x00world' # create a string containing null bytes
sz = len(s)
from ctypes import *
p = c_char_p(s) # obtain a pointer of various types
p2 = cast(p,POINTER(c_char))
address = cast(p,c_void_p).value
print p.value # by default it is interpreted as null-terminated
print p2[:sz] # various methods of explicitly specifying the full length
print string_at(p,size=sz)
print (c_char * sz).from_address(address).raw