網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
簡(jiǎn)單的利用boost.python 和 boost.numpy 實(shí)現(xiàn)python和c++之間數(shù)據(jù)通信例子
作者:meng_zhi_xiang 更新時(shí)間: 2022-07-13 編程語(yǔ)言對(duì)利用boost庫(kù)生成python庫(kù)一點(diǎn)記錄
boost.python對(duì)c++很友好,甚至能直接將python的一些數(shù)據(jù)結(jié)構(gòu) list(列表),dict(字典)和相互嵌套等數(shù)據(jù)傳輸?shù)絚++,boost.numpy也方便python的數(shù)組傳輸?shù)絚++端,有時(shí)c++端還能直接調(diào)用python的函數(shù)(很方便調(diào)試一些python有的庫(kù)而windows編譯困難的庫(kù),相對(duì)來(lái)說(shuō)很多庫(kù)在linux上編譯總是友好些),本人也是因?yàn)槿蝿?wù)需要處理list、dict和其相互嵌套才嘗試弄這個(gè)的(boost里面不支持set集合,不過(guò)自己試過(guò)set強(qiáng)制轉(zhuǎn)換為list好像也是可行的),好東西用起來(lái)就很喜歡。
1. 安裝boost庫(kù)需要注意的;
linux上好像編譯安裝一些庫(kù)都很容易,自己習(xí)慣用vs調(diào)試代碼,一般盡量windows和linux都安裝,只是通過(guò)boost.python 編寫(xiě)python庫(kù)調(diào)用,相對(duì)來(lái)說(shuō)簡(jiǎn)單點(diǎn)。
注意:個(gè)人是調(diào)用boost的靜態(tài)庫(kù),動(dòng)態(tài)庫(kù)沒(méi)弄成功過(guò),也沒(méi)時(shí)間、不太想去嘗試了,重點(diǎn)是按需先實(shí)現(xiàn)功能。
1.1 linux下編譯boost時(shí)候需注意:
linux下需帶上cxxflags=-fPIC cflags=-fPIC編譯選項(xiàng),在解壓boost源文件后,本人虛擬機(jī)上利用anaconda安裝了python3.8環(huán)境,用的是 boost_1_79_0的源碼(這個(gè)好像需要指定的python版本),切換到python3.8環(huán)境下,而且該環(huán)境下安裝了numpy,類(lèi)似如下:
./bootstrap.sh
sudo ./b2 install cxxflags=-fPIC cflags=-fPIC -j8
1.2 windows下有關(guān)boost編譯的一點(diǎn)記錄
windows下也要配置好python3.8的環(huán)境,里面也要安裝numpy,自己已經(jīng)配置了環(huán)境變量,最終的python庫(kù)也是用該環(huán)境的,只是編譯boost庫(kù)的話還算順利,自己是編譯了的,先前下載了一個(gè)自帶動(dòng)態(tài)、靜態(tài)boost庫(kù)的版本,解壓就有,boost_1_74_0/lib64-msvc-14.2 里面沒(méi)有l(wèi)ibboost_numpy38的庫(kù),要用numpy傳多維數(shù)組則只好自己編譯了,就有了libboost_numpy38-vc142-mt-x64-1_79.lib 庫(kù);指令好像挺簡(jiǎn)單,可以?xún)纱坞p擊bootstrap.bat,和bootstrap.bat生成的b2.exe(不配置參數(shù)的話,甚至32位的庫(kù)也給編譯了):
2. 通過(guò)boost編寫(xiě)python接口的例子
個(gè)人主要通過(guò)cmake,vs2019、anaconda和pycharm等實(shí)現(xiàn)方便的c++代碼編寫(xiě)生成python庫(kù)和python環(huán)境聯(lián)調(diào)。通過(guò)不斷查找資料、嘗試才實(shí)現(xiàn)的,當(dāng)然能實(shí)現(xiàn)大部分也是通過(guò)查找資料弄出來(lái)的,也謝謝他人的分享,所以自己記錄下,提供簡(jiǎn)化的例子也是方便自己也方便他人,能實(shí)現(xiàn)雙贏的伙伴或者工作、競(jìng)爭(zhēng)等關(guān)系是最好的,最終實(shí)現(xiàn)不了雙贏的總會(huì)雙虧或者失去本該能贏的部分,只是時(shí)間問(wèn)題。
2.1 最外層cmakelist文件:
多層cmakelist文件布局確實(shí)好用,以前看過(guò)人家開(kāi)源代碼是這樣的,自己嘗試用,發(fā)現(xiàn)真好用,例子用的HelloWorld.cpp文件和對(duì)應(yīng)的CMakeLists.txt文件放到了子目錄“01_HelloWorld”下,通過(guò)外層cmakelist文件ADD_SUBDIRECTORY(01_HelloWorld)包含子目錄“01_HelloWorld”。
注意windows下的cmakelist文件里面:set(BOOST_ROOT "修改為你們自己的boost目錄”),linux下安裝boost會(huì)自動(dòng)配置環(huán)境變量之類(lèi)的,注冊(cè)這塊就行,下面注釋掉的可以不用管,都是自己不斷嘗試的結(jié)果。
project(Boost_Test)
cmake_minimum_required(VERSION 3.17)
# find python
find_package(PythonInterp REQUIRED)
find_package(PythonLibs ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR} EXACT REQUIRED)
# now search for the boost component
# depending on the boost version it is called either python,
# python2, python27, python3, python36, python37, ...
list(
APPEND _components
python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR}
python${PYTHON_VERSION_MAJOR}
python
)
#set(_Boost_NUMPY_DEPENDENCIES python${component_python_version})
set(BOOST_ROOT "D:/software/app2/Third-party_libraries/boost/boost_1_79_0")
#set(Boost_INCLUDE_DIR "D:/software/app2/Third-party_libraries/boost/1.74.0/boost")
#set(Boost_LIBRARIES "D:/software/app2/Third-party_libraries/boost/1.74.0/stage/lib")
message(BOOST_ROOT " ${BOOST_ROOT}")
set(_boost_python_found "")
#set(Boost_NAMESPACE "libboost")
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_LIBS ON)
#set(Boost_USE_STATIC_RUNTIME OFF)
foreach(_component IN ITEMS ${_components})
find_package(Boost COMPONENTS ${_component})
if(Boost_FOUND)
set(_boost_python_found ${_component})
break()
endif()
endforeach()
#if(_boost_python_found STREQUAL "")
# message(FATAL_ERROR "No matching Boost.Python component found")
#endif()
#
find_package(Boost REQUIRED COMPONENTS
numpy38
)
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}")
ADD_SUBDIRECTORY(01_HelloWorld)
2.2 內(nèi)層cmakelist 文件:
這個(gè)也是找別人資料基礎(chǔ)上修改的。#set(CMAKE_LIBRARY_OUTPUT_DIRECTORY “配置輸出python庫(kù)的路徑很有用”) ,這個(gè)配置庫(kù)生成路徑挺方便的,個(gè)人在linux下有用,cmakelist配置文件內(nèi)部如果不特意指定的話默認(rèn)生成的是python動(dòng)態(tài)庫(kù),windows上修改后綴為.pyd,linux上后綴.so,個(gè)人測(cè)試過(guò),將生成的庫(kù)放到需要調(diào)用該庫(kù)的python文件夾下就可以,個(gè)人生成的都是python動(dòng)態(tài)庫(kù)(雖然調(diào)用的boost用的是靜態(tài)庫(kù))。
set(MODULE_NAME hello_world)
# 設(shè)置靜態(tài)庫(kù)文件目錄
#set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
# 動(dòng)態(tài)庫(kù)文件目錄
#set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
# 可執(zhí)行文件目錄
#set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
include_directories(${CMAKE_SOURCE_DIR})
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()
target_link_libraries(${MODULE_NAME}
${Boost_LIBRARIES}
${PYTHON_LIBRARIES}
)
2.3 內(nèi)層HelloWorld.cpp文件:
需要注意的是BOOST_PYTHON_MODULE(hello_world)里面hello_world是模塊名,這個(gè)需要和里層cmakelist里面的set(MODULE_NAME hello_world)相對(duì)應(yīng)。具體的生成的python類(lèi)名或者函數(shù)名不需要和c++定義時(shí)候一模一樣,肯定需要做對(duì)應(yīng)的映射,這就是BOOST_PYTHON_MODULE里面做的事情,配置就好,這個(gè)比自己先前利用swig做python庫(kù)方便太多了。
#define BOOST_PYTHON_STATIC_LIB
#define BOOST_NUMPY_STATIC_LIB
#include <iostream>
#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
//#include <boost/python/list.hpp>
//#include <boost/python/return_by_value.hpp>
//#include <boost/python/return_value_policy.hpp>
namespace bp = boost::python;
namespace np = boost::python::numpy;
//using namespace bp;
bp::list list_reverse(bp::list& list1)
{
std::cout << "Input list length c++ reverse: " << len(list1) << std::endl;
list1.reverse();
return list1;
}
bp::dict dict_test(bp::dict& dt1)
{
std::cout << "Input dict length : " << len(dt1) << std::endl;
bp::list temKeys = dt1.keys();
bp::list temValues = dt1.values();
try {
//Py_Initialize();//VS 嵌入python時(shí)調(diào)用初始化
for (int i = 0; i < len(dt1); i++)
{
bp::object msg = "%s : %s" % bp::make_tuple(temKeys[i], temValues[i]);
std::string dt1String = bp::extract<std::string>(msg);
std::cout << dt1String << std::endl;
//bp::object ignored = bp::exec("print('c++?')");//執(zhí)行python代碼 ?
}
//Py_Finalize();VS 嵌入python時(shí)調(diào)用結(jié)束 end?
}
catch (bp::error_already_set) {
PyErr_Print();
}
if (dt1.has_key("a"))
{
std::string dt1String = bp::extract<std::string>(dt1["a"]);//key "a"需要存在,才能正常調(diào)用
std::cout << "dict has_key a:" << dt1String << std::endl;
}
else
{
dt1.setdefault("a", "default");
dt1.setdefault("float_pi", 3.1415926);
}
return dt1;
}
void list_maps(bp::list& list2)
{
auto a= list2[0];
for (size_t i = 0; i < len(list2); i++)
{
a = list2[i];
bp::dict dic = bp::extract<bp::dict>(a);
bp::list temKeys = dic.keys();
bp::list temValues = dic.values();
std::cout << "list :"<<i << std::endl;
for (int j = 0; j < len(dic); j++)
{
bp::object msg = "%s : %s" % bp::make_tuple(temKeys[j], temValues[j]);
std::string dt1String = bp::extract<std::string>(msg);
std::cout << dt1String << " ";
}
std::cout << "" << std::endl;
}
}
void two_dimensional_array(np::ndarray & ndArray1)
{
//ndArray1.get_shape();
int shapeSize = ndArray1.get_nd();//len(ndArray1);
std::cout << "shapeSize :" << shapeSize << std::endl;
if (2 != shapeSize)
{
std::cout << "ndarray is not two dimension ,but is:" << shapeSize << std::endl;
return;
}
auto shapePtr= ndArray1.get_shape();
auto strdPtr = ndArray1.get_strides();
std::cout << "shape is :[" << shapePtr[0]<<","<< shapePtr[1]<<"]" << std::endl;
std::cout << "get_strides is :[" << strdPtr[0]<<","<< strdPtr[1]<<"]" << std::endl;
int step = strdPtr[0] / strdPtr[1];
std::cout << "step is :[" << step << "]" << std::endl;
float* npF= (float*)ndArray1.get_data();//浮點(diǎn)類(lèi)型的話,strdPtr[1]=4(4個(gè)字節(jié)),strdPtr[0]/strdPtr[1]==shapePtr[1](列數(shù))
for (size_t i = 0; i < shapePtr[0]* shapePtr[1]; i++)
{
npF[i] *= npF[i];//square
}
}
char const* greet()
{
return "hello, world,are you ready";
}
float cppCallPythonLogaddexp(PyObject* func, int a, int b)
{
//可不返回,目的是c++端調(diào)用python函數(shù)獲得結(jié)果,比如某些函數(shù)很難通過(guò)編譯c++的庫(kù)進(jìn)而調(diào)用(比如windows編譯難,linux編譯簡(jiǎn)單,則windows上臨時(shí)調(diào)試可采用此方法,待移植到linux上用相應(yīng)的c++庫(kù)替換則可)
float result = bp::call<float>(func, a, b);
std::cout << "cppCallPythonLogaddexp(" << a << "," << b << ")=" << result << std::endl;
return result;
}
class Cpp2PythonClass
{
public:
Cpp2PythonClass(PyObject* func1) :cppLogaddexp(func1) {};
~Cpp2PythonClass() {};
void callbackFun1Test() {
int a1 = 55;
int a2 = 66;
float result2 = bp::call<float>(cppLogaddexp, a1, a2);//可保存函數(shù)func到本地,根據(jù)自己需要調(diào)用
std::cout << "cppCallPythonLogaddexp(" << a1 << "," << a2 << ")=" << result2 << std::endl;
};
private:
PyObject* cppLogaddexp;
};
BOOST_PYTHON_MODULE(hello_world)
{
using namespace boost::python;
np::initialize();//boost::python::numpy;需要用到
boost::python::def("greet", greet);//python調(diào)用名字和c++可不一致,但參數(shù)需要保持一致
def("cppCallPythonLogaddexp", cppCallPythonLogaddexp);//好像不加return_value_policy<return_by_value>()也可以
def("list_reverse", list_reverse, args("list1"), return_value_policy<return_by_value>());
def("dict_test", dict_test, args("dt1"), return_value_policy<return_by_value>());
def("list_maps", list_maps);
def("two_dimensional_array", two_dimensional_array);
class_<Cpp2PythonClass>("Cpp2PythonClass", boost::python::init<PyObject*>())
.def("callbackFun1Test", &Cpp2PythonClass::callbackFun1Test);
}
自己是配置好cmakelist文件和c++等文件后,用cmake-gui.exe生產(chǎn)vs工程,然后配置vs工程里面庫(kù)輸出路徑(放到需要調(diào)用該庫(kù)的python文件下,如自己的test_boost_python_api.py),cmakelist文件沒(méi)修改的話其實(shí)就不用cmake-gui.exe來(lái)配置了,每次修改cpp文件就可以了,vs上生成庫(kù)就好。
2.4 python調(diào)用庫(kù)的例子
test_boost_python_api.py文件內(nèi)容如下,一些簡(jiǎn)單測(cè)試,比如傳遞list、dict和嵌套的數(shù)據(jù),c++調(diào)用python的簡(jiǎn)單測(cè)試。
#!/usr/bin/env python
import sys
import numpy as np
import hello_world
def test1():
l1 = ["df", "df2", "56", 1]
l2 = hello_world.list_reverse(l1)
hello_world.dict_test({"a": "aaa", "b": "222"})
# print(l2)
# print(hello_world.greet())
list_dict = [
{'g1': '545', 'h2': '什么', 'p1': -1.26, 'p2': -1.65854,'p3': -9.56447},
{'g1': '545', 'h2': '納尼', 'p1': -5.65, 'p2': -8.15,'p3': -63.15},
{'g1': '545', 'h2': 'what?', 'p1': -15.9414, 'p2': -4.3564,'p3': -56.348},
]
hello_world.list_maps(list_dict)
def test2():
np1 = np.arange(15, dtype="float32").reshape(3, 5)
print(type(np1), np1.dtype)
print(np1)
hello_world.two_dimensional_array(np1)
print(np1)
def test3():
dic1 = {"a1": "aaa", "b": "222"}
hello_world.dict_test(dic1)
print(dic1)
print(type(dic1["float_pi"]))
a2 = hello_world.cppCallPythonLogaddexp(np.logaddexp, 2, 3)
print("a2==", a2)
a1 = np.logaddexp(2, 3)
print("python call np.logaddexp(2, 3)==", a1)
print("\n----------------------Cpp2PythonClass----------------------------")
cpc_obj = hello_world.Cpp2PythonClass(np.logaddexp)
cpc_obj.callbackFun1Test()
print("----------------------Cpp2PythonClass.callbackFun1Test ----------------------------\n")
a3 = np.logaddexp(55, 66)
print("python call np.logaddexp(55, 66)==", a3)
if __name__ == '__main__':
test1()
pass
結(jié)語(yǔ):
以上做一些記錄,不一定都對(duì),個(gè)人很多時(shí)候都是嘗試并加上自己的一些理解才弄出來(lái)的,對(duì)需要的人應(yīng)該總有些啟發(fā)和幫助的,長(zhǎng)時(shí)間不用的東西肯定會(huì)忘記的,所以需要積累和記錄,不過(guò)一旦看了記錄就大致能明白怎么做了。評(píng)論一般不回的,謝謝,愿世界惡意少點(diǎn),對(duì)自己可以嚴(yán)格點(diǎn),但對(duì)別人需要寬容點(diǎn),壞情緒會(huì)傳染的。
人生需要多點(diǎn)嘗試,也需時(shí)刻反省。交際、勇氣、認(rèn)知、格局等等限制了個(gè)人的能力,自己也是很菜的,搞工程、軟件這么多年,沒(méi)做太深、交際太差、弄得處境很難看,雖然還在掙扎,最后怕是難免轉(zhuǎn)行,也不想抱怨什么,也不想評(píng)價(jià)別人,且行且珍惜,能通過(guò)分享節(jié)約別人的時(shí)間也好,什么都從頭開(kāi)始很不現(xiàn)實(shí)、也太難了,孤軍奮戰(zhàn)基本注定悲劇的…
原文鏈接:https://blog.csdn.net/meng_zhi_xiang/article/details/125749150
相關(guān)推薦
- 2022-07-03 el-upload上傳組件的動(dòng)態(tài)添加;el-upload動(dòng)態(tài)上傳文件;el-upload區(qū)分文件是哪
- 2022-06-22 C#操作ini文件的幫助類(lèi)_C#教程
- 2022-11-14 Go語(yǔ)言Goroutinue和管道效率詳解_Golang
- 2022-05-17 【go】解決“dial tcp 142.251.42.241:443: connect: conne
- 2022-04-03 Flutter折疊控件使用方法詳解_Android
- 2022-08-23 python畫(huà)圖中文不顯示問(wèn)題的解決方法_python
- 2022-10-10 Go?語(yǔ)言前綴樹(shù)實(shí)現(xiàn)敏感詞檢測(cè)_Golang
- 2022-10-15 python?實(shí)現(xiàn)syslog?服務(wù)器的詳細(xì)過(guò)程_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支