Using SWIG to export C++ class as Python , error undefined symbol after importing module

Viewed 271

I'm attempting to learn SWIG (http://www.swig.org/) in order to extend/expose a C++ library that I have to Python & Java languages.

Before starting this endeavor, I started with a simple SWIG example (http://www.swig.org/tutorial.html) & got that working.

See below (you can see swig woks on my system & that I can load "example" module in Python shell):

linux{me}% swig -python example.i
linux{me}% gcc -c -fPIC example.c example_wrap.c / -I/usr/include/python2.7
gcc: warning: /: linker input file unused because linking not done
linux{me}% gcc -c -fPIC example.c example_wrap.c -I/usr/include/python2.7
linux{me}% ld -shared example.o example_wrap.o -o _example.so
linux{me}% python
Python 2.7.5 (default, Aug 2 2016, 04:20:16)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.

import example
example.fact(5)
120
example.my_mod(5)
Traceback (most recent call last):
File "", line 1, in
TypeError: my_mod() takes exactly 2 arguments (1 given)
example.my_mod(7,3)
1
example.get_time()
'Fri Feb 28 14:54:08 2020\n'

All is good, now the next step is to expose a C++ class, so I followed this tutorial initially (https://realmike.org/blog/2010/07/18/python-extensions-in-cpp-using-swig/). When compared to official SWIG docs this tutorial had a few steps missing, like load the .so shared libray so that Python can call "import" on the library, we will get to that later...

I made a few changes to the SomeClass.h to add constants "#define TypeBool 0" etc...

Here is the relevant dummy C++ header:

#ifndef _SOME_CLASS_H_
#define _SOME_CLASS_H_

class SomeClass {
public:
    SomeClass();
    SomeClass(int a, int b);
    virtual ~SomeClass();
    void MethodA(int a = -1);
    void MethodB(int b = -1);
    void setType(int type);
    int GetValA();
    int GetValB();
    int GetType(); 
private:
    int mValA;
    int mValB;
    int type; 
};

#endif  // _SOME_CLASS_H_

Here is the relevant Class file that implements the SomeClass.h:

#include "SomeClass.h"
//refactor to use enum
#define TypeBool 0
#define TypeChar 1
#define TypeByte 2      
#define TypeInt 3
#define TypeShort 4
#define TypeFloat 5
#define TypeDouble 6
#define TypeString 7
#define TypeComposite 8

SomeClass::SomeClass() {
   mValA = -1;  
   mValB = -1; 
}

SomeClass::~SomeClass() {
}

void SomeClass::MethodA(int a)
{
 mValA =  mValA  * -1; 
}

void setType(int type)
{
    switch (type)
    {
     case 0:
     type = TypeBool;   
     break; 
      case 1:
     type = TypeChar;
     break; 
     case 2:
     type = TypeByte;   
     break; 
      case 3:
     type = TypeInt;   
     break; 
      case 4:
     type = TypeShort;   
     break; 
      case 5:
     type = TypeFloat;   
     break; 
      case 6:
     type = TypeDouble;   
     break; 
      case 7:
     type = TypeString;   
     break; 
     case 8:
     type = TypeComposite;   
     break; 
    }
}

void SomeClass::MethodB(int b)
{
     mValB  = b;   
}

 int SomeClass::GetValA()
 {
     return mValA; 
 }

 int SomeClass::GetValB()
 {
     return  mValB;
 }

 int SomeClass::GetType()
 {
     return type; 
 }

Attempt #1 Here is the SWIG required "interface" file, based on (https://realmike.org/blog/2010/07/18/python-extensions-in-cpp-using-swig/) or http://www.swig.org/Doc4.0/SWIGDocumentation.pdf pages 25-32.

%module mymodule
 %{
 #include "SomeClass.h"
 %}

%include "SomeClass.h"

1) Then I issue the command:

swig -c++ -python -I/home/me/NetBeansProjects/example mymodule.i

This commnand runs and seems to create python & cpp autogenerated wrappers:

-rw-r--r-- 1 me linuxlusers 350 Mar 3 15:53 mymodule.i -rw-r--r-- 1 me linuxlusers 2864 Mar 3 15:55 mymodule.py -rw-r--r-- 1 me linuxlusers 121327 Mar 3 15:55 mymodule_wrap.cxx

2) Then I compile:

g++ -c -fPIC SomeClass.cpp mymodule_wrap.cxx -I/usr/include/python2.7

Which creates object files:

-rw-r--r-- 1 me linuxlusers 60136 Mar 3 15:56 mymodule_wrap.o -rw-r--r-- 1 me linuxlusers 4384 Mar 3 15:56 SomeClass.o

3)Then, I bundle my object files into shared library file ".so" & make that shared library available system-wide so I can load it from Python:

linux{me}% ld -shared mymodule_wrap.o -o _mymodule.so

4) Finally, I call python shell from my command line & attempt to import mymodule:

linux{me}% python
Python 2.7.5 (default, Aug  2 2016, 04:20:16)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import mymodule
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "mymodule.py", line 15, in <module>
    import _mymodule
ImportError: ./_mymodule.so: undefined symbol: __gxx_personality_v0
>>>

You can see the error: ImportError: ./_mymodule.so: undefined symbol: __gxx_personality_v0.

