微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 pybind11 动态加载 libpython

如何解决使用 pybind11 动态加载 libpython

我正在尝试使用 Mac OSX 的 pybind11 构建一些共享库。我遇到了错误

dyld: Symbol not found: _PyBaSEObject_Type
  Referenced from: /Users/xxxxx/work/test_dynamic_linking/./example
  Expected in: flat namespace
 in /Users/xxxxx/work/test_dynamic_linking/./example
Abort trap: 6

我想要实现的是关闭构建时链接,但在运行时使用 dlopen 动态加载 libpython。注意 cmake 文件中的 -Wl,-undefined,dynamic_lookup 标志。我这样做是因为我想构建一个轮子,并且链接到 libpython 不是一个好主意 AFAIU。

下面是一个可重现的最小示例。我很困惑,如果你直接从 main.cpp 调用 Py_DecodeLocale()Py_InitializeEx() 之类的函数,它工作正常。但是调用 pybind11::initialize_interpreter() 失败并出现上述错误。 如果我这样做

nm -gU /opt/anaconda3/envs/py38/lib/libpython3.8.dylib | grep PyBaSEObject

符号 _PyBaSEObject_Type 确实是在 lib 中定义的:

0000000000334528 D _PyBaSEObject_Type

如果我创建一个包装器共享库来包装对 pybind11 函数调用,并从 main.cpp dlopen 它,它工作正常。这让我更糊涂了。

Cmake 文件

cmake_minimum_required(VERSION 3.4)
project(example)
set (CMAKE_CXX_STANDARD 11)
set(UNDEFINED_SYMBOLS_IGnorE_FLAG "-Wl,dynamic_lookup")
string(APPEND CMAKE_EXE_LINKER_FLAGS " ${UNDEFINED_SYMBOLS_IGnorE_FLAG}")
string(APPEND CMAKE_SHARED_LINKER_FLAGS " ${UNDEFINED_SYMBOLS_IGnorE_FLAG}")
include_directories(pybind11/include)
include_directories(/opt/anaconda3/envs/py38/include/python3.8)
add_library(pywrapper SHARED ${CMAKE_CURRENT_SOURCE_DIR}/wrapper.cpp)
add_executable(example main.cpp)

pyembed.hpp:

#pragma once

#include "pybind11/embed.h"

namespace py = pybind11;

void initialize_interpreter_func();


struct pybind_wrap_api {
    decltype(&initialize_interpreter_func) initialize_interpreter;
};

wrapper.cpp:

#include "pyembed.hpp"

#include <set>
#include <vector>
#include <iostream>

#include "pybind11/embed.h"
#include "pybind11/stl.h"

namespace py = pybind11;


void initialize_interpreter_func() {
    pybind11::initialize_interpreter();
}

pybind_wrap_api init_pybind_wrap_api() noexcept {
    return {
        &initialize_interpreter_func,};
}

__attribute__((visibility("default"))) pybind_wrap_api pybind_wrapper_api =
    init_pybind_wrap_api();

main.cpp:

#include <pybind11/embed.h> // everything needed for embedding                                                                                                    

#include "pyembed.hpp"

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

namespace py = pybind11;
static void* pylib_handle = nullptr;
static void* pybind_wrapper_handle = nullptr;
pybind_wrap_api* wrappers = nullptr;

int main() {
    std::string path_libpython = "/opt/anaconda3/envs/py38/lib/libpython3.8.dylib";
    pylib_handle = dlopen(path_libpython.c_str(),RTLD_Now | RTLD_GLOBAL);
    if(!pylib_handle) {
        std::cout << "load libpython Failed..." << std::endl;
    } else {
        std::cout << "load libpython succeeded..." << std::endl;
    }

    std::string path_wrapper = "./libpywrapper.dylib";
    pybind_wrapper_handle = dlopen(path_wrapper.c_str(),RTLD_Now | RTLD_GLOBAL);
    wrappers = static_cast<pybind_wrap_api*>(dlsym(pybind_wrapper_handle,"pybind_wrapper_api"));

    std::string pythonhome = "/opt/anaconda3/envs/py38";
    setenv("PYTHONHOME",pythonhome.c_str(),1);
    std::string pythonpath = "/opt/anaconda3/envs/py38/lib/python3.8/site-packages";
    setenv("PYTHONPATH",pythonpath.c_str(),true);

    // this line will cause it to fail with the symbol not found error
    py::initialize_interpreter();
    // if comment out the prevIoUs line and do the following line,it works fine. I'm confused why is so. 
    //wrappers->initialize_interpreter();

    return 0; 
} 

然后做

cmake . && make && ./example 

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。