0

I am trying to build a shared object library from a cpp file that is a simple set of functions. I want to use ctypes to interface with python.

Say I have the cpp file:

#include "print.h"
#include <vector>
#include <iostream>
#include <dlfcn.h>

void print_array(const std::vector<std::vector<float>> &A){
  for(size_t i = 0; i < A.size(); i++) {
    for(size_t j = 0; j < A[0].size(); j++) {
      std::cout << A[i][j] << "\n";
    }
  }
}

and header file

#ifndef ADD_H
#define ADD_H
#include <vector>

void print_array(const std::vector<std::vector<float>> &A);

#endif

I tried to build

g++ -fpic -c print.cpp -o print.o
g++ -shared -o print.so print.o

Then in python

from cytpes import cdll
print_lib = cdll.LoadLibrary("print.so")

the line

print_lib.print_array()

yields

AttributeError: ./print.so: undefined symbol: print_array

nm -D print.so

gives the output

0000000000201060 B __bss_start
                 U __cxa_atexit
                 w __cxa_finalize
0000000000201060 D _edata
0000000000201068 B _end
0000000000000c14 T _fini
                 w __gmon_start__
0000000000000898 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses
0000000000000a50 T _Z11print_arrayRKSt6vectorIS_IfSaIfEESaIS1_EE
0000000000000bcc W _ZNKSt6vectorIfSaIfEE4sizeEv
0000000000000bf2 W _ZNKSt6vectorIfSaIfEEixEm
0000000000000b6a W _ZNKSt6vectorIS_IfSaIfEESaIS1_EE4sizeEv
0000000000000ba2 W _ZNKSt6vectorIS_IfSaIfEESaIS1_EEixEm
                 U _ZNSolsEf
                 U _ZNSt8ios_base4InitC1Ev
                 U _ZNSt8ios_base4InitD1Ev
                 U _ZSt4cout
                 U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc

What am I fundamentally doing wrong in the compilation step?

user18764
  • 253
  • 2
  • 10
  • Notice how it says, `undefined symbol: print_array`. It seems like the symbol in **print.so** is `_Z11print_arrayRKSt6vectorIS_IfSaIfEESaIS1_EE` – Justin Jun 27 '18 at 19:51
  • You have been hit by c++ ABI. See https://stackoverflow.com/questions/1615813/how-to-use-c-classes-with-ctypes – Michael Doubez Jun 27 '18 at 20:58

1 Answers1

4

You are calling a C++ function while ctypes expects C functions linkage:

  • C functions have a different (i. e. none) name mangling, as you noticed
  • C function cannot have C++ parameters (std objects like std::vector, references, etc.)

To solve your issue, declare a clean C-style API in your headers, and if you intend to implement the functions in C++ and use g++ as your compiler/linker, you must add an extern "C" declaration right before the function declaration in the header file, or as a block around your declarations:

extern "C" void print_array(...);

This will prevent the C++ name mangling.

Using vectors and other C++ types in your interface functions will cause you ABI issues, i. e. "it won't work", even if everything links and compiles seemingly cleanly.

Use C functions with the data types supported by ctypes (see Python docs on ctypes) in your functions as an interface, feel free to implement in C++, but then wrap your C function declarations in an extern "C" block to prevent name mangling.

Please do refer to the documentation of ctypes to find out how to correctly use structs, unions, references, etc. as function parameters, as there are many pitfalls and issues to consider.

Gyroplast
  • 56
  • 4
  • agreed, I am going to stick with arrays since I do not need dynamic structures. – user18764 Jun 27 '18 at 21:00
  • @mdoubez What are you talking about? It's absolutely fine to mark C++ functions as `extern "C"`. `extern "C"` tells the compiler not to perform name mangling on the function and instead emit a C-compatible symbol. – Justin Jun 27 '18 at 22:52
  • 1
    it also modifies the calling convention and "C" doesn't have a calling convention for the parameter std::vector<>&. You couldn't expose the same function to a C compiler. – Michael Doubez Jun 28 '18 at 04:09
  • Passing in a numpy array is passed as float *var. I then convert to c++ types in an inner function. This method worked well for me! – user18764 Jun 29 '18 at 13:43