1. 背景

按装好protobuf之后,我们简单使用它。

在我的test/protobuf目录下,我创建了main.cc、test.proto,在main.cc和test.proto各写了简单内容(我将例子放在后面了)之后,使用命令protoc对test.proto进行编译:

protoc test.proto --cpp.out=./

执行这条命令之后,会在当前目录下生成test.pb.h和test.pb.cc,test/protobuf/整个目录结构如下:

.
├── main.cc
├── test.pb.cc
├── test.pb.h
└── test.proto

重点来了,我想要编译main.cc,生成可执行程序,我执行如下命令:

g++ main.cc test.pb.cc -o main -lprotobuf

上面的命令中,值得说一下的就是-lprotobuf,它就是说,要使用protobuf的库,名为protobuf的动态库或者静态库,出问题的也是因为这里,我暂且不表,后续会说如何解决。

错误如下:

/usr/bin/ld: /tmp/ccguO4TS.o: in function `google::protobuf::internal::ArenaStringPtr::Set(char const*, google::protobuf::Arena*)':
main.cc:(.text._ZN6google8protobuf8internal14ArenaStringPtr3SetEPKcPNS0_5ArenaE[_ZN6google8protobuf8internal14ArenaStringPtr3SetEPKcPNS0_5ArenaE]+0x5e): undefined reference to `google::protobuf::internal::ArenaStringPtr::Set(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, google::protobuf::Arena*)'
/usr/bin/ld: /tmp/ccZ5dEAo.o: in function `fixbug::ResultCode::ResultCode(fixbug::ResultCode const&)':
test.pb.cc:(.text+0x124): undefined reference to `google::protobuf::internal::ArenaStringPtr::Set(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, google::protobuf::Arena*)'
/usr/bin/ld: /tmp/ccZ5dEAo.o: in function `fixbug::ResultCode::Clear()':
test.pb.cc:(.text+0x24b): undefined reference to `google::protobuf::internal::ArenaStringPtr::ClearToEmpty()'
/usr/bin/ld: /tmp/ccZ5dEAo.o: in function `fixbug::ResultCode::ByteSizeLong() const':
test.pb.cc:(.text+0x5df): undefined reference to `google::protobuf::Message::MaybeComputeUnknownFieldsSize(unsigned long, google::protobuf::internal::CachedSize*) const'
/usr/bin/ld: /tmp/ccZ5dEAo.o: in function `fixbug::ResultCode::MergeFrom(fixbug::ResultCode const&)':
test.pb.cc:(.text+0x711): undefined reference to `google::protobuf::internal::ArenaStringPtr::Set(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, google::protobuf::Arena*)'
..........
/usr/bin/ld: /tmp/ccZ5dEAo.o:(.data.rel.ro+0x10): undefined reference to `google::protobuf::Message::CopyWithSizeCheck(google::protobuf::Message*, google::protobuf::Message const&)'
/usr/bin/ld: /tmp/ccZ5dEAo.o:(.data.rel.ro+0x20): undefined reference to `google::protobuf::Message::CopyWithSizeCheck(google::protobuf::Message*, google::protobuf::Message const&)'
/usr/bin/ld: /tmp/ccZ5dEAo.o: in function `fixbug::LoginRequest::SharedDtor()':
test.pb.cc:(.text._ZN6fixbug12LoginRequest10SharedDtorEv[_ZN6fixbug12LoginRequest10SharedDtorEv]+0x9e): undefined reference to `google::protobuf::internal::ArenaStringPtr::Destroy()'
/usr/bin/ld: test.pb.cc:(.text._ZN6fixbug12LoginRequest10SharedDtorEv[_ZN6fixbug12LoginRequest10SharedDtorEv]+0xae): undefined reference to `google::protobuf::internal::ArenaStringPtr::Destroy()'
collect2: error: ld returned 1 exit status

粗鲁看了一下,上面的错误信息,发现是编译test.proto生成的test.pb.cc中的一些函数没有办法找到protobuf中的定义。换句话说,在我们编译main.cc的时候,没有提供给编译器protobuf的动态库或者静态库的位置,导致-lprotobuf无法找到protobuf的动态库和静态库。

2.寻找protobuf库

我们使用locate命令:

locate libprotobuf

如果之前成功安装protobuf,那么会出现它的库所在目录:

/usr/local/lib/libprotobuf-lite.a
/usr/local/lib/libprotobuf-lite.la
/usr/local/lib/libprotobuf-lite.so
/usr/local/lib/libprotobuf-lite.so.31
/usr/local/lib/libprotobuf-lite.so.31.0.2
/usr/local/lib/libprotobuf.a
/usr/local/lib/libprotobuf.la
/usr/local/lib/libprotobuf.so
/usr/local/lib/libprotobuf.so.31
/usr/local/lib/libprotobuf.so.31.0.2

我们发现动态库和静态库在/usr/local/lib目录下。

3. 重新执行失败命令

我们重新执行失败命令,在原来基础上,添加库的定位信息,使用-L操作,具体如下:

g++ main.cc test.pb.cc -o main -L/usr/local/lib/ -lprotobuf

-L表示库所在目录;

-l表示使用的库名称,会自动忽略lib前缀、.a后缀、.so后缀等,比如说有libxxx.a和libxxx.so库,那么我们只需要使用xxx名字即可,正如这个例子所使用的protobuf一样。

编译成功:

.
├── main
├── main.cc
├── test.pb.cc
├── test.pb.h
└── test.proto

上面的main就是可执行程序,执行效果如下:

lijizhi 123456789
lijizhi
123456789

4. 代码附录

main.cc

#include "test.pb.h"
#include <iostream>
#include <string>

using namespace fixbug;

int main()
{
    LoginRequest req;
    req.set_name("lujizhi");//用户名
    req.set_pwd("123456789");//密码:123456789

    //将req数据序列化
    std::string send_str;
    if (req.SerializeToString(&send_str))
    {
        std::cout << send_str.c_str() << std::endl;
    }

    //将req数据反序列化
    LoginRequest reqA;
    if (reqA.ParseFromString(send_str))
    {
        std::cout << reqA.name() << std::endl;
        std::cout << reqA.pwd() << std::endl;
    }

    return 0;
}

test.proto

syntax = "proto3";//设置protobuf版本

//声明代码所在包,相当于C++的namespace,在引入由这份文件生成的.cc文件和.h文件时,
//需要using namespace fixbug
package fixbug;

//message相当于C++中的类或结构体
message ResultCode//封装失败消息类
{
    int32 errcode = 1;//表示第1字段
    bytes errmsg = 2;//表示第2字段,protobuf中bytes相当于C++中的string
}

//定义登录请求
message LoginRequest
{
    bytes name = 1;//表示第1字段
    bytes pwd = 2;//表示第2字段
}

//定义响应登录请求
message LoginResponse
{
    ResultCode result = 1;//表示第1字段
    bool success = 2;//表示第2字段
}