Search code examples
pythonc++dllvirtualctypes

python can never call a c++ dll or so file including inheritance logic?


class A{
  A();
  void a();
  virtual void v();
}

make this to liba.so

class B : A {
  B();
  void b();
  void v();
}
extern "C" {
  A* newB() {
    return new B();
  }
  void calla(A*b) {
    b->a();
  }
  void callv(A*b) {
     b->v();
  }
}

make this to libb.so

in python:

from ctypes import cdll 
lib = cdll.LoadLibrary('./libb.so')

class B(object):
  def __init__(self):
    self.obj = lib.newb()
  def a(self):
    lib.calla(self.obj)
  def v(self):
    lib.callv(self.obj)

b = B()
b.a()  # call base class function, that's ok
b.v()  # call derive class virtual function , segment fault!

Does this mean python cant use dll including inheritance logic?

seems python cant call c++ class as its function entrance changed bc inherit logic, can anyone talk about this?

Thanks


Solution

  • Notes:

    I've prepared a full (and dummy) example, to illustrate the behavior.

    a.h:

    #pragma once
    #define COUT() std::cout << __FILE__ << ":" << __LINE__ << "(" << __FUNCTION__ << ")\n"
    
    #if defined(_WIN32)
    #  define DLL_EXPORT __declspec(dllexport)
    #else
    #  define DLL_EXPORT
    #endif
    
    
    class DLL_EXPORT A {
    public:
        A();
        virtual ~A();
        void a();
        virtual void v();
    };
    

    a.cpp:

    #include "a.h"
    #include <iostream>
    
    
    A::A() {
        COUT();
    }
    
    A::~A() {
        COUT();
    }
    
    void A::a() {
        COUT();
    }
    
    void A::v() {
        COUT();
    }
    

    b.h:

    #pragma once
    #include "a.h"
    
    
    class B : public A {
    public:
        B();
        void b();
        void v();
    };
    
    
    extern "C" {
        DLL_EXPORT A *newB() {
            return new B();
        }
    
        DLL_EXPORT void calla(A *b) {
            b->a();
        }
    
        DLL_EXPORT void callv(A *b) {
            b->v();
        }
    
        DLL_EXPORT void delB(A *b)
        {
            delete b;
        }
    }
    

    b.cpp:

    #include "b.h"
    #include <iostream>
    
    
    B::B() : 
        A() {
        COUT();
    }
    
    void B::b() {
        COUT();
    }
    
    void B::v() {
        COUT();
    }
    

    code.py:

    #!/usr/bin/env python3
    
    import sys
    import ctypes
    
    
    LIB_NAME = "./libb.so"
    
    
    class B(object):
    
        def __init__(self, lib_name=LIB_NAME):
            self.lib = ctypes.cdll.LoadLibrary(lib_name)
            self.lib.newB.restype = ctypes.c_void_p
            self.obj = self.lib.newB()
    
        def a(self):
            self.lib.calla.argtypes = [ctypes.c_void_p]
            self.lib.calla(self.obj)
    
        def v(self):
            self.lib.callv.argtypes = [ctypes.c_void_p]
            self.lib.callv(self.obj)
    
        def __del__(self):
            self.lib.delB.argtypes = [ctypes.c_void_p]
            self.lib.delB(self.obj)
            self.obj = None
            self.lib = None
    
    
    def main():
        b = B()
        b.a()
        b.v()
    
    
    if __name__ == "__main__":
        print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
        main()
    

    Output:

    [cfati@cfati-5510-0:/cygdrive/e/Work/Dev/StackOverflow/q054646019]> ls
    a.cpp  a.h  b.cpp  b.h  code.py
    [cfati@cfati-5510-0:/cygdrive/e/Work/Dev/StackOverflow/q054646019]> g++ -shared -fPIC -o liba.so a.cpp
    [cfati@cfati-5510-0:/cygdrive/e/Work/Dev/StackOverflow/q054646019]> g++ -shared -fPIC -o libb.so b.cpp ./liba.so
    [cfati@cfati-5510-0:/cygdrive/e/Work/Dev/StackOverflow/q054646019]> ls
    a.cpp  a.h  b.cpp  b.h  code.py  liba.so  libb.so
    [cfati@cfati-5510-0:/cygdrive/e/Work/Dev/StackOverflow/q054646019]> python3 code.py
    Python 3.6.4 (default, Jan  7 2018, 15:53:53)
    [GCC 6.4.0] on cygwin
    
    a.cpp:6(A)
    b.cpp:7(B)
    a.cpp:14(a)
    b.cpp:15(v)
    a.cpp:10(~A)