使用 Boost::Python 从嵌入式 python 中提取数据

Posted

技术标签:

【中文标题】使用 Boost::Python 从嵌入式 python 中提取数据【英文标题】:Extracting data from embedded python using Boost::Python 【发布时间】:2014-08-27 17:20:16 【问题描述】:

我正在尝试学习一点 boost::python 知识,但我一直坚持从 python 字符串中提取数据。

目前,我能够干净地编译,但是在执行代码时,我收到了分段错误。我已将 seg-fault 缩小到实际使用 boost::python::extract 的行。

希望得到指导。提前致谢!

为方便起见,我提供了一个github repo:https://github.com/brianbruggeman/boost_python_hello_world/tree/feature/stack_overflow

Cpp 代码(say_hello.cpp):

#include <boost/python.hpp>
#include <iostream>

namespace bp = boost::python;

// Embedding python
int main(int argc, char** argv) 
    int data = 0;
    Py_Initialize();
    PyRun_SimpleString("data = 1");
    bp::object module(bp::handle<>(bp::borrowed(PyImport_AddModule("__main__"))));
    bp::object dictionary = module.attr("__dict__");
    bp::object data_obj = dictionary["data"];
    // Error: The following line has the segmentation fault...
    data = bp::extract<int>(data_obj);
    std::cout << "data = " << data << std::endl;
    Py_Finalize();
    return 0;

我做错了什么?为了完整起见,我使用的是 Mac OS X Mavericks,并且在下面包含了 CMakeLists.txt 文件:

project(hello)
cmake_minimum_required(VERSION 2.8)

FIND_PACKAGE(PythonInterp)
FIND_PACKAGE(PythonLibs)
FIND_PACKAGE(Boost COMPONENTS python)

include_directories($PYTHON_INCLUDE_DIRS $Boost_INCLUD_DIRS)
link_directories($PYTHON_LIBRARY_DIRS $Boost_LIBRARY_DIRS)

add_executable(hello say_hello.cpp)
target_link_libraries(hello
  $Boost_LIBRARIES
  $PYTHON_LIBRARIES)

Python 是使用 Homebrew 编译和安装的:

brew install python

Boost 是使用 Homebrew 编译和安装的:

brew install boost --with-python

编辑(boost-python 的新安装):

brew install --build-from-source boost-python

【问题讨论】:

【参考方案1】:

代码很好。构建过程是问题所在。

Brew 正在针对从 Brew 的 python 包安装的 python 库构建 libboost_python 库:

$ otool -L $(brew list boost | grep "libboost_python.dylib")
/usr/local/Cellar/boost/1.55.0_2/lib/libboost_python.dylib:
    /usr/local/lib/libboost_python.dylib (...)
    /usr/local/Frameworks/Python.framework/Versions/2.7/Python (...) <-- brew
    ...

但是,CMake 正在链接 OSX 系统提供的 Python 库:

$ (mkdir build && cd build && cmake ../)
...
-- Found PythonLibs: /usr/lib/libpython2.7.dylib (found version "2.7.5") 
...

因此,程序和 Boost.Python 链接到不同的 Python 库,导致未定义的行为。要解决此问题,请修复 FindPythonLibs.cmake 文件或更新 CMakeLists.txt 以明确链接到 brew 提供的 Python 库。


这是一个完整的 CMakeLists.txt 示例,它将 PYTHON_LIBRARIES 变量显式设置为 brew 在我的系统上安装的 Python 库:

project(hello)
cmake_minimum_required(VERSION 2.8)

find_package(PythonLibs)
set(PYTHON_LIBRARIES /usr/local/Frameworks/Python.framework/Versions/2.7/Python)
find_package(Boost COMPONENTS python)

include_directories($PYTHON_INCLUDE_DIRS $Boost_INCLUDE_DIRS)
link_directories($Boost_LIBRARY_DIRS)

add_executable(hello say_hello.cpp)
target_link_libraries(hello
  $Boost_LIBRARIES
  $PYTHON_LIBRARIES)

及其用法:

$ (mkdir build && cd build && cmake ../ && make)
...
-- Found PythonLibs: /usr/lib/libpython2.7.dylib (found version "2.7.5") 
...
Scanning dependencies of target hello
[100%] Building CXX object CMakeFiles/hello.dir/say_hello.cpp.o
Linking CXX executable hello
[100%] Built target hello
$ ./build/hello
data = 1

请注意,PythonLibs 仍会打印它找到的库,但 PYTHON_LIBRARIES 变量已明确设置为适当的库。


还有几点需要注意:

Boost.Python 不支持安全调用Py_Finalize()。根据Embedding - Getting started 部分:

注意此时千万不能调用Py_Finalize()来停止解释器。这可能会在 boost.python 的未来版本中得到修复。

在嵌入时,我经常发现使用 try/catch 块、捕获 boost::python::error_already_set 异常和打印错误很有帮助:

Py_Initialize();
try

  // ...

catch (boost::python::error_already_set&)

  PyErr_Print();

【讨论】:

感谢您的洞察! (y) 今天,boost-python 应该安装:brew install --build-from-source boost-python

以上是关于使用 Boost::Python 从嵌入式 python 中提取数据的主要内容,如果未能解决你的问题,请参考以下文章

如何组织 python / Boost Python 项目

使用boost python嵌入python时导入错误

如何在 boost::python 嵌入式 python 代码中导入模块?

Boost python,嵌入时从python调用c++函数

Embedded Boost::Python 和 C++:并行运行

如何使用 Boost.Python 在 Python 中调用内置函数