Array of pointers to character string. (***char)

451 views
Skip to first unread message

Gary Pajer

unread,
Jun 2, 2016, 5:31:42 PM6/2/16
to python-cffi
cffi newbie.
python 2.7

I'm calling a c function who's signature is like  foo(char*** names).  I'd like to recover the contents of names.   I'm no C expert, but I believe I'm getting an array of  c strings in the form a an array of pointers to strings.  Here's where I tried to start.


names
= ffi.new('char[1024]', "\0")
out = lib.foo(names)
print('names = {0}'.format(ffi.string(names)))



Which doesn't seem to work. Nor did I really expect it to.
Help appreciated.

Armin Rigo

unread,
Jun 3, 2016, 2:11:27 PM6/3/16
to pytho...@googlegroups.com
Hi Gary,

On 2 June 2016 at 23:31, Gary Pajer <gary....@gmail.com> wrote:
> I'm calling a c function who's signature is like foo(char*** names).

If that's what you put in ffi.cdef(), then this cannot work:

> names = ffi.new('char[1024]', "\0")
> out = lib.foo(names)

because "names" is a "char[]" or a "char *" in C; it is not a "char ***".

I'm not sure about the exact way you're supposed to call this
particular 'foo(char ***)'. Do you have an example of calling it from
C? There are many ways in which a "char ***" argument can be used to
return a result: foo() can expect a pointer to a single "char **"
variable and write to this variable; or it can expect a pointer to an
array of "char *" and fill the array (up to some maximum length, which
is usually given as another argument); or... well, I'll ignore the
rarer possibilities here. The point is that we can't guess just from
the signature of foo(): we need to learn that from additional
documentation or an example.


A bientôt,

Armin.

Gary Pajer

unread,
Jun 6, 2016, 2:14:34 PM6/6/16
to python-cffi, ar...@tunes.org
Thanks, Armin.

Here's exactly as it's called in C.  The argument &tmplist is the one that's specified char*** in the signature.   FYI, this reads data from a Finger Lakes Instrumentation camera.
The data we want is at the beginning of the string, and we don't care about the ";" and everything after the ";".
The printf() statement does print the string.   Regards!  

p.s.  I have a related question, but the answer to this might answer my second question, so I'll wait on the second question.


                       
//  Get list of devices
                       
char **tmplist;
                       
FLIList(FLIDOMAIN_USB | FLIDEVICE_CAMERA, &tmplist);
                        printf
("\nDevice List:\n %s\n", *tmplist);

                       
// Extract the address of the camera from tmplist.
 
                       // It will end up in variable tmplist
                       
// We identify the end of the device addrss by the character ';'

                       
for (int i=0; tmplist[i] != NULL; i++)
                       
{
                               
for (int j = 0; tmplist[i][j] != '\0'; j++)
                                       
if (tmplist[i][j] == ';')
                                       
{
                                                tmplist
[i][j] = '\0';
                                               
break;
                                       
}
                       
}

Armin Rigo

unread,
Jun 7, 2016, 4:33:45 AM6/7/16
to pytho...@googlegroups.com
Hi Gary,

On 6 June 2016 at 20:14, Gary Pajer <gary....@gmail.com> wrote:
> char **tmplist;
> FLIList(FLIDOMAIN_USB | FLIDEVICE_CAMERA, &tmplist);

In CFFI, you write:

tmplist_p = ffi.new("char ***")
FLIList(..., tmplist_p)
tmplist = tmplist_p[0]

> printf("\nDevice List:\n %s\n", *tmplist);

print "Device List:"
print ffi.string(tmplist[0])

> for (int i=0; tmplist[i] != NULL; i++)
> {
> for (int j = 0; tmplist[i][j] != '\0'; j++)
> if (tmplist[i][j] == ';')
> {
> tmplist[i][j] = '\0';
> break;
> }
> }

This part can have a one-to-one mapping to "while" loops, or else you
can use some more pythonic string manipulations instead of the inner
loop.

i = 0
while tmplist[i] != ffi.NULL:
s = ffi.string(tmplist[i])
if ';' in s:
s = s[:s.index(';')]
...
i += 1

Note also that if you have some example of C code that is hard to
follow, like this, you can also paste it verbatim in ffi.set_source(),
and put it inside another function which does only what you want
instead and has got a simpler C interface:

void print_stuff_to_stdout(void)
{
// all the code above is here
}

then you only have to mention ffi.cdef("void
print_stuff_to_stdout(void);") and call lib.print_stuff_to_stdout().
Of course you may want to grab some string instead of printing it to
stdout, then it becomes for example "char *fetch_stuff(void)" if there
is no concern about freeing the memory, or else more traditionally:

int fetch_stuff(char *out_buf, size_t bufsize)
{
// fetch...
strncpy(out_buf, tmplist[i], bufsize); // copies to out_buf
return 0; // 'success' indicator
}


A bientôt,

Armin.

Gary Pajer

unread,
Jun 9, 2016, 4:05:13 PM6/9/16
to python-cffi, ar...@tunes.org


On Tuesday, June 7, 2016 at 4:33:45 AM UTC-4, Armin Rigo wrote:
Hi Gary,

On 6 June 2016 at 20:14, Gary Pajer <gary....@gmail.com> wrote:
>                         char **tmplist;
>                         FLIList(FLIDOMAIN_USB | FLIDEVICE_CAMERA, &tmplist);

In CFFI, you write:

Beautiful.   You nailed it.

Thanks for the example.  I'll have to study that to see what you did.  I haven't looked closely at set_source.  I'll be looking into that.
Also, I haven't tried your fix on my second question ... slightly different  ... if I have trouble with that I'll ask for help.

Thanks very much
Reply all
Reply to author
Forward
0 new messages