文章

Protobuf问题

Protobuf问题

该文记录使用 Protobuf 问题。

Protobuf问题

1. 动态链接

Google 默认编译模式是静态库,可以在 cmake-gui 中勾选 protobuf_BUILD_SHARED_LIBS 编译成动态库

自己项目使用时还需要设置宏 PROTOBUF_USE_DLLS 指定使用 protobuf 动态库

1
add_compile_definitions(PROTOBUF_USE_DLLS)

2. proto 文件已存在1

问题的详细信息如下

1
2
3
4
5
[libprotobuf ERROR google/protobuf/descriptor_database.cc:58] File already exists in database: address.proto
[libprotobuf FATAL google/protobuf/descriptor.cc:1358] CHECK failed: GeneratedDatabase()->Add(encoded_file_descriptor, size): 
terminate called after throwing an instance of 'google::protobuf::FatalException'
  what():  CHECK failed: GeneratedDatabase()->Add(encoded_file_descriptor, size): 
Aborted (core dumped)

这个错误原因是因为在两个编译目标(共享库或者可执行文件)中都引入了相同的 *.pb.cc 文件。

protobuf 本身有一个 global 的 registry,每个 message type 都需要去那里注册一下,而且不能重复注册。

最常规的解决办法就是把所有 address.proto 生成的 address.pb.haddress.pb.cc 编译成一个共享库 address.dll,然后其他要使用的工程都去链接这个共享库。

值得注意的是这样做就需要导入导出符号,需要进行以下修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 设置 protobuf 目录
if(CMAKE_CXX_PLATFORM_ID MATCHES "Windows")
    set(CMAKE_PREFIX_PATH "C:\\Program Files\\protobuf")
elseif(CMAKE_CXX_PLATFORM_ID MATCHES "Linux")
    set(CMAKE_PREFIX_PATH "")
endif()

macro(AddProtobufInc)
    find_package(Protobuf REQUIRED)
    include_directories(${PROTOBUF_INCLUDE_DIRS})

    # 关键在这里,通过 EXPORT_MACRO 设置导入导出宏为 PROTO_EXPORT
    protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS "address.proto"
        EXPORT_MACRO "PROTO_EXPORT"
    )
endmacro()

macro(AddProtobufLib)
    add_compile_definitions(PROTOBUF_USE_DLLS)
    # 编译成动态库时设置为导出宏,Windows 导入导出不一样
    if(CMAKE_CXX_PLATFORM_ID MATCHES "Windows")
        add_compile_definitions(PROTO_EXPORT=__declspec\(dllexport\))
    elseif(CMAKE_CXX_PLATFORM_ID MATCHES "Linux")
        add_compile_definitions(PROTO_EXPORT=__attribute__\(\(visibility\("default"\)\)\))
    endif()
    target_link_libraries(${PROJECT_NAME} ${Protobuf_LIBRARIES})
endmacro()

其他工程链接 address.dll 也需要设置为导入宏

1
2
3
4
5
6
# 其他工程链接时设置为导入宏,Windows 导入导出不一样
if(CMAKE_CXX_PLATFORM_ID MATCHES "Windows")
    add_compile_definitions(PROTO_EXPORT=__declspec\(dllimport\))
elseif(CMAKE_CXX_PLATFORM_ID MATCHES "Linux")
    add_compile_definitions(PROTO_EXPORT=__attribute__\(\(visibility\("default"\)\)\))
endif()

参考

本文由作者按照 CC BY 4.0 进行授权