41

I am trying to send 2 strings from Python (3.2) to C using ctypes. This is a small part of my project on my Raspberry Pi. To test if the C function received the strings correctly, I place one of them in a text file.

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(ctypes.create_string_buffer(b_string1),
              ctypes.create_string_buffer(b_string2))

C code

void my_c_function(const char* str1, const char* str2)
{
    // Test if string is correct
    FILE *fp = fopen("//home//pi//Desktop//out.txt", "w");
    if (fp != NULL)
    {
        fputs(str1, fp);
        fclose(fp);
    }

    // Do something with strings..
}

The problem

Only the first letter of the string appears in the text file.

I've tried many ways to convert the Python string object with ctypes.

  • ctypes.c_char_p
  • ctypes.c_wchar_p
  • ctypes.create_string_buffer

With these conversions I keep getting the error "wrong type" or "bytes or integer address expected instead of str instance".

I hope someone can tell me where it goes wrong. Thanks in advance.

4
  • 9
    Set my_c_function.argtypes = [ctypes.c_char_p, ctypes.c_char_p]. Then, because the parameters are const, simply call it as my_c_function(b_string1, b_string2). Commented Nov 25, 2014 at 14:38
  • 2
    FYI, a literal backslash character needs to be escaped as "\\", but it's not required for a forward slash. It's just "/home/pi/Desktop/out.txt". Commented Nov 25, 2014 at 14:42
  • 1
    @eryksun Thanks for your reply. It works now, I totally forgot I had argtypes still set on c_wchar_p. About the slashes, I always get them mixed up. Commented Nov 25, 2014 at 15:03
  • 5
    Only use buf = ctypes.create_string_buffer(bstr) when the function modifies the string. It's equivalent to buf = (ctypes.c_char * (len(bstr) + 1))(); buf.value = bstr. Commented Nov 25, 2014 at 15:12

3 Answers 3

50

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)
Sign up to request clarification or add additional context in comments.

5 Comments

my_c_function.argtypes = [ctypes.c_char_p, ctypes_char_p] You mean my_c_function.argtypes = [ctypes.c_char_p, ctypes.char_p] (notice . instead of _)?
Haha, after 5 years someone noticed or felt bothered to point it out. Thanks for it, I edited it in the answer.
You know, Obsessive-Compulsive Disorder!
my_c_function.argtypes = [ctypes.c_char_p, ctypes.char_p] Do you mean my_c_function.argtypes = [ctypes.c_char_p, ctypes.c_char_p] ?
This is incorrect. encode does not create NULL-terminated strings which your C function expects.
20

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().

Comments

1

Have you considered using SWIG? I haven't tried it myself but here's what it would look like, without changing your C source:

/*mymodule.i*/

%module mymodule
extern void my_c_function(const char* str1, const char* str2);

This would make your Python source as simple as (skipping compilation):

import mymodule

string1 = "my string 1"
string2 = "my string 2"
my_c_function(string1, string2)

Note I'm not certain .encode('utf-8') is necessary if your source file is already UTF-8.

1 Comment

For Python 3 remember to use swig's -py3 option. The wrapper encodes a Python 3 string as UTF-8, using PyUnicode_AsUTF8String and then PyBytes_AsStringAndSize. Passing bytes raises a TypeError.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.