5

I'm trying to call a C++ function from my Python code, if I pass a Boolean or an int it works perfectly, but if I send a string, it only prints the first character.
I am compiling with:

g++ -c -fPIC foo.cpp -Wextra -Wall -o foo.o
g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o
python3 fooWrapper.py

Here is the C++ and Python code:

Python:

from ctypes import cdll
lib = cdll.LoadLibrary("./libfoo.so")
lib.Foo_bar("hello")

c++:

#include <iostream>
#include <string>
#include <unistd.h>

void bar(char* string){
    printf("%s", string);
}

extern "C" {
    void Foo_bar(char* aString){
        bar(aString);
    }
}

I'm aware of the Boost Library, but i couldn't manage to download it, and this way works well excepts for strings. Thank you for your help

7
  • 2
    Tell me, what does %i do? Commented Oct 21, 2018 at 14:01
  • Also, why are you claiming this is C++ when you're essentially writing C? Commented Oct 21, 2018 at 14:05
  • 1
    Just changed to %s , was an old try with ints. Commented Oct 21, 2018 at 14:05
  • Because this is only a small part of the code, it's a test, but i saw I had to declare it as extern "C" to be able to cal it, but I can use any c++ in the bar function Commented Oct 21, 2018 at 14:07
  • You should consider filing a documentation bug against Python. [Python 3] Extending Python with C or C++ in their manual does not appear treat the subject. wchar_t is shown in one place but not explained. wchar_t and wide chars are not explained. Commented Oct 21, 2018 at 14:29

2 Answers 2

5

The problem is that strings are passed as pointers to wchar_t wide characters in Python 3. And in little-endian system your string can be coded in binary as

"h\0\0\0e\0\0\0l\0\0\0l\0\0\0o\0\0\0\0\0\0\0"

Which, when printed with %s will stop at the first null terminator.


For UTF-8-encoded byte strings (char *) you need a bytes object. For example:

lib.Foo_bar("hello".encode())

or use bytes literals:

lib.Foo_bar(b"hello")

Even better if you had specified the correct argument types:

from ctypes import cdll, c_char_p
foo_bar = cdll.LoadLibrary("./libfoo.so").Foo_bar
foo_bar.argtypes = [c_char_p]
foo_bar(b"hello\n")
foo_bar("hello\n")

when run will output the following:

hello
Traceback (most recent call last):
  File "foo.py", line 5, in <module>
    foo_bar("hello\n")
ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type

i.e. the latter call that uses a string instead of bytes would throw.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for your answer, that worked really well !
1

You may also process Python3 strings in C++ directly using the wchar_t type. In that case, you need to do any necessary conversions in C++ like this:

#include <iostream>
#include <locale>
#include <codecvt>

void bar(wchar_t const* aString)
{
    // Kudos: https://stackoverflow.com/a/18374698
    std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> convert;

    std::cout << convert.to_bytes(aString) << std::endl;
}

extern "C" {
    void Foo_bar(wchar_t const* aString)
    {
        bar(aString);
    }
}

You will lose Python2 compatibility, however.

Comments

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.