Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python ctypes: Passing an array of strings

I have an array of strings in Python 2.7, which I would like to pass to a C function via ctypes:

unsigned int SetParams(unsigned int count, const char **params)

So I can define the parameters in python:

import ctypes as ct
lib = ct.cdll.LoadLibrary('...')
lib.SetParams.restype  = ct.c_uint
lib.SetParams.argtypes = [ct.c_uint, ct.POINTER(ct.c_char_p)]

but now when I am given a set of params in a Python function, which I would like to use in the above library call, how do I actually call it? I am thinking something like this:

def setParameters(strParamList):
    numParams    = len(strParamList)
    strArrayType = ct.c_char_p * numParams
    strArray     = strArrayType()
    for i, param in enumerate(strParamList):
        strArray[i] = param
    lib.SetParams(numParams, strArray)

I am just starting with ctypes and wondering if there is a more automated way to do this? Also is the assignment strArray[i] = param actually reallocating? If so, this seems quite expensive -- is there a way to do this just pointing the string pointers to Python-allocated buffers, or they are not NULL-terminated?

UPDATE I did look through a couple related questions, but could not find a direct one to deal with the same issues. Thank you very much

like image 585
gt6989b Avatar asked Nov 09 '22 05:11

gt6989b


2 Answers

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*.

like image 62
Mark Tolonen Avatar answered Nov 14 '22 23:11

Mark Tolonen


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'].

like image 24
Cathy Avatar answered Nov 14 '22 23:11

Cathy