gpt4 book ai didi

c++ - 从 pybind11 包装的代码动态链接共享库

转载 作者:行者123 更新时间:2023-12-01 13:07:39 35 4
gpt4 key购买 nike

我正在尝试将 python 绑定(bind)添加到中型 C++ 科学代码(大约数万个 LOC)中。我已经设法让它在没有太多问题的情况下工作,但我现在遇到了一个我自己无法解决的问题。代码组织如下:

  • 所有的类和数据结构都编译在一个库中 libcommon.a
  • 通过链接此库
  • 创建可执行文件
  • pybind11 用于创建 core.so python模块

  • “主要”部分的绑定(bind)工作正常。事实上,从独立代码或 python 启动的模拟给出了完全相同的结果。

    但是,该代码还支持类似插件的系统,该系统可以在运行时加载共享库。这些共享库包含从主代码中定义的接口(interface)继承的类。事实证明,如果我尝试从 python 链接这些共享库,我会得到臭名昭著的“ undefined symbol ”错误。我检查了这些符号是否在 core.so 中模块(使用 nm -D )。事实上,使用独立代码执行动态链接的模拟工作得很好(在同一个文件夹中并使用相同的输入)。不知何故,共享库在通过 python 调用时找不到正确的符号,但在由独立代码加载时没有问题。我正在使用 CMake 构建系统。

    接下来是 MCE。复制文件夹中的每个文件,复制(或链接) pybind11文件夹在同一个地方并使用以下命令:
    mkdir build
    cd build
    cmake ..
    make

    这将生成 standalone二进制和python模块。 standalone可执行文件将产生正确的输出。相比之下,在 python3 中使用以下命令(至少在我看来,应该是等效的)会产生错误:

    import core
    b = core.load_plugin()

    主文件
    #include "Base.h"
    #include "plugin_loader.h"

    #include <iostream>

    int main() {
    Base *d = load_plugin();
    if(d == NULL) {
    std::cerr << "No lib found" << std::endl;
    return 1;
    }
    d->foo();

    return 0;
    }

    基数.h
    #ifndef BASE
    #define BASE

    struct Base {
    Base();
    virtual ~Base();

    virtual void foo();
    };

    #endif

    基数.cpp
    #include "Base.h"

    #include <iostream>

    Base::Base() {}

    Base::~Base() {}

    void Base::foo() {
    std::cout << "Hey, it's Base!" << std::endl;
    }

    plugin_loader.h
    #ifndef LOADER
    #define LOADER

    #include "Base.h"

    Base *load_plugin();

    #endif

    plugin_loader.cpp
    #include "plugin_loader.h"

    #include <dlfcn.h>
    #include <iostream>

    typedef Base* make_base();

    Base *load_plugin() {
    void *handle = dlopen("./Derived.so", RTLD_LAZY | RTLD_GLOBAL);
    const char *dl_error = dlerror();
    if(dl_error != nullptr) {
    std::cerr << "Caught an error while opening shared library: " << dl_error << std::endl;
    return NULL;
    }
    make_base *entry = (make_base *) dlsym(handle, "make");

    return (Base *) entry();
    }

    派生的.h
    #include "Base.h"

    struct Derived : public Base {
    Derived();
    virtual ~Derived();
    void foo() override;
    };

    extern "C" Base *make() {
    return new Derived();
    }

    派生的.cpp
    #include "Derived.h"

    #include <iostream>

    Derived::Derived() {}

    Derived::~Derived() {}

    void Derived::foo() {
    std::cout << "Hey, it's Derived!" << std::endl;
    }

    绑定(bind).cpp
    #include <pybind11/pybind11.h>

    #include "Base.h"
    #include "plugin_loader.h"

    PYBIND11_MODULE(core, m) {
    pybind11::class_<Base, std::shared_ptr<Base>> base(m, "Base");

    base.def(pybind11::init<>());
    base.def("foo", &Base::foo);

    m.def("load_plugin", &load_plugin);
    }

    CMakeLists.txt

    PROJECT(foobar)

    # compile the library
    ADD_LIBRARY(common SHARED Base.cpp plugin_loader.cpp)
    TARGET_LINK_LIBRARIES(common ${CMAKE_DL_LIBS})
    SET_TARGET_PROPERTIES(common PROPERTIES POSITION_INDEPENDENT_CODE ON)

    # compile the standalone code
    ADD_EXECUTABLE(standalone main.cpp)
    TARGET_LINK_LIBRARIES(standalone common)

    # compile the "plugin"
    SET(CMAKE_SHARED_LIBRARY_PREFIX "")
    ADD_LIBRARY(Derived SHARED Derived.cpp)

    # compile the bindings
    ADD_SUBDIRECTORY(pybind11)
    INCLUDE_DIRECTORIES( ${PROJECT_SOURCE_DIR}/pybind11/include )

    FIND_PACKAGE( PythonLibs 3 REQUIRED )
    INCLUDE_DIRECTORIES( ${PYTHON_INCLUDE_DIRS} )

    ADD_LIBRARY(_oxpy_lib STATIC bindings.cpp)
    TARGET_LINK_LIBRARIES(_oxpy_lib ${PYTHON_LIBRARIES} common)
    SET_TARGET_PROPERTIES(_oxpy_lib PROPERTIES POSITION_INDEPENDENT_CODE ON)

    pybind11_add_module(core SHARED bindings.cpp)
    TARGET_LINK_LIBRARIES(core PRIVATE _oxpy_lib)

    最佳答案

    你是对的,导入库中的符号不​​可见,因为 core未加载 RTLD_GLOBAL标志设置。您可以在 python 端使用几行额外的行来解决这个问题:

    import sys, os
    sys.setdlopenflags(os.RTLD_GLOBAL | os.RTLD_LAZY)

    import core
    b = core.load_plugin()

    来自 sys.setdlopenflags() doc :

    To share symbols across extension modules, call as sys.setdlopenflags(os.RTLD_GLOBAL). Symbolic names for the flag values can be found in the os module (RTLD_xxx constants, e.g. os.RTLD_LAZY).

    关于c++ - 从 pybind11 包装的代码动态链接共享库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60799939/

    35 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com