Attempt $2 (I rewrote the interface file with .cpp implememntation in the %inline tag):

 %module mymodule
 %{
 #include "SomeClass.h"
 %}

%include "SomeClass.h"

%constant TypeBool 0
%constant TypeChar 1
%constant TypeByte 2        
%constant TypeInt 3
%constant TypeShort 4
%constant TypeFloat 5
%constant TypeDouble 6
%constant TypeString 7
%constant TypeComposite 8

%inline %{

   SomeClass::SomeClass() {
   mValA = -1;  
   mValB = -1; 
}

SomeClass::~SomeClass() {
}

void SomeClass::MethodA(int a)
{
 mValA =  mValA  * -1; 
}


void setType(int type)
{
    switch (type)
    {
     case 0:
     type = TypeBool;   
     break; 
      case 1:
     type = TypeChar;
     break; 
     case 2:
     type = TypeByte;   
     break; 
      case 3:
     type = TypeInt;   
     break; 
      case 4:
     type = TypeShort;   
     break; 
      case 5:
     type = TypeFloat;   
     break; 
      case 6:
     type = TypeDouble;   
     break; 
      case 7:
     type = TypeString;   
     break; 
     case 8:
     type = TypeComposite;   
     break; 
    }
}

void SomeClass::MethodB(int b)
{
     mValB  = b;   
}

 int SomeClass::GetValA()
 {
     return mValA; 
 }

 int SomeClass::GetValB()
 {
     return  mValB;
 }

 int SomeClass::GetType()
 {
     return type; 
 }


 %}

I ran the swig commands & compilation again, however I got the same error:

linux{me}% swig -c++ -python -I/home/me/NetBeansProjects/example mymodule.i
linux{me}% g++ -c -fPIC SomeClass.cpp mymodule_wrap.cxx -I/usr/include/python2.7
linux{me}% ld -shared mymodule_wrap.o -o _mymodule.so                
linux{me}% python                                                    
Python 2.7.5 (default, Aug  2 2016, 04:20:16)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import mymodule
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "mymodule.py", line 15, in <module>
    import _mymodule
ImportError: ./_mymodule.so: undefined symbol: __gxx_personality_v0
>>>
KeyboardInterrupt
>>>

Attempt 3 (I rewrote the interface file with header .h in the %inline tag):

 %module mymodule
 %{
 #include "SomeClass.h"
 %}

%include "SomeClass.h"

%constant TypeBool 0
%constant TypeChar 1
%constant TypeByte 2        
%constant TypeInt 3
%constant TypeShort 4
%constant TypeFloat 5
%constant TypeDouble 6
%constant TypeString 7
%constant TypeComposite 8

%inline %{

class SomeClass {
public:
    SomeClass();
    SomeClass(int a, int b);
    virtual ~SomeClass();
    void MethodA(int a = -1);
    void MethodB(int b = -1);
    void setType(int type);
    int GetValA();
    int GetValB();
    int GetType(); 
private:
    int mValA;
    int mValB;
    int type; 
}; 

 %}

Again, I got the same error (ImportError: ./_mymodule.so: undefined symbol: __gxx_personality_v0.) as the other 2 attempts above.

I attempted compiler flags as recommended in "undefined symbol: __cxa_pure_virtual" error when loading library from java

However, I got the same error.

1) How can I resolve this error?

Attempt #4 Somewhat of a solution:

I wrote whole new simpler class based on another online guide, but without overloaded constructors & functions.

I imported SWIG wrapped C++ class (Word.cpp), by removing multiple constructors & only have single default const.

See below:

 #ifndef WORD_H
    #define WORD_H
    #include <stdio.h>
    #include <iostream>
    #include <string.h>
    using namespace std;

    class Word {
    public:
        Word();
        // REMOVED Word(std::string the_word);
        //REMOVED Word(const Word& orig);
        virtual ~Word();
        virtual void updateWord(std::string word);
        virtual std::string getWord();
    private:
        std::string _the_word;

    };

#endif /* WORD_H */

SWIG interface for Word.h:

  %{
     /* Put header files here or function declarations like below */
     /*#include "example.h"*/
     #include "Word.h"
     %}

     %include "std_string.i"
    /* %include "example.h"*/
     %include "Word.h"

And used these compile options:

swig -python example.i
swig -python -c++ example.i
python setup.py build_ext --inplace
Python:
python
Python 2.7.5 (default, Aug 2 2016, 04:20:16)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
import _example
_example.new_Word()
<Swig Object of type 'Word *' at 0x7f4debcceb70>
_example.new_Word()
swig/python detected a memory leak of type 'Word *', no destructor found.
<Swig Object of type 'Word *' at 0x7f4debcced50>
w = _example.new_Word()
_example.Word_updateWord(w,"beef")
_example.Word_getWord(w)
swig/python detected a memory leak of type 'Word *', no destructor found.
'beef'
_example.Word_updateWord(w,"chicken")
_example.Word_getWord(w)
'chicken'

So without the same function, or constructor signature we are able to export C++ Class as a Python module.

However, this is very limiting, since Object Orientation design patterns use overloading/overriding significantly.

Thanks,

A million!

0 Answers
Related