Re: [pypy-dev] How to convert python string to C char*?

661 views
Skip to first unread message

Armin Rigo

unread,
May 14, 2015, 4:14:07 AM5/14/15
to Yicong Huang, pypy-dev, pytho...@googlegroups.com
Hi Yicong,

(CC to the cffi mailing list)

On 14 May 2015 at 05:02, Yicong Huang <hengh...@gmail.com> wrote:
> We had a python function that return a string value. The function will
> callback in C code.
> The below is an example of the code:
>
> @ffi.callback("char *(char *, char *)")
> def strconcat(x, y):
> x1 = ffi.string(x)
> y1 = ffi.string(y)
> print x1 + y1
> return x1 + y1
>
> The error messages are:
> Trying to convert the result back to C:
> TypeError: initializer for ctype 'char *' must be a cdata pointer, not str
>
> How could we convert the return python string value to C char*?

This is not obvious, because you have problems writing it in C too:
returning a "char *" is only possible when you return some constant
string literal (only in C, can't do that with cffi), or when you
allocate the resulting string and keep it alive for as long as the
caller needs it (and you don't really know how long that is).

If you have a callback to a C library that you didn't write, it must
be written in its documentation. If it is not, and you really have no
choice, then you have to guess. The following would work in all cases
but leak the strings by never releasing them:

ALL_RESULTS = []
@ffi.callback("char *(char *, char *)")
def strconcat(x, y):
x1 = ffi.string(x)
y1 = ffi.string(y)
print x1 + y1
p = ffi.new("char[]", x1 + y1)
ALL_RESULTS.append(p)
return p

There are chances that it works as expected if you only keep alive the
last returned result; try it out, but no guarantee.


A bientôt,

Armin.

Yicong Huang

unread,
May 14, 2015, 5:40:04 AM5/14/15
to pytho...@googlegroups.com, ar...@tunes.org, pypy...@python.org
Thanks, the method did work.
To my understanding, "ALL_RESULTS" is used to prevent pypy GC the pointing buffer. 
And thus, it is the C callback function's responsibility to free the buffer.
Am I right? 

Armin Rigo

unread,
May 14, 2015, 6:30:01 AM5/14/15
to Yicong Huang, pypy-dev, pytho...@googlegroups.com
Hi Yicong,

On 14 May 2015 at 11:33, Yicong Huang <hengh...@gmail.com> wrote:
> To my understanding, "ALL_RESULTS" is used to prevent pypy GC the pointing
> buffer.
> And thus, it is the C callback function's responsibility to free the buffer.
> Am I right?

No. In my example I just keep storing ffi.new("char[]") objects in
the list, so this list grows forever and leaks.

You really need to see the documentation of the C code for which you
are making a callback. Maybe it says that the callback needs to
return a string obtained by ``malloc()``, and it will be ``free()``-ed
for you. In that case you need to call ``lib.malloc()`` from the
library obtained by cffi, after adding "char *malloc(size_t);" to the
cdef().

But I can't guess if I'm correct; it depends on the C code.


A bientôt,

Armin.
Reply all
Reply to author
Forward
0 new messages