C/C++ Linux protobuf2 简单用法记录

本文介绍了protobuf2的安装步骤,包括下载、编译和安装。接着讲解如何编写.proto文件,包括message结构、required、optional和repeated属性。然后阐述了如何编译.proto文件生成C++代码,并展示了代码示例,包括设置和获取属性值、序列化和反序列化。最后,文章展示了protobuf与libevent库结合使用,创建了一个简单的客户端和服务端通信的例子。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简单记录以下protobuf2的用法,以后忘记了可以回顾一下!(这是谷歌的一个库)


一、protobuf安装

mirrors / google / protobuf · GitCodeProtocol Buffers - Google's data interchange format 🚀 Github 镜像仓库 🚀 源项目地址 ⬇...https://gitcode.net/mirrors/google/protobuf?utm_source=csdn_github_accelerator点击上面的连接去下载!

然后执行命令安装必要条件:

apt-get install autoconf automake libtool curl make g++ unzip 

centos系统将apt-get 改为 yum

依次执行以下命令安装:(进入root用户) 

unzip protobuf-master.zip

cd protobuf-master/

./autogen.sh

./configure

make

make check

make install

ldconfig

头文件所在路径:/usr/local/include/

库所在路径:/usr/local/lib

 安装完毕!


二、编写.proto文件

编写按照如下格式去编写:

message 名字
{
    required 类型 变量名   = 1;   
    required 类型 变量名  = 2;   
    optional 类型 变量名  = 3;   
}

message是固定的,开头需要写上;

required是属性,一共有三种属性,分别是,required、optional、repeated

  • required:表示该值是必须要设置的;
  • optional:消息格式中该字段可以有0个或1个值(不超过1个);即可以不用设置它;
  • repeated:在一个格式良好的消息中,该值可以被设置多个值;

也就是说,设置了required,就必须给他设置值;

repeated int32 code= 4 [packed=true];        // 定义时可以这样,效率会高点

设置了optional,可以不用给他设置值,但要设置默认值,如下:

optional std::string data = 3 [default = 10];

设置了repeated,(可以说是枚举)也要给他设置值,且它可以被设置多个值;

类型可以是以下:(在其他博客截图的)

后面赋值 1, 2, 3,根据顺序赋值,从1开始,自增赋值即可!

如下编写案例:

ptb.proto

syntax = "proto2";

package tutorial;



message response
{
    required int32 code   = 1;   
    required int32 icode  = 2;   
    optional string data  = 3;   
}



message list_account_records_response
{
    required int32   code   = 1;    
    optional string  desc   = 2;    
    message account_record
    {
        required int32  type      = 1; 
        required int32  limit     = 2; 
        required uint64 timestamp = 3; 
    }

    repeated account_record records = 3;
}

开头一定要写上:

syntax = "proto2";        // 用的是protobuf2,所以这里写proto2

package tutorial;        // 这个是命名空间


三、编译.proto文件

编译语法:

protoc -I=$SRC_DIR --cpp_out=$DST_DIR  ptb.proto

SRC_DIR 表示proto文件所在的目录,cpp_out指定了生成的代码的路径, ptb.proto指proto文件名。

protoc -I=./ --cpp_out=./ ptb.proto

这样在当前目录生成了ptb.pb.cc和ptb.pb.h两个文件。


四、代码使用

接下来使用代码去操作。

set_变量名();        // 设置属性值

变量名();                // 获取值

SerializeToString();        // 将类格式化为stirng类型字符处

 ParseFromString();        // 将字符串转换为类

 add_变量名();                // 生成一个repeated 对象返回

编译命令:g++ -std=c++11 test.cc ptb.pb.cc -lprotobuf

test.cc 

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


using namespace std;
using namespace tutorial;


