2

I am using ctypes to call a C code:

mysum.cpp

//global variables and required functions are defined
int mysum(int ECG_sample) 
{
   int HR;
   ecg_wave_sample = ECG_sample;
   Filter_CurrentECG_sample(&ecg_wave_sample, &ecg_filterout);  
   Calculate_HeartRate(ecg_filterout,&global_HeartRate); // calculate HeartRate
   HR = global_HeartRate;
   return HR;
}

g++ -shared -o mysum.so -fPIC mysum.cpp used to create the .so file

Python wrapper:

libname = '/home/yasaswini/hp2-notebooks/notebooks/Algorithm_testing_on_database/mysum_example  /mysum.so'
libdir = './'
lib=ctl.load_library(libname, libdir)
py_add_one = lib.mysum
py_add_one.restype = ctypes.c_int
py_add_one.argtypes = [ctypes.c_int]
sample = -145
results = py_add_one(sample)

I am getting this error

 AttributeError Traceback (most recent call last) <ipython-input-75-858227ec99e5> in <module>
  6 # 1. open the shared library
  7 #mylib = ctypes.CDLL(libfile)
  ----> 8 py_add_one = lib.mysum
  9 
  10 # 2. tell Python the argument and result types of function mysum

  ~/anaconda3/lib/python3.7/ctypes/__init__.py in __getattr__(self, name)
   375         if name.startswith('__') and name.endswith('__'):
   376             raise AttributeError(name)
   --> 377         func = self.__getitem__(name)
   378         setattr(self, name, func)
   379         return func

   ~/anaconda3/lib/python3.7/ctypes/__init__.py in __getitem__(self, name_or_ordinal)
  380 
  381     def __getitem__(self, name_or_ordinal):
  --> 382         func = self._FuncPtr((name_or_ordinal, self))
  383         if not isinstance(name_or_ordinal, int):
  384             func.__name__ = name_or_ordinal

  AttributeError: /home/yasaswini/hp2-notebooks/notebooks/Algorithm_testing_on_database /mysum_example/mysum.so: undefined symbol: mysum

Can someone point out what exactly I am doing wrong here?

Jasmine
  • 476
  • 3
  • 22
  • https://stackoverflow.com/questions/33908356/attributeerror-python-undefined-symbol-when-accessing-c-function-from-pytho – Joe Dec 16 '19 at 08:43
  • what does `lib=ctl.load_library(libname, libdir) print(lib)` show? – Joe Dec 16 '19 at 08:44
  • I also recommend to not use ctypes directly. Consider using [`cffi`](https://cffi.readthedocs.io/en/latest/overview.html#api-mode-calling-the-c-standard-library) which is more high level and usually convenient to use in cases like yours. – Joe Dec 16 '19 at 09:32
  • It can also compile the C code on change: https://dbader.org/blog/python-cffi or https://hyunyoung2.github.io/2018/05/13/How_To_Use_CFFI_To_Interface_To_C_function_On_Python/ – Joe Dec 16 '19 at 09:34
  • https://stackoverflow.com/questions/47508459/loading-so-library-with-cffi – Joe Dec 16 '19 at 09:36
  • @Joe, it shows the path of the .so file. – Jasmine Dec 16 '19 at 11:10
  • https://stackoverflow.com/a/34380673/7919597 – Joe Dec 16 '19 at 12:54
  • https://stackoverflow.com/questions/11237072/ctypes-not-finding-symbols-in-shared-library-created-using-cmake – Joe Dec 16 '19 at 12:56
  • Does this answer your question? [Export C++ function to python using ctypes: undefined symbol](https://stackoverflow.com/questions/34380569/export-c-function-to-python-using-ctypes-undefined-symbol) – CristiFati Dec 16 '19 at 13:03
  • What if using *gcc* insteadog *g++*? And if that alone doesn't work renaming the source file to *mysum.c*? – CristiFati Dec 16 '19 at 13:04

2 Answers2

5

This is probably related to C++ name mangling.

You need to add extern to the definition of your function in mysum.cpp since you are using C++ (g++). In your case:

extern "C" int mysum(int ECG_sample) 
{
   ...
}

More info on name mangling

See also

https://stackoverflow.com/a/11237254/7919597

https://stackoverflow.com/a/34380673/7919597

Joe
  • 6,758
  • 2
  • 26
  • 47
0

I used the logic given here.

.c file:

int simple_function(int ECG_samples) 
{
    ecg_wave_sample = ECG_samples;
    ECG_ProcessCurrSample(&ecg_wave_sample, &ecg_filterout); 
    QRS_Algorithm_Interface(ecg_filterout);
    printf("HR:%d\n",ecg_wave_sample);
    return global_HeartRate;  
}

Python script:

import numpy
def call_function_with_no_args(libc): 
ECG_samples = [3,4,6,6,9,11,13,17,18,21,23,24,28,30,33,35,36,36]
while(1):

    for i in range (len(ECG_samples)):
        value = print(libc.simple_function(ECG_samples[i]))
    print()
return value

if __name__ == "__main__":
# load the shared library into c types.
    libname = os.path.abspath(os.path.join(os.path.dirname(__file__),
                                       "libclib1.so"))
    LIBC = ctypes.CDLL(libname)


call_function_with_no_args(LIBC)

I think this is the best way of working with ctypes. Its simple and easy to understand. The Makefile generates the .so file. Thank you for the help. :-)

Jasmine
  • 476
  • 3
  • 22