pybind11使用記錄---使用cmake編譯c++工程為python庫
前言:
因為最近c++下的工程需要在python下呼叫,所以需要把c++編譯成可供python呼叫的庫,記錄一下具體做法:
編譯c++有多種方法,因為我的是cmake構建的工程,所以直接在cmake的基礎上編譯成python庫(c++中用了第三方庫opencv和boost)
下載編譯pybind11
下載pybind11:git clone https://github.com/pybind/pybind11.git
安裝pytest: pip install pytest
編譯:
cd pybind11 mkdir build cd build cmake .. cmake --build . --config Release --target check
操作C++程式碼
我的做法是將編譯好的pybind11資料夾拷貝到了c++工程目錄下(這樣是方便在編譯c++的時候能找到pybind11,當然你也可以通過其他方式,只要能找到pybind11就行)
(我只封裝了所需要的函式介面)
將你需要的函式介面定義在一個.cpp檔案中,比如說,我在pcc.cpp檔案中定義了兩個函式(該包含的.h檔案就正常包含):
int pcc_encoder() 和 int pcc_decoder()
然後在此檔案中加入如下程式碼:
#include <pybind11/pybind11.h> namespace py = pybind11; PYBIND11_PLUGIN(Pypcc) { py::module m("Pypcc", "pcc python module"); m.def("pcc_encoder", &pcc_encoder, "Encoder the pointcloud data"); m.def("pcc_decoder", &pcc_decoder, "Decoder the pointcloud data"); return m.ptr(); }
其中比較重要的是這兩句:
m.def("pcc_encoder", &pcc_encoder, "Encoder the pointcloud data");
m.def("pcc_decoder", &pcc_decoder, "Decoder the pointcloud data");
這兩句程式碼表明瞭可供python呼叫的函式介面,沒有在此操作的函式python是找不到的
修改CMakeLists.txt
(只需改動幾處即可,第三方庫的include和連結基本和c++編譯一樣)
PROJECT(compression) CMAKE_MINIMUM_REQUIRED(VERSION 2.1.8) add_definitions(-std=c++11) find_package(Boost COMPONENTS system thread program_options) find_package(OpenCV QUIET) set(LIBRARY_OUTPUT_PATH ../) set(dso_SOURCE_FILES ${PROJECT_SOURCE_DIR}/src/decoder.cpp ${PROJECT_SOURCE_DIR}/src/encoder.cpp ${PROJECT_SOURCE_DIR}/src/utils.cpp ${PROJECT_SOURCE_DIR}/src/io.cpp ${PROJECT_SOURCE_DIR}/src/pcc_module.cpp ) #add_library(pcc SHARED ${dso_SOURCE_FILES}) include_directories( ${PROJECT_SOURCE_DIR}/include ${OpenCV_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} ) add_subdirectory(pybind11) pybind11_add_module(Pypcc ${PROJECT_SOURCE_DIR}/src/pcc.cpp ${dso_SOURCE_FILES}) #add_executable(pcc_test ${PROJECT_SOURCE_DIR}/src/pcc_test.cpp ${dso_SOURCE_FILES}) #add_executable(pcc_test ${PROJECT_SOURCE_DIR}/src/pcc_test.cpp) target_link_libraries(Pypcc PRIVATE ${Boost_LIBRARIES} ${OpenCV_LIBS} )
可以看到只需要修改原本檔案的幾個地方就可以:
set(LIBRARY_OUTPUT_PATH ../)
這個的意思是設定生成的python庫的位置
add_subdirectory(pybind11)
這個是用來增加pybind11的目錄,因為之前我把pybind11放在了c++的工程目錄下,所以可以直接順利找到
pybind11_add_module(Pypcc ${PROJECT_SOURCE_DIR}/src/pcc.cpp ${dso_SOURCE_FILES})
這個的意思對應於編譯c++的add_executable命令,就是說把哪些檔案編譯成python庫,Pypcc是取的庫的名字
target_link_libraries(Pypcc PRIVATE
${Boost_LIBRARIES}
${OpenCV_LIBS}
)
這裡是連結第三方庫,注意要加上PRIVATE
然後按照一般的c++編譯步驟就可以了,編譯完成之後我們可以看到在指定目錄下生成.so檔案
編寫python呼叫檔案
import Pypcc #匯入.so模組
Pypcc.XXX() #呼叫模組的函式
這裡說明一下:因為我的.so檔案放在了和.py檔案同一目錄下,所以可以直接匯入
遇到的問題:
- 因為我的.so檔案是在python3.6的環境下編譯的,所以python2.7環境下匯入報出沒有這個模組的錯誤,原因到底是編譯c++環境的原因,還是一開始編譯pybind11預設的python環境的原因?後續可以驗證一下
- 因為我需要傳二維陣列給這個引數,考慮到python和c++之間用純c語言“溝通”可能會好一點,所以打算以這樣的方式傳參:
float points[][4]
int nums
但是在編譯的時候報出錯誤:
cannot convert ‘pybind11::detail::type_caster<float, void>::cast_op_type<float (*&&)[4]> {aka float*}’ to ‘float (*)[4]’ in argument passing
看到有人說pybind11不支援這種指標型別???這裡還沒弄清楚
後面我的解決方法是使用STL vector進行傳參的,c++中的STL與python是有型別對應的:
前提是要在之前的.cpp檔案中包含一個頭檔案
#include <pybind11/stl.h>
- 在c++中,一般在.h中宣告一個函式的時候,可以賦給引數預設值,這樣在呼叫的時候如果預設值的引數沒有傳進來,那麼函式就是用其預設值,但是如果某個函式作為python介面供python呼叫,即使在c++中聲明瞭預設值,python呼叫的時候也要將全部引數傳參,否則會出現引數不匹配的錯誤