int main(void) {
    
    /* 1、 */
    {
        std::string data;    // 存储序列化的消息
       
        // 模拟客户端发送请求
        {
            response res;
            res.set_code(200);
            res.set_icode(123);
            res.set_data("字符串");
        
            // 将类格式化为stirng类型字符处
            res.SerializeToString(&data);
        
            // 客户端将data发送给服务器
        }
        
        // 模拟服务器接受请求
        {
            response res;
        
            // 将字符串转换为
            res.ParseFromString(data);
        
            std::cout << "code = " << res.code() << std::endl;
            std::cout << "icode = " << res.icode() << std::endl;
            std::cout << "data = " << res.data() << std::endl;
        }   
    }
    
    printf("-------------华丽的分隔符-------------\n");
    
    /* 2、 */
    {
        std::string data;    // 存储序列化的消息

        // 模拟客户端发送请求
        {
            list_account_records_response larr;
            
            larr.set_code(200);
            larr.set_desc("ok");

            for (int i = 0; i < 2; i++) {
                // 分配一个对象
                list_account_records_response_account_record *ar = larr.add_records();
                ar->set_type(i);
                ar->set_limit(i * 100);
                ar->set_timestamp(time(NULL));
            }

            // 输出records的个数
            printf("client:recoreds size : %d\n", larr.records_size());

            // 将类格式化为stirng类型字符处
            larr.SerializeToString(&data);

            // 客户端将data发送给服务器
        }

        // 模拟服务器接受请求
        {
            list_account_records_response larr;

            larr.ParseFromString(data);

            // 输出records的个数
            printf("server:recoreds size : %d\n", larr.records_size());
            
            printf("code: %d\n", larr.code());
            printf("desc: %s\n", larr.desc().c_str());

            for (int i = 0; i < 2; i++) {
                const list_account_records_response_account_record &ar = larr.records(i);
                printf("type: %d\n", ar.type());
                printf("limit: %d\n", ar.limit());
                printf("timestamp: %lu\n", ar.timestamp());
            }
        }
    }

    return 0;
}


五、protobuf与libevent结合使用

client.cc

g++ -std=c++11 client.cc ptb.pb.cc -lprotobuf -levent -o client.exe 

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <event.h>
#include <event2/util.h>

#include "ptb.pb.h"
#include <string>
#include <time.h>



using namespace std;
using namespace tutorial;

#define COUNT 3

int connect_server(const char *server_ip, int port);
void cmd_read_data(int fd, short events, void *arg);
void socket_read_data(int fd, short events, void *arg);


int main(int argc, char **argv) {
    
    if (argc < 3) {
        printf("please input 2 parameters!\n");
        return -1;
    }
    
    // 两个参数依次是服务器的IP地址和端口号
    int sockfd = connect_server(argv[1], atoi(argv[2]));
    if (-1 == sockfd) {
        perror("tcp_connect error!");
        return -1;
    }
    
    printf("connect to server successfully\n");
    
    
    struct event_base *base = event_base_new();
    
    // 监听服务端发送的消息
    struct event *ev_sockfd = event_new(base, sockfd, EV_READ | EV_PERSIST, socket_read_data, NULL);
    event_add(ev_sockfd, NULL);
    
    // 监听终端输入事件
    struct event *ev_cmd = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST, cmd_read_data, (void *)&sockfd);
    event_add(ev_cmd, NULL);
    
    // 事件循环
    event_base_dispatch(base);
    
    event_base_free(base);
    
    printf("finished\n");
    
    return 0;
}


void cmd_read_data(int fd, short events, void *arg) {
    char msg[1024] = { '\0' };
    std::string data = "";
    
    int ret = read(fd, msg, sizeof(msg) - 1);
    if (0 == ret) {
        printf("connection close. exit!\n");
        exit(1);
    }
    if (ret < 0) {
        perror("read failed!");
        exit (1);
    }
    
    int sockfd = *((int *)arg);
    
    if (msg[ret - 1] == '\n') {
        msg[ret - 1] = '\0';
    } else {
        msg[ret] = '\0';
    }
    

    {
        list_account_records_response larr;
        
        larr.set_code(200);
        larr.set_desc(msg);

        for (int i = 0; i < COUNT; i++) {
            // 分配一个对象
            list_account_records_response_account_record *ar = larr.add_records();
            ar->set_type(i + 1);
            ar->set_limit((i+1)*10);
            ar->set_timestamp(time(NULL));
        }
        
       //printf("recoreds size: %d\n", larr.records_size());

        // 将类格式化为stiring类型字符串
        larr.SerializeToString(&data);
    }


    // 把终端的消息发送给服务器端,客户端忽略性能考虑,直接利用阻塞方式发送
    //printf("write to server >>> %s\n", msg);
    ret = write(sockfd, data.c_str(), data.length());
    if (ret == -1) {
        perror("write to server failed!");
        exit(1);
    }
    //printf("ret  =  %d\n", ret);
    
    if (strncmp(msg, "exit", 4) == 0) {   
        memset(msg, 0, sizeof(msg));
        write(sockfd, msg, sizeof(msg));
        usleep(100000); // 100ms
        close(sockfd);
        exit(1);
    }

}


