简体   繁体   中英

Undefined Symbol error when linking pybind11 with a dynamic library that calls an external function

I'm trying to link a pybind11 module with a .so dynamic library, and the library calls functions not implemented in the .so file. It works fine in a normal c++ executable file, but raises Undefined Symbol error when imported in python.

Here is a simple demo to reproduce my problem.

The function Student::print() is complied to a dynamic library and it calls a function Student::setId() that is not included in the .so file. (If use nm command, it will show U _ZN7Student5setIdEi .)

It works fine in main.cpp to call Student::print() , but in test.py , it raises ImportError: {mypath}/libstu_lib.so: undefined symbol: _ZN7Student5setIdEi

To simplify, I set the external function to be its own member function. The problem still reproduces when calling functions belonging to another classes, so I think it doesn't matter.

Is there any option in pybind11 to deal with this problem? Because it is difficult to modify the source code of the dynamic library. Thanks.

Student.h

#include <iostream>
using namespace std;

class Student {
    public:
        Student(int id);
        void setId(int id);
        void print();
    private:
        int id;
};

Student.cpp

#include "Student.h"

Student::Student(int id) {
    this->id = id;
}

void Student::setId(int id) {
    this->id = id;
}

Student_lib.cpp

#include "Student.h"

void Student::print() {
    cout << "id: " << this->id << endl;
    this->setId(111);
    cout << "id: " << this->id << endl;
}

Student_wrapper.cpp

#include <pybind11/pybind11.h>
#include "Student.h"

namespace py = pybind11;

PYBIND11_MODULE(stu, m){
    py::class_<Student>(m, "Student")
        .def(py::init<int>())
        .def("setId", &Student::setId)
        .def("print", &Student::print);
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.15)
project(test)

link_directories(${PROJECT_SOURCE_DIR})

add_library(stu_lib SHARED Student_lib.cpp)

add_executable(main main.cpp Student.cpp)
target_link_libraries(main stu_lib)

find_package(pybind11 REQUIRED)
pybind11_add_module(stu Student_wrapper.cpp Student.cpp)
target_link_libraries(stu PUBLIC stu_lib)

main.cpp

#include "Student.h"

int main(int argc, char** argv) {
    Student* s = new Student(1);
    s->print();
    return 0;
}

test.py

from stu import Student

s = Student(1)
s.print()

Output of ldd libstu_core.so :

        linux-vdso.so.1 (0x00007ffcffdac000)
        libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f3def395000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3def1a3000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f3def054000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f3def593000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f3def039000)

I'll be honest: I don't know how pybind11 works internally well enough to tell you why this is happening. However, there is a workaround that makes your code works, which is to compile Student.cpp into a shared library of its own and link it to the pybind11 module. This is how you can modify your CMakeLists.txt to make it work:

cmake_minimum_required(VERSION 3.15)
project(test)

link_directories(${PROJECT_SOURCE_DIR})

add_library(stu_lib SHARED Student_lib.cpp)

add_executable(main main.cpp Student.cpp)
target_link_libraries(main stu_lib)

find_package(pybind11 REQUIRED)
add_library(stu_class SHARED Student.cpp)
pybind11_add_module(stu Student_wrapper.cpp)
target_link_libraries(stu PUBLIC stu_lib stu_class)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM