Call C++ function from python and get return value

Viewed 747

I am trying to call a C++ function from a python script. Here is my sample C++ and Python code.

strfunc.cpp

#include <iostream>
#include <string>

using namespace std;

string getString()
{
    string hostname = "test.stack.com";
    return hostname;
}

strfunc.py

import ctypes

print(ctypes.CDLL('./strfunc.so').getString())

 

I compiled and generated a shared library from my C++ program using the following command:

g++ -fPIC strfunc.cpp -shared -o strfunc.so

When I try to execute strfunc.py, it gives the following error:

$ ./strfunc.py 
Traceback (most recent call last):
  File "./strfunc.py", line 5, in <module>
    print(ctypes.CDLL('./strfunc.so').getString())
  File "/usr/lib64/python3.7/ctypes/__init__.py", line 372, in __getattr__
    func = self.__getitem__(name)
  File "/usr/lib64/python3.7/ctypes/__init__.py", line 377, in __getitem__
    func = self._FuncPtr((name_or_ordinal, self))
AttributeError: ./strfunc.so: undefined symbol: getString

Please help me know how I can resolve this issue. The same thing works for int function.

1 Answers

If you use the readelf -Ws on your so file, it will give you items inside your so library:

FUNC GLOBAL DEFAULT 12 _Z9getStringB5cxx11v

You will see that your function is in fact there, it just has a mangled name. So proper name for calling ctype on the library would be _Z9getStringB5cxx11v().

However there are still few things wrong with it. Mark your method as an extern to let compiler know it has external linkage:

extern string getString()

Alternatively if you want to use it as getString() you can mark it as extern "C" which will disable c++ mangler

extern "C" string getString()

But in either case I think you will find that you have some memory problems. I think the proper way would be to return c style pointer to a character array and memory manage it yourself, something like this should work:

strfunc.cpp:

#include <iostream>
#include <string>

using namespace std;

char hostname[] = "test.stack.com";

extern "C" char * getString()
{

        return hostname;

}

strfunc.py:

#!/usr/bin/env python
from ctypes import *

test=cdll.LoadLibrary("./strfunc.so")
test.getString.restype=c_char_p
print(test.getString())

In case of string I think you need to figure out how to manage memory and return types properly to let know to the python that you are actually passing string. It might be doable but not so easy as the above.

Related