void socket_read_data(int fd, short events, void *arg) {
    char msg[1024] = { '\0' };
    
    // 不考虑一次读不完数据的情况
    int len = read(fd, msg, sizeof(msg) - 1);
    if (0 == len) {
        printf("connection close. exit!\n");
        exit(1);
    } else if (len < 0) {
        perror("read failed!");
        return ;
    }
    
    msg[len] = '\0';
   
    std::string data = msg;
    list_account_records_response larr;

    // 字符串转化为对象
    larr.ParseFromString(data);

    printf("code: %d\n", larr.code());
    printf("desc: %s\n", larr.desc().c_str());

    for (int i = 0; i < COUNT; i++) {
        const list_account_records_response_account_record &ar = larr.records(i);
        printf("type: %d\n", ar.type());
        printf("limit: %d\n", ar.limit());
        printf("time: %lu\n", ar.timestamp());
    }
 
    //printf("recv from server <<< %s\n", msg);
}


typedef struct sockaddr SA;
int connect_server(const char *server_ip, int port) {
    int sockfd, status, save_errno;
    struct sockaddr_in server_addr;
    
    memset(&server_addr, 0, sizeof(server_addr));
    
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    status = inet_aton(server_ip, &server_addr.sin_addr);
    
    if (0 == status) {
        errno = EINVAL;
        return -1;
    }
    
    sockfd = socket(PF_INET, SOCK_STREAM, 0);
    
    status = connect(sockfd, (SA *)&server_addr, sizeof(server_addr));
    if (-1 == status) {
        save_errno = errno;
        close(sockfd);
        errno = save_errno;     // the close may be error
        return -1;
    }
    
    return sockfd;
}

  

server.cc

g++ -std=c++11 server.cc ptb.pb.cc -lprotobuf -levent -o server.exe 

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <event.h>
#include <event2/event.h>
#include <event2/listener.h>
#include <assert.h>
#include <arpa/inet.h>

#include <string>
#include <time.h>

#include "ptb.pb.h"


using namespace std;
using namespace tutorial;


#define COUNT	3
#define BUFLEN  1024


typedef struct _ConnectStat {
    struct bufferevent *bev;
    char buf[BUFLEN];
}ConnectStat;


ConnectStat *stat_init(struct bufferevent *bev);
void do_echo_request(struct bufferevent *bev, void *arg);           // 读数据
void do_echo_response(struct bufferevent *bev, void *arg);          // 写数据
void event_cb(struct bufferevent *bev, short event, void *arg);     // 出错处理函数
int tcp_server_init(int port, int listen_num);
void listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *sock, int socklen, void *arg);   // 监听函数



struct event_base *base;

int main(int argc, char **argv) {
 
    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(struct sockaddr_in));
    
    sin.sin_family = AF_INET;
    sin.sin_port = htons(9999);
    //server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    base = event_base_new();
    
    
    // 创建socket,绑定、监听、接受链接
    // 创建监听对象,在指定的地址上监听接下来的TCP连接
    // listen、connect、bind、accept;  LEV_OPT_REUSEABLE:可重用,LEV_OPT_CLOSE_ON_FREE:自动关闭
    struct evconnlistener *listener = evconnlistener_new_bind(base, listener_cb, base,
                                                             LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE,
                                                             1024, (struct sockaddr *)&sin,
                                                             sizeof(struct sockaddr_in));
                                                             
    // 监听集合中的事件
    event_base_dispatch(base);
    
    // 释放
    evconnlistener_free(listener);
    event_base_free(base);
    
    return 0;
}


