Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to pass a python string by reference through ctypes?

Tags:

python

ctypes

I'm sorry, but I generally have a hard time reading the current ctypes docs...

If I have a C function that takes a const char * pointer, and I know it will neither modify the passed in string, nor keep a reference to it beyond the function call, it really makes sense to pass in a pointer directly to the bytes of a python string.

Can ctypes can do this or is it just plain unsupported? Do I really have to create_string_buffer and copy my string into it?

like image 500
porgarmingduod Avatar asked Apr 07 '11 06:04

porgarmingduod


People also ask

What does ctypes do in Python?

ctypes is a foreign function library for Python. It provides C compatible data types, and allows calling functions in DLLs or shared libraries. It can be used to wrap these libraries in pure Python.

Are strings passed by value in Python?

Python utilizes a system, which is known as “Call by Object Reference” or “Call by assignment”. In the event that you pass arguments like whole numbers, strings or tuples to a function, the passing is like call-by-value because you can not change the value of the immutable objects being passed to the function.

How are strings passed in Python?

The pythonic way of dealing with this is to hold strings in an list and join them together once you have all the pieces. Python never passes by reference. It passes references, but "pass by reference" is already taken for another argument passing style (one which permits, for example, the function swap(a, b) ).

Does ctypes work with C++?

ctypes is the de facto standard library for interfacing with C/C++ from CPython, and it provides not only full access to the native C interface of most major operating systems (e.g., kernel32 on Windows, or libc on *nix), but also provides support for loading and interfacing with dynamic libraries, such as DLLs or ...


2 Answers

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.

like image 144
bryanegr Avatar answered Oct 17 '22 00:10

bryanegr


The type ctypes.c_char_p represents a nul-terminated string. If a 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:

#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('x')
>>> 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'
>>>
like image 27
Mark Tolonen Avatar answered Oct 17 '22 01:10

Mark Tolonen