日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

簡單的利用boost.python 和 boost.numpy 實現python和c++之間數據通信例子

作者:meng_zhi_xiang 更新時間: 2022-07-13 編程語言

對利用boost庫生成python庫一點記錄

boost.python對c++很友好,甚至能直接將python的一些數據結構 list(列表),dict(字典)和相互嵌套等數據傳輸到c++,boost.numpy也方便python的數組傳輸到c++端,有時c++端還能直接調用python的函數(很方便調試一些python有的庫而windows編譯困難的庫,相對來說很多庫在linux上編譯總是友好些),本人也是因為任務需要處理list、dict和其相互嵌套才嘗試弄這個的(boost里面不支持set集合,不過自己試過set強制轉換為list好像也是可行的),好東西用起來就很喜歡。

1. 安裝boost庫需要注意的;

linux上好像編譯安裝一些庫都很容易,自己習慣用vs調試代碼,一般盡量windows和linux都安裝,只是通過boost.python 編寫python庫調用,相對來說簡單點。

注意:個人是調用boost的靜態庫,動態庫沒弄成功過,也沒時間、不太想去嘗試了,重點是按需先實現功能。

1.1 linux下編譯boost時候需注意:

linux下需帶上cxxflags=-fPIC cflags=-fPIC編譯選項,在解壓boost源文件后,本人虛擬機上利用anaconda安裝了python3.8環境,用的是 boost_1_79_0的源碼(這個好像需要指定的python版本),切換到python3.8環境下,而且該環境下安裝了numpy,類似如下:
./bootstrap.sh
sudo ./b2 install cxxflags=-fPIC cflags=-fPIC -j8

1.2 windows下有關boost編譯的一點記錄

windows下也要配置好python3.8的環境,里面也要安裝numpy,自己已經配置了環境變量,最終的python庫也是用該環境的,只是編譯boost庫的話還算順利,自己是編譯了的,先前下載了一個自帶動態、靜態boost庫的版本,解壓就有,boost_1_74_0/lib64-msvc-14.2 里面沒有libboost_numpy38的庫,要用numpy傳多維數組則只好自己編譯了,就有了libboost_numpy38-vc142-mt-x64-1_79.lib 庫;指令好像挺簡單,可以兩次雙擊bootstrap.bat,和bootstrap.bat生成的b2.exe(不配置參數的話,甚至32位的庫也給編譯了):

2. 通過boost編寫python接口的例子

個人主要通過cmake,vs2019、anaconda和pycharm等實現方便的c++代碼編寫生成python庫和python環境聯調。通過不斷查找資料、嘗試才實現的,當然能實現大部分也是通過查找資料弄出來的,也謝謝他人的分享,所以自己記錄下,提供簡化的例子也是方便自己也方便他人,能實現雙贏的伙伴或者工作、競爭等關系是最好的,最終實現不了雙贏的總會雙虧或者失去本該能贏的部分,只是時間問題。

2.1 最外層cmakelist文件:

多層cmakelist文件布局確實好用,以前看過人家開源代碼是這樣的,自己嘗試用,發現真好用,例子用的HelloWorld.cpp文件和對應的CMakeLists.txt文件放到了子目錄“01_HelloWorld”下,通過外層cmakelist文件ADD_SUBDIRECTORY(01_HelloWorld)包含子目錄“01_HelloWorld”。

注意windows下的cmakelist文件里面:set(BOOST_ROOT "修改為你們自己的boost目錄”),linux下安裝boost會自動配置環境變量之類的,注冊這塊就行,下面注釋掉的可以不用管,都是自己不斷嘗試的結果。

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 內層cmakelist 文件:

這個也是找別人資料基礎上修改的。#set(CMAKE_LIBRARY_OUTPUT_DIRECTORY “配置輸出python庫的路徑很有用”) ,這個配置庫生成路徑挺方便的,個人在linux下有用,cmakelist配置文件內部如果不特意指定的話默認生成的是python動態庫,windows上修改后綴為.pyd,linux上后綴.so,個人測試過,將生成的庫放到需要調用該庫的python文件夾下就可以,個人生成的都是python動態庫(雖然調用的boost用的是靜態庫)。

set(MODULE_NAME  hello_world)

# 設置靜態庫文件目錄
#set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
# 動態庫文件目錄
#set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
# 可執行文件目錄
#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 內層HelloWorld.cpp文件:

需要注意的是BOOST_PYTHON_MODULE(hello_world)里面hello_world是模塊名,這個需要和里層cmakelist里面的set(MODULE_NAME hello_world)相對應。具體的生成的python類名或者函數名不需要和c++定義時候一模一樣,肯定需要做對應的映射,這就是BOOST_PYTHON_MODULE里面做的事情,配置就好,這個比自己先前利用swig做python庫方便太多了。


#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時調用初始化

        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++?')");//執行python代碼 ?
        }

        //Py_Finalize();VS 嵌入python時調用結束 end?
    }
    catch (bp::error_already_set) {
        PyErr_Print();
    }

    if (dt1.has_key("a"))
    {
        std::string dt1String = bp::extract<std::string>(dt1["a"]);//key "a"需要存在,才能正常調用

        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();//浮點類型的話,strdPtr[1]=4(4個字節),strdPtr[0]/strdPtr[1]==shapePtr[1](列數)

    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++端調用python函數獲得結果,比如某些函數很難通過編譯c++的庫進而調用(比如windows編譯難,linux編譯簡單,則windows上臨時調試可采用此方法,待移植到linux上用相應的c++庫替換則可)

    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);//可保存函數func到本地,根據自己需要調用
        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調用名字和c++可不一致,但參數需要保持一致
    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生產vs工程,然后配置vs工程里面庫輸出路徑(放到需要調用該庫的python文件下,如自己的test_boost_python_api.py),cmakelist文件沒修改的話其實就不用cmake-gui.exe來配置了,每次修改cpp文件就可以了,vs上生成庫就好。

2.4 python調用庫的例子

test_boost_python_api.py文件內容如下,一些簡單測試,比如傳遞list、dict和嵌套的數據,c++調用python的簡單測試。

#!/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

結語:

以上做一些記錄,不一定都對,個人很多時候都是嘗試并加上自己的一些理解才弄出來的,對需要的人應該總有些啟發和幫助的,長時間不用的東西肯定會忘記的,所以需要積累和記錄,不過一旦看了記錄就大致能明白怎么做了。評論一般不回的,謝謝,愿世界惡意少點,對自己可以嚴格點,但對別人需要寬容點,壞情緒會傳染的。

人生需要多點嘗試,也需時刻反省。交際、勇氣、認知、格局等等限制了個人的能力,自己也是很菜的,搞工程、軟件這么多年,沒做太深、交際太差、弄得處境很難看,雖然還在掙扎,最后怕是難免轉行,也不想抱怨什么,也不想評價別人,且行且珍惜,能通過分享節約別人的時間也好,什么都從頭開始很不現實、也太難了,孤軍奮戰基本注定悲劇的…

原文鏈接:https://blog.csdn.net/meng_zhi_xiang/article/details/125749150

欄目分類
最近更新