C++和python使用boost库混合编程
Thursday, March 9, 2023
本文共1759字
4分钟阅读时长
⚠️本文是作者P3troL1er原创,首发于https://peterliuzhi.top/posts/c++%E5%92%8Cpython%E4%BD%BF%E7%94%A8boost%E5%BA%93%E6%B7%B7%E5%90%88%E7%BC%96%E7%A8%8B/。商业转载请联系作者获得授权,非商业转载请注明出处!
Wherever a man may happen to turn, whatever a man may undertake, he will always end up by returning to the path which nature has marked out for him.
— Johann Wolfgang von Goethe
注意,请保证你的机器上下载了boost库,如果没有,Ubuntu系统下使用:
apt-get install libboost-all-dev
Windows请前往Boost Downloads下载
01_HelloWorld——简单示例
参考自Boost(2):boost.python库介绍及简单示例_boost python_翔底的博客-CSDN博客
假设工作目录如下:
.
├── 01_HelloWorld
│ ├── CMakeLists.txt
│ ├── hello.py
│ └── HelloWorld.cpp
└── CMakeLists.txt
在主目录的CMakeLists.txt:
# 设置项目名称为 Boost_Test
project(Boost_Test)
# 指定 CMake 最低版本号
cmake_minimum_required(VERSION 2.8.3)
set(CMAKE_VERBOSE_MAKEFILE ON)
# 查找 Python 解释器和 Python 库
find_package(PythonInterp REQUIRED)
find_package(PythonLibs ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR} EXACT REQUIRED)
# 查找 Boost 库的 Python 组件
# 根据不同的 Boost 版本,Python 组件的名称可能是 python、python2、python27、python3、python36、python37 等
list(
APPEND _components
python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR}
python${PYTHON_VERSION_MAJOR}
python
)
# 输出 BOOST_ROOT 变量的值
message(BOOST_ROOT " ${BOOST_ROOT}")
# 设置变量
set(_boost_python_found "")
set(Boost_NAMESPACE "libboost")
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_STATIC_RUNTIME OFF)
# 循环查找 Boost 库的 Python 组件
foreach(_component IN ITEMS ${_components})
find_package(Boost COMPONENTS ${_component})
if(Boost_FOUND)
set(_boost_python_found ${_component})
break()
endif()
endforeach()
# 如果没有找到符合要求的 Boost.Python 组件,则输出错误信息
#if(_boost_python_found STREQUAL "")
# message(FATAL_ERROR "No matching Boost.Python component found")
#endif()
# 添加头文件搜索路径
include_directories("${PYTHON_INCLUDE_DIRS}")
include_directories("${Boost_INCLUDE_DIRS}")
# 输出变量的值
message(PYTHON_INCLUDE_DIRS " ${PYTHON_INCLUDE_DIRS}")
message(PYTHON_LIBRARIES " ${PYTHON_LIBRARIES}")
message(Boost_INCLUDE_DIRS " ${Boost_INCLUDE_DIRS}")
message(Boost_LIBRARIES " ${Boost_LIBRARIES}")
# 添加子目录 01_HelloWorld
ADD_SUBDIRECTORY(01_HelloWorld)
然后在01_HelloWorld文件夹下的CMakeLists.txt中控制动态共享库的生成:
# 设置 MODULE_NAME 变量的值为 hello
set(MODULE_NAME hello)
set(source_code_dir ${CMAKE_SOURCE_DIR}/01_HelloWorld)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${source_code_dir}/cpplib)
# 添加头文件搜索路径为 CMAKE_SOURCE_DIR
include_directories(${CMAKE_SOURCE_DIR})
# 添加动态库,名称为 MODULE_NAME 变量的值
add_library(${MODULE_NAME} SHARED
HelloWorld.cpp
)
# 根据操作系统设置动态库属性
if (UNIX)
set_target_properties(${MODULE_NAME}
PROPERTIES
PREFIX ""
)
elseif (WIN32)
set_target_properties(${MODULE_NAME}
PROPERTIES
SUFFIX ".pyd"
)
endif()
# 链接 Boost 和 Python 库
target_link_libraries(${MODULE_NAME}
${Boost_LIBRARIES}
${PYTHON_LIBRARIES}
)
然后我们在HelloWorld.cpp中写入如下代码:
#include <iostream>
#include <string>
// 原始C++函数
std::string greet()
{
std::string str;
std::cout << "Please input your name: ";
std::getline(std::cin,str);
return str + ",hello!";
}
#include <boost/python.hpp>
// hello是python模块的名字,请务必和导入的python模块名一致
BOOST_PYTHON_MODULE(hello)
{
using namespace boost::python;
// 将greet函数定义为python中的greet函数
def("greet", greet);
}
然后我们使用cmake构建:
mkdir build
cd build
cmake ..
make
这时会在01_HelloWorld生成cpplib\hello.so
,然后由于这个动态文件已经为python定义了接口,所以python可以直接将其当成普通模块使用:
#!/usr/bin/env python
import cpplib.hello as hello
print (hello.greet())
输出如下:
show01——更复杂一些的例子
假设我们想要编写一个类,当其初始化的时候会读入二进制文件,然后将其转化为十六进制或二进制输出
这一次我们将boost.python模块的代码放到单独的一个C++文件中,其他的代码按照头文件-源文件的组织方式编写,然后我们使用一个函数来返回C++对象,让它能够被python的GC机制托管
生成模块的代码show01py.cpp
#include <boost/python.hpp>
#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/manage_new_object.hpp>
#include <boost/python/return_value_policy.hpp>
#include "show01.hpp"
BinaryFile* get_binary_file_obj(std::string filename){
return new BinaryFile(filename);
}
BOOST_PYTHON_MODULE(show01)
{
using namespace boost::python;
def("get_binary_file_obj", get_binary_file_obj, return_value_policy<manage_new_object>());
class_<BinaryFile>("BinaryFile", init<std::string>())
.def("get_hex", &BinaryFile::get_hex)
.def("get_binary", &BinaryFile::get_binary)
;
}
show01.hpp & show01.cpp
头文件:
#ifndef __SHOW01_SHOW01_H__
#define __SHOW01_SHOW01_H__
#include <fstream>
#include <string>
class BinaryFile
{
public:
BinaryFile() = delete;
BinaryFile(std::string filename);
~BinaryFile();
// 定义其他函数
std::string get_hex(){ return hex_result_; }
std::string get_binary(){ return binary_result_; }
private:
std::ifstream& fin;
std::string hex_result_;
std::string binary_result_;
void read_content();
std::string to_hex(char ch);
std::string to_bin(char ch);
bool check_exist();
};
#endif /* __SHOW01_SHOW01_H__ */
源文件:
#include <iostream>
#include <sstream>
#include <ctype.h>
#include "show01.hpp"
BinaryFile::BinaryFile(std::string filename)
:fin(*(new std::ifstream(filename))), hex_result_(""), binary_result_("")
{
if (!check_exist())
{
hex_result_ = "cannot open the file!";
binary_result_ = "cannot open the file!";
return;
}else{
read_content();
}
}
BinaryFile::~BinaryFile()
{
fin.close();
delete &fin;
hex_result_ = "Deleted!";
binary_result_ = "Deleted!";
}
void BinaryFile::read_content()
{
char hex_char[17] = {0};
std::stringstream hss;
hss << "00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F | Decoded Text \n";
hss << "------------------------------------------------|----------------\n";
std::stringstream bss;
char buf = 0;
char cnt = 0;
char hexcnt = 0;
while (fin.get(buf)) { // 一个字节一个字节地读取文件
// 处理读取到的字节
hss << to_hex(buf) << ' ';
if (isprint(buf))
{
hex_char[hexcnt++] = buf;
}else
{
hex_char[hexcnt++] = '.';
}
bss << to_bin(buf) << ' ';
++cnt;
if (cnt >= 0x10)
{
hss << "| " << hex_char << '\n';
hexcnt = 0;
bss << '\n';
cnt = 0;
}
}
hex_result_ = hss.str();
binary_result_ = bss.str();
}
std::string BinaryFile::to_hex(char ch)
{
const static char hex_dict[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
char ret[3] = {0};
for(int i = 0; i < 2; i++)
{
ret[1-i] = hex_dict[(ch >> (i*4)) & 0xf];
}
return ret;
}
std::string BinaryFile::to_bin(char ch)
{
char ret[9] = {0};
for(int i = 0; i < 8; i++)
{
ret[7-i] = ((ch >> i) & 0x1) + '0';
}
return ret;
}
bool BinaryFile::check_exist()
{
if(!this->fin.is_open())
{
std::cerr << "cannot open the file!" << std::endl;
return false;
}
return true;
}
子目录的CMakeLists.txt
# 设置 MODULE_NAME 变量的值为 hello
set(MODULE_NAME show01)
set(CMAKE_CXX_STANDARD 20)
set(source_code_dir ${CMAKE_SOURCE_DIR}/show01)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${source_code_dir}/cpplib)
# 添加头文件搜索路径为 CMAKE_SOURCE_DIR
include_directories(${CMAKE_SOURCE_DIR})
set(sourcefiles
show01.cpp
show01py.cpp)
# 添加动态库,名称为 MODULE_NAME 变量的值
add_library(${MODULE_NAME} SHARED
${sourcefiles}
)
# 根据操作系统设置动态库属性
if (UNIX)
set_target_properties(${MODULE_NAME}
PROPERTIES
PREFIX ""
)
elseif (WIN32)
set_target_properties(${MODULE_NAME}
PROPERTIES
SUFFIX ".pyd"
)
endif()
# 链接 Boost 和 Python 库
target_link_libraries(${MODULE_NAME}
${Boost_LIBRARIES}
${PYTHON_LIBRARIES}
)
show01.cpp
import cpplib.show01 as show01
obj = show01.get_binary_file_obj("/cpp/FileAnalyst/show01/test")
print(obj.get_hex())
# print(obj.get_binary())
用于分析的二进制文件test.cpp->test
#include <iostream>
int main(int argc, char const *argv[])
{
std::cout << "Hello World!" << std::endl;
return 0;
}
编译完成后生成ELF文件test
如何跑起来
首先我们主目录的CMakeLists.txt和01_HelloWorld中的基本一样,只用在末尾加一句:
因此我们的组织形式如下:
然后和之前一样的方法cmake就行,或者使用vscode的cmake插件
最后使用命令运行python文件:
python show01.py > hex_output.txt
结果:
头部的ELF魔数说明对这个ELF文件的输出是正确的
扫码阅读此文章
点击按钮复制分享信息
点击订阅