ConnectStat *stat_init(struct bufferevent *bev) {
    ConnectStat *temp = NULL;
    temp = (ConnectStat *)malloc(sizeof(ConnectStat));
    
    if (!temp) {
        fprintf(stderr, "malloc failed. reason: %s\n", strerror(errno));
        return NULL;
    }
    
    memset(temp, '\0', sizeof(ConnectStat));
    temp->bev = bev;
    
    return temp;
}


void do_echo_request(struct bufferevent *bev, void *arg) {
    ConnectStat *stat = (ConnectStat *)arg;
    char *msg = stat->buf;
    std::string data = "";
    
    // 从缓冲区中获取数据
    size_t len = bufferevent_read(bev, msg, BUFLEN);
    if (0 == len) {
        return;
    }
    msg[len] = '\0';
    data = msg;

    {
        list_account_records_response larr;

        // 将字符串转换
        larr.ParseFromString(data);
//printf("recoreds size: %d\n", larr.records_size());

        // 输出接收到的数据
        printf("code: %d\n", larr.code());
        printf("desc: %s\n", larr.desc().c_str());

        for (int i = 0; i < COUNT; i++) {
            const list_account_records_response_account_record &ar = larr.records(i); 
            printf("type: %d\n", ar.type());
            printf("limit: %d\n", ar.limit());
            printf("time: %lu\n", ar.timestamp());
        }
        
        larr.set_desc(larr.desc() + "123");
    }


    //printf("recv from client <<< %s\n", msg);
    
    // 将数据添加到缓冲区
    bufferevent_write(bev, msg, strlen(msg));
}


void do_echo_response(struct bufferevent *bev, void *arg) {
    return ;
}

void event_cb(struct bufferevent *bev, short event, void *arg) {
    ConnectStat *stat = (ConnectStat *)arg;
    
    if (event & BEV_EVENT_EOF) {
        printf("connect cloase\n");
    } else if (event & BEV_EVENT_ERROR) {
        printf("some other error\n");
    }
    
    // 自动close套接字和free读写缓冲区
    bufferevent_free(bev);// 释放bufferevent对象
    free(stat);
}


typedef struct sockaddr SA;
int tcp_server_init(int port, int listen_num) {
    int errno_save;
    evutil_socket_t listener;   // int listener
    
    listener = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == listener) {
        return -1;
    }
    
    // 允许多次绑定同一个地址,要用在socket和bind之间
    evutil_make_listen_socket_reuseable(listener);
    
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = 0;
    sin.sin_port = htons(port);
    
    if (bind(listener, (SA *)&sin, sizeof(sin)) < 0) {
        errno_save = errno;
        evutil_closesocket(listener);
        errno = errno_save;
        
        return -1;
    }
    
    if (listen(listener, listen_num) < 0) {
        errno_save = errno;
        evutil_closesocket(listener);
        errno = errno_save;
        
        return -1;
    }
    
    // 跨平台统一接口,将套接字设置为非阻塞状态
    evutil_make_socket_nonblocking(listener);
    
    return listener;
}


// 一个客户端连接上服务器此函数就会被调用;当此函数被调用时,libevent已经帮我们accept了这个客户端
void listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *sock, int socklen, void *arg) {
    printf("accept a client %d \n", fd);
    
    struct event_base *base = (struct event_base *)arg;
    
    // 针对已经存在的socket创建bufferevent对象
    // BEV_OPT_CLOSE_ON_FREE:如果释放bufferevent对象,则关闭连接
    struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
    // BEV_OPT_CLOSE_ON_FREE:释放bufferevent时关闭底层传输端口。这将关闭底层套接字,释放底层bufferevent等。
    
    ConnectStat *stat = stat_init(bev);
    
    // 给bufferevent设置回调函数
    // bufferevent对象、读事件回调函数、写事件回调函数、其他事件回调函数、参数
    bufferevent_setcb(bev, do_echo_request, do_echo_response, event_cb, stat);  // evnet_set
    
    bufferevent_enable(bev, EV_READ | EV_PERSIST);  // evnet_add,使bufferevent 生效
}


