Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling c++ function from python

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

like image 295
Chronoxx Avatar asked Oct 21 '18 13:10

Chronoxx


2 Answers

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.


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.

like image 26
Adrian W Avatar answered Oct 26 '22 21:10

Adrian W