Protobuf是google開發的一個序列化和反序列化的協議庫,我們可以自己設計傳遞資料的格式,通過.proto檔案定義我們的要傳遞的資料格式,例如,在深度學習中常用的ONNX交換模型就是使用.proto撰寫的,我們可以通過多種前端(MNN、NCNN、TVM的前端)去讀取這個.onnx這個模型,但是首先你要安裝protobuf,
在之前的博文中已經簡單介紹了onnx,其中onnx.proto就代表了onnx模型的基本資料結構,一般來說,protobuf經常搭配Cmake使用,Cmake有官方的modules,可以通過簡單的幾個命令protobuf_generate_cpp來生成對應的.pb.cc和.pb.h,
簡單的例子:
find_package(Protobuf REQUIRED)
include_directories(${Protobuf_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS foo.proto)
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS EXPORT_MACRO DLL_EXPORT foo.proto)
protobuf_generate_python(PROTO_PY foo.proto)
add_executable(bar bar.cc ${PROTO_SRCS} ${PROTO_HDRS})
target_link_libraries(bar ${Protobuf_LIBRARIES})
但是這個例子太簡單了,如果我們的.proto檔案只有一個或者說都只在一個目錄里,那用這個命令沒什么毛病...
但如果是這種情況,我們的檔案目錄如下:
├── CMakeLists.txt
├── README.md
├── meta
│ └── proto
│ ├── CMakeLists.txt
│ └── common
│ ├── bar
│ │ ├── CMakeLists.txt
│ │ └── bar.proto
│ └── foo
│ ├── CMakeLists.txt
│ └── foo.proto
└── src
├── CMakeLists.txt
├── c_proto.cc
└── c_proto.hh
其中foo.proto檔案如下:
message foo_msg
{
optional string name = 1;
}
bar.proto的檔案如下:
import "common/foo/foo.proto";
message bar_msg
{
optional foo_msg foo = 1;
optional string name = 2;
}
如上,bar檔案參考foo,而且這兩個不在一個目錄,如果直接使用protobuf_generate_cpp來生成,直接會報錯,(這個例子取自Yu的一篇博文)
也想過把他倆放到同一個目錄...然后bar.proto中import的代碼就要修改,雖然這樣可以,但顯然是不適合大型的專案,
而這個大型專案顯然就是mediapipe...折磨了我好久,
關于mediapipe的詳細介紹在另一篇文章,mediapipe中使用了大量的ProtoBuf技術來表示圖結構,而且mediapipe原生并不是采用cmake來構建專案,而是使用google自家研發的bazel,這個專案構建系統我就不評價了,而現在我需要使用Cmake來對其進行構建,

這也是噩夢的開始,mediapipe的.proto檔案很多,核心的framework的目錄下存在很多的.proto檔案,根目錄和子目錄都有.proto檔案:

而且每個proto檔案之間存在參考的順序,framework根目錄下的calculator.proto檔案:
// mediapipe/framework/calculator.proto
syntax = "proto3";
package mediapipe;
import public "mediapipe/framework/calculator_options.proto";
import "google/protobuf/any.proto";
import "mediapipe/framework/mediapipe_options.proto";
import "mediapipe/framework/packet_factory.proto";
import "mediapipe/framework/packet_generator.proto";
import "mediapipe/framework/status_handler.proto";
import "mediapipe/framework/stream_handler.proto";
每個.proto檔案都import了其他目錄下的檔案,這里的import類似于C++中的include,但是這里的import又可以相互參考,例如上述的status_handler.proto也參考了mediapipe_options.proto,
如果直接對上述所有的.proto檔案直接使用protobuf_generate_cpp命令,會直接報錯,因為這些檔案不在一個目錄,而且import的相對目錄也無法分析,另外,不同目錄內的.cc檔案會參考相應目錄生成的.pb.h檔案,我們需要生成的.pb.cc和.pb.h在原始的目錄中,這樣才可以正常參考,要不然需要修改其他源代碼的include地址,比較麻煩,
CLion中Cmake來編譯proto生成的.pb.cc和.pb.h不在原始目錄,而是集中在cmake-build-debug(release)中,我們額外需要將其中生成的.pb.cc和.pb.h檔案移動到原始地址(Clion的情況是這樣),
正確修改cmake
對于這種情況,比較合適的做法是直接使用命令進行生成,
首先找到所有需要編譯的.proto檔案:
file(GLOB protobuf_files
mediapipe/framework/*.proto
mediapipe/framework/tool/*.proto
mediapipe/framework/deps/*.proto
mediapipe/framework/testdata/*.proto
mediapipe/framework/formats/*.proto
mediapipe/framework/formats/annotation/*.proto
mediapipe/framework/formats/motion/*.proto
mediapipe/framework/formats/object_detection/*.proto
mediapipe/framework/stream_handler/*.proto
mediapipe/util/*.proto
mediapipe/calculators/internal/*.proto
)
接下來,定義相關的目錄地址,PROTO_META_BASE_DIR為編譯之后生成檔案的目錄,PROTO_FLAGS很重要,指定編譯.proto檔案時的總的尋找路徑,.proto中的import命令根據根據這個地址去連接其他的.proto檔案:
SET(PROTO_META_BASE_DIR ${CMAKE_CURRENT_BINARY_DIR})
LIST(APPEND PROTO_FLAGS -I${CMAKE_CURRENT_SOURCE_DIR})
設定好之后,通過FOREACH去回圈之前的.proto檔案,依次編譯每個檔案,然后將生成的.pb.cc和.pb.h移動回原始的目錄,至此就可以正常作業了,
FOREACH(FIL ${protobuf_files})
GET_FILENAME_COMPONENT(FIL_WE ${FIL} NAME_WE)
string(REGEX REPLACE ".+/(.+)\\..*" "\\1" FILE_NAME ${FIL})
string(REGEX REPLACE "(.+)\\${FILE_NAME}.*" "\\1" FILE_PATH ${FIL})
string(REGEX MATCH "(/mediapipe/framework.*|/mediapipe/util.*|/mediapipe/calculators/internal/)" OUT_PATH ${FILE_PATH})
set(PROTO_SRCS "${CMAKE_CURRENT_BINARY_DIR}${OUT_PATH}${FIL_WE}.pb.cc")
set(PROTO_HDRS "${CMAKE_CURRENT_BINARY_DIR}${OUT_PATH}${FIL_WE}.pb.h")
EXECUTE_PROCESS(
COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} ${PROTO_FLAGS} --cpp_out=${PROTO_META_BASE_DIR} ${FIL}
)
message("Copying " ${PROTO_SRCS} " to " ${FILE_PATH})
file(COPY ${PROTO_SRCS} DESTINATION ${FILE_PATH})
file(COPY ${PROTO_HDRS} DESTINATION ${FILE_PATH})
ENDFOREACH()
參考鏈接
http://blog.argcv.com/articles/3884.c
https://www.v2ex.com/t/602363
https://stackoverflow.com/questions/29720410/no-member-found-when-use-cmake-construct-proto/29817843#answer-29727925
撩我吧
- 如果你與我志同道合于此,老潘很愿意與你交流;
- 如果你喜歡老潘的內容,歡迎關注和支持,
- 如果你喜歡我的文章,希望點贊?? 收藏 ?? 評論 ?? 三連一下~
想知道老潘是如何學習踩坑的,想與我交流問題~請關注公眾號「oldpan博客」,
老潘也會整理一些自己的私藏,希望能幫助到大家,點擊神秘傳送門獲取,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/270944.html
標籤:設計模式
下一篇:設計模式-組合模式搭配訪問者模式
