1

I'm trying to link my Python (3.7.6) code to C++ with a pass-back of a boolean from the C++ code to Python. My OS is Ubuntu 16.04. The IDE for the C++ is Visual Studio.

On running the Python wrapper code, I receive the message:

libcppstring.so: undefined symbol: cpp_string

The python code, the *.so file, etc. are all in the same directory. The c++ code was compiled at the terminal prompt with

g++ -O3 -Wall -Werror -shared -std=c++11 -fPIC -o libcppstring.so cpp_string.cpp  

My python wrapper code, c++ code, and the header code are very simple. Just want to pass two strings from Python to C++ shared object and return 'true' to Python. Perhaps I have messed up the order of the compile commands.

#---------python wrapper code------------------
import ctypes
import sys
import os
localPath = os.path.dirname(os.path.realpath(__file__))
sys.path.append(localPath)
if __name__ == "__main__":
# Load the shared library into c types.

    c_lib = ctypes.CDLL(localPath+"/libcppstring.so")

    # Sample data for our call:
    string1 = "hello, "
    string2 = "world"

    c_lib.cpp_string.restype = ctypes.c_bool
    answer = c_lib.cpp_string( string1, string2)

    print(answer)

cpp code: cpp_string.cpp

#include "cpp_string.h"

bool cpp_string( std::string string1, std::string string2){
    /*use this to convert to const char* to use sqlite3 in c */
    /*used once the code is working*/  
    string1.c_str();
    return (true);
}

header code: cpp_string.h

#include <string>
#ifdef _MSC_VER
    #define EXPORT_SYMBOL __declspec(dllexport)
#else
    #define EXPORT_SYMBOL
#endif

EXPORT_SYMBOL bool cpp_string( std::string string1, std::string string2);
BSL787
  • 7
  • 1
  • ctypes is called ctypes and not c++types for a reason. Stick to C types, or use something other than ctypes. – n. m. could be an AI May 18 '21 at 12:48
  • Looks like the name of `cpp_string` was changed in the binary because of [name mangling](https://en.wikipedia.org/wiki/Name_mangling#C++). Also, you set `c_lib.cpp_string.restype = ctypes.c_bool`, but what are the types of the _arguments_?? `std::string` isn't really a pointer to `char`, is it? – ForceBru May 18 '21 at 12:49
  • Hi, and thank you. First, I should have used C types; there's a space in the title between c++ and ctypes. Second, I'm not sure what's meant by name-mangling. I intend later to compare the input strings from Python with two strings in the C++ code, and return 'true' if both pairs of strings match. The std::string will be used with SQlite3, which accepts C-code chars. – BSL787 May 18 '21 at 12:56
  • Removed all instances of _ symbol from code names and uses. Should address name-mangling issue. Still receive same error: libcppstring.so: undefined symbol: cppstring. – BSL787 May 18 '21 at 13:08
  • 1
    You need to declare your function as `extern "C"`, and better make it return `void` or `int` because C `bool` and C++ `bool` are not necessarily the same – n. m. could be an AI May 18 '21 at 15:28
  • 1
    You cannot use std::string with c types, even if you use extern C. See for example https://stackoverflow.com/q/63688553/5769463 – ead May 18 '21 at 18:02

2 Answers2

0

To interface ctypes with C++, use extern "C" functions with C-compatible types. Also declare .argtypes (argument types and .restype (return type) for better error checking.

test.cpp

#include <iostream>

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

extern "C" API
int cpp_string(const char* string1, const char* string2) {
    std::cout << string1 << std::endl << string2 << std::endl;
    return 1;
}

test.py

import ctypes as ct

dll = ct.CDLL('./test')
dll.cpp_string.argtypes = ct.c_char_p,ct.c_char_p
dll.cpp_string.restype = ct.c_int

print(dll.cpp_string(b'Hello',b'World!'))

Output:

Hello
World!
1

Note that char* in C is c_char_p in Python and takes Python bytes type parameters. If you want to pass Python str type parameters, use wchar_t* / c_wchar_p.

Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
-2

First, thanks very much to Mr. Tolonen. From another community and a very helpful individual (all credit > J*M#), I did solve my problem. Here's my code (all I wanted was the return of TRUE, which it does.

cppstring.h

#include <string>
#ifdef _MSC_VER
    #define EXPORT_SYMBOL __declspec(dllexport)
#else
    #define EXPORT_SYMBOL
#endif

extern "C" {
EXPORT_SYMBOL bool cppstring( std::string string1, std::string string2);
}

cppstring.cpp

#include <string>
#include "cppstring.h"
bool cppstring( std::string string1, std::string string2){
/*use this to convert to const char* to use sqlite3 in c */
    string1.c_str();
    return (true);
}

and,

cString_test.py

#!/usr/bin/env python
""" Simple examples of calling C functions through ctypes module. """
import ctypes
import sys
import os

localPath = os.path.dirname(os.path.realpath(__file__))

sys.path.append(localPath)
if __name__ == "__main__":
    # Load the shared library into c types.

    c_lib = ctypes.CDLL(localPath+"/libcppstring.so")

    # Sample data for our call:
    string1 = "hello, "
    string2 = "world"
    c_lib.cppstring.restype = ctypes.c_bool
    answer = c_lib.cppstring( string1, string2)
    print(answer)
BSL787
  • 7
  • 1