六、总结

简单用法介绍完毕,现阶段学习中,我是这样去使用的;日后如果接触的项目有用到再来更新。 

<think>首先,用户的问题是关于CMake错误:"CMake Error protobuf-targets.cmake 42 message"。用户提到了引用,这些引用是关于protobuf和CMake版本不兼容、编译protobuf时的输出,以及protobufC++项目中的使用。用户意图是找到这个错误的原因和解决方法。 我需要回顾系统级指令: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 用户的问题具体是:"CMake Error protobuf-targets.cmake 42 message 解决方法"。这看起来是一个CMake错误消息,可能是在使用protobuf时发生的。错误消息提到"protobuf-targets.cmake",这通常是CMake配置文件的一部分。 从引用中: - 引用[1]显示CMake找到了protobuf,但可能有问题。 - 引用[2]提到protobuf和cmake版本不兼容,并提供了下载链接。 - 引用[3]讨论了protobufC++项目中的使用,包括跨文件夹导入。 错误消息"protobuf-targets.cmake 42 message"可能指的是在protobuf-targets.cmake文件的第42行有一个错误消息。我需要推断常见的错误原因。 常见原因: 1. **版本不兼容**:protobuf和CMake版本可能不匹配。引用[2]直接提到了这个。 2. **路径问题**:protobuf库或头文件路径不正确。 3. **配置错误**:在CMakeLists.txt中配置protobuf时有错误。 4. **缺少依赖**:可能缺少pthread或其他依赖。 在回答中,我需要: - 结构清晰:逐步解释问题原因和解决方法。 - 使用中文。 - 在引用的段落末尾添加引用标识。 - 生成相关问题。 步骤: 1. **理解错误**:解释错误可能的原因。 2. **提供解决方法**:基于常见解决方案。 3. **参考引用**:自然地引用提供的材料。 4. **生成相关问题**:在最后添加相关问题。 可能的解决方法: - 检查protobuf和CMake版本。 - 确保protobuf正确安装。 - 更新CMakeLists.txt以正确引用protobuf。 - 使用find_package正确处理。 从引用[2],它建议更新CMake版本。 回答结构: 1. **介绍问题**:简述错误。 2. **分析原因**:解释可能原因。 3. **解决方法**:一步步给出解决方案。 4. **验证**:如何测试是否解决。 5. **相关问题**:生成相关问题。 生成相关问题: - 例如:如何检查protobuf版本?如何安装特定版本的CMake?protobuf-targets.cmake是什么? 确保回答真实可靠:基于标准CMake和protobuf知识。 最终回答框架: - 开头:解释错误。 - 主体:逐步解决方法。 - 结尾:相关问题。</think>### CMake Error: protobuf-targets.cmake 第42行错误解决方法 您遇到的错误消息 `CMake Error protobuf-targets.cmake 42 message` 通常表示在 CMake 配置过程中,protobuf 的配置文件(`protobuf-targets.cmake`)在第42行出现了问题。这可能是由于 protobuf 与 CMake 版本不兼容、路径配置错误或依赖缺失导致的。以下我将逐步解释原因并提供解决方法,帮助您快速修复问题。本文基于您提供的引用信息和常见 CMake 配置经验,确保方案真实可靠。 #### 错误原因分析 - **版本不兼容**:protobuf 和 CMake 的版本不匹配是常见原因。protobuf 3.17.x 及以上版本可能需要 CMake 3.21.x 或更高版本支持,否则在加载 `protobuf-targets.cmake` 时会触发错误(如第42行可能涉及变量解析失败)[^2]。 - **路径配置问题**:CMake 未能正确找到 protobuf 的头文件或库文件路径。例如,您的引用中显示 `Protobuf_INCLUDE_DIRS` 和 `PROTOBUF_LIBRARIES` 被设置为系统路径(如 `/usr/local/include` 和 `/usr/local/lib/libprotobuf.so`),但如果这些路径无效或权限不足,CMake 会在配置文件中报错[^1]。 - **依赖缺失**:protobuf 依赖 pthread 等库,如果 CMake 未正确链接,会导致配置文件执行失败[^1]。 - **跨文件夹导入问题**:在大型项目中(如 Apollo 7.0.0),跨文件夹导入 `.proto` 文件时,CMakeLists.txt 配置不当可能引发此错误[^3]。 #### 逐步解决方法 请按顺序尝试以下方案,每个步骤后运行 `cmake ..` 或 `make` 验证是否解决。**优先推荐方式一和二**,因为它们针对版本和路径问题。 1. **更新 CMake 和 protobuf 版本(解决版本不兼容)** - 检查当前版本: ```bash cmake --version # 查看 CMake 版本 protoc --version # 查看 protobuf 版本 ``` - 如果 CMake 版本低于 3.21.x(如 3.17.x),请升级: - 下载最新 CMake(如 3.21.1): ```bash wget https://github.com/Kitware/CMake/releases/download/v3.21.1/cmake-3.21.1-linux-x86_64.sh sh cmake-3.21.1-linux-x86_64.sh --prefix=/usr/local --exclude-subdir ``` - 验证升级:`cmake --version` 应输出 `3.21.1` 或更高。 - 确保 protobuf 版本匹配(推荐 3.17.x 或更高): - 如果版本过低,重新安装: ```bash sudo apt remove libprotobuf-dev protobuf-compiler # 卸载旧版 sudo apt install libprotobuf-dev protobuf-compiler # 安装新版 ``` - **原因**:版本升级能修复配置文件中的兼容性错误,避免第42行变量解析失败[^2]。 2. **修正 CMakeLists.txt 配置(解决路径问题)** - 在您的 CMakeLists.txt 中,确保正确使用 `find_package` 和路径设置。参考您的引用,添加以下代码: ```cmake find_package(Protobuf REQUIRED) include_directories(${Protobuf_INCLUDE_DIRS}) # 显式包含头文件路径 link_directories(${Protobuf_LIBRARY_DIRS}) # 链接库文件路径 target_link_libraries(your_target_name ${Protobuf_LIBRARIES} pthread) # 添加 pthread 依赖 ``` - 如果 protobuf 安装在非标准路径(如自定义目录),设置环境变量: ```bash export Protobuf_ROOT=/your/custom/protobuf/path # 替换为实际路径 ``` 然后在 CMakeLists.txt 中添加: ```cmake set(Protobuf_DIR "${Protobuf_ROOT}/lib/cmake/protobuf") find_package(Protobuf REQUIRED) ``` - **原因**:这确保 CMake 正确加载 `protobuf-targets.cmake`,避免第42行因路径缺失导致的错误[^1]。 3. **处理跨文件夹导入(针对大型项目)** - 如果项目涉及多文件夹(如 Apollo 风格),在根 CMakeLists.txt 中统一生成 protobuf 文件: ```cmake file(GLOB PROTO_FILES "path/to/proto/*.proto" "subfolder/*.proto") # 包含所有子文件夹 protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${PROTO_FILES}) add_executable(your_target_name ${PROTO_SRCS} other_sources.cpp) target_link_libraries(your_target_name ${Protobuf_LIBRARIES}) ``` - **原因**:跨文件夹导入时,`.proto` 文件路径错误会导致 `protobuf-targets.cmake` 解析失败,此方法能统一管理依赖[^3]。 4. **清理和重建(通用修复)** - 删除构建目录并重新生成: ```bash rm -rf build # 删除旧构建 mkdir build && cd build cmake .. # 重新配置 make # 编译 ``` - 检查权限:确保 `/usr/local/lib` 等路径有读写权限(`sudo chmod -R 755 /usr/local`)。 #### 验证是否解决 运行 `cmake ..` 后,查看输出是否包含成功消息: ``` -- Found Protobuf: /usr/local/lib/libprotobuf.so (found version "3.17.3") ``` 如果错误消失,项目应能正常编译。 #### 注意事项 - 如果问题持续,检查 `protobuf-targets.cmake` 第42行的具体内容(使用文本编辑器打开),可能涉及特定变量错误,如 `message()` 调用失败。分享该行内容可进一步诊断。 - 引用中提到的版本冲突是常见根源,优先升级 CMake[^2]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cpp_learners

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值