Protobuf:复杂类型接口
Protobuf:复杂类型接口
- package
- 字段规则
- 复杂类型
- enum
- Any
- oneof
- map
本博客基于proto3
语法,讲解protobuf
中的复杂类型。
package
在.proto
文件中,支持导入其它.proto
文件的内容,例如:
test.proto
:
syntax = "proto3";
package test_pkg;import "other.proto";message Person {int32 id =1;string name = 2;other_pkg.Address addr = 3;
}
other.proto
:
syntax="proto3";
package other_pkg;message Address {string country = 1;string city = 2;
}
在test.proto
文件中,使用了other.proto
的内容。想要实现多个文件的效果,需要通过improt
导入文件:
import 文件名.proto
随后在导入的文件中,使用包名.变量名
的形式使用变量,比如other_pkg.Address
。
字段规则
如果想在protobuf
中实现一个数组,它使用了一种字段规则的修饰方式来实现数组。
消息字段类型可以用以下规则修饰:
singular
:消息中该字段可以出现0
次或1
次,字段默认使用该规则repeated
:消息中该字段可以出现任意次数,包括0
次
简单来说,对于singular
变量只能存储一个值,如果输入新的值,后来的值会把原先的值覆盖。而对于repeated
变量,其可以存储多个值,所以值都会被保留,其实也就是数组。
示例:
syntax = "proto3";
package test_pkg;message Person {int32 id = 1;string name = 2;repeated string phone = 3;
}
此处的phone
字段被设置为了repeated
,可以理解为一个string
的数组,在pb.h
文件中,该字段包含以下接口:
// repeated string phone = 3;
int phone_size() const;
private:
int _internal_phone_size() const;
public:
void clear_phone();
const std::string& phone(int index) const;
std::string* mutable_phone(int index);
void set_phone(int index, const std::string& value);
void set_phone(int index, std::string&& value);
void set_phone(int index, const char* value);
void set_phone(int index, const char* value, size_t size);
std::string* add_phone();
void add_phone(const std::string& value);
void add_phone(std::string&& value);
void add_phone(const char* value);
void add_phone(const char* value, size_t size);
可以看到,相比于一般的string
类型,被repeated
修饰后,多出了很多下标操作,这里挑几个接口出来讲解:
int phone_size() const
:获取当前数组的长度。const std::string& phone(int index) const
:通过下标获取值的操作,获取该变量的const
引用。std::string* mutable_phone(int index)
:获取指定下标元素的指针,可以通过指针修改该元素。
void set_phone(int index, const std::string& value);
void set_phone(int index, std::string&& value);
void set_phone(int index, const char* value);
void set_phone(int index, const char* value, size_t size);
这四个接口用于设置指定下标的值,四种函数重载效果是一样的,只是传入字符串的形式不同。
std::string* add_phone()
:数组末尾增加一个空元素,并返回指向该元素的指针,可以通过指针修改该元素。
void add_phone(const std::string& value);
void add_phone(std::string&& value);
void add_phone(const char* value);
void add_phone(const char* value, size_t size);
这四个接口直接插入一个指定元素到数组末尾,重载是为了兼容不同形式的字符串。
被repeat
修饰的变量接口总结:
接口 | 功能 |
---|---|
xxx_size() | 获取数组的长度 |
xxx(index) | 获取指定下标的元素,不可修改 |
muteble_xxx(index) | 获取指定下标元素的指针,可修改 |
set_xxx(index, value) | 设置指定下标的元素为value |
add_xxx() | 尾插一个元素到数组,返回指向该元素的指针,可修改 |
add_xxx(value) | 尾插value 到数组 |
复杂类型
enum
enum
是枚举类型,其列举多个常量,方便后续使用。
定义一个枚举,表示有哪些人物种类:
enum Type{STU = 0;TEACHER = 1;OTHER = 2;
}
此处枚举了三个常量,分别表示学生,老师,其它。
枚举类型有以下要求:
0
值必须存在,且要作为第一个元素- 在语言中,定义枚举变量后,如果指定变量值,第一个元素
0
是默认值 - 枚举的常量值在
32
位整数内,但是负数无效
示例:
syntax = "proto3";
package test_pkg;enum Type{STU = 0;TEACHER = 1;OTHER = 2;
}message Person {int32 id = 1;string name = 2;repeated string phone = 3;Type type = 4;
}
编译后,在.pb.h
文件可以找到以下枚举类型:
enum Type : int {STU = 0,TEACHER = 1,OTHER = 2,Type_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(),Type_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max()
};
此处使用了C++11
的强枚举类型语法,enum Type : int
指定底层类型为int
。除去在.proto
文件中指定的三个成员,还增加了两个其它成员,最后两个成员用于做边界值判断,限制成员的值只在int32
范围内。
紧接着的是部分枚举类型的专有接口:
bool Type_IsValid(int value);
constexpr Type Type_MIN = STU;
constexpr Type Type_MAX = OTHER;
constexpr int Type_ARRAYSIZE = Type_MAX + 1;const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* Type_descriptor();
template<typename T>
inline const std::string& Type_Name(T enum_t_value)
inline bool Type_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name, Type* value);
Type_IsValid
:判断整数是否是enum
内部的成员之一Type_Name
:输入枚举值,返回枚举值对应的字符串名称Type_parse
:输入字符串和枚举值,判断两者是否匹配
再往后,在Person
类中,定义了枚举的通用接口:
// .test_pkg.Type type = 4;
void clear_type();
::test_pkg::Type type() const;
void set_type(::test_pkg::Type value);
可以看到,枚举类型的get
和set
接口还是比较简单的。
enum
接口总结:
接口 | 功能 |
---|---|
xxx_IsValid() | 判断整数是否是enum 内部的成员之一 |
xxx_Name(value) | 输入枚举值,返回枚举值对应的字符串名称 |
xxx_Parse(string, value) | 输入字符串和枚举值,判断两者是否匹配 |
xxx() | 获取当前枚举变量的值 |
set_xxx(value) | 设置枚举的值 |
Any
假设现在要给Person
类定义一个info
字段,用于存储该人的具体信息。但是由于Person
可以是学生,可以是老师,这两种人存储的info
是不同的,如何让一个字段存储不同的值,成为一个泛型?
此处Any
类型就排上用场了,Any
可以存储任何其它类型的message
,相当于一个泛型。
syntax = "proto3";
package test_pkg;import "google/protobuf/any.proto";message test{google.protobuf.Any info = 1;
}
Any
的本质是一个message
,写在google/protobuf/any.proto
中,使用Any
类型需要import
导包。在类内使用类型时,也需要指定包名google.protobuf.Any
。
引入泛型后,当前Person
类如下:
syntax = "proto3";
package test_pkg;import "google/protobuf/any.proto";enum Type{STU = 0;TEACHER = 1;OTHER = 2;
}message stuInfo {string className = 1; // 班级int32 score = 2; // 成绩
}message teacherInfo {string subject = 1; // 科目int32 jobAge = 2; // 工龄
}message Person {int32 id = 1;string name = 2;repeated string phone = 3;Type type = 4;google.protobuf.Any info = 5;
}
Person
的info
字段用于存储具体信息,对于学生来说,要存储班级和成绩。而对于老师来说,要存储所教的科目和工龄。此时将这些信息分别定义在stuInfo
和teacherInfo
中,在Person
内引入一个Any
字段info
,这个info
就可以存储任意其它的message
,自然也包括stuInfo
和teacherInfo
。
编译后,在.pb.h
中Any
的接口如下:
// .google.protobuf.Any info = 5;
bool has_info() const;
private:
bool _internal_has_info() const;
public:
void clear_info();
const ::PROTOBUF_NAMESPACE_ID::Any& info() const;
PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::Any* release_info();
::PROTOBUF_NAMESPACE_ID::Any* mutable_info();
void set_allocated_info(::PROTOBUF_NAMESPACE_ID::Any* info);
has_info()
:检查该字段是否为空clear_info()
:清空该字段的值info()
:获取该字段的值release_info()
:获取字段内的值后,清空字段内的值mutable_info()
:返回指向元素的指针,可通过指针修改元素
另外的,还有几个重要的接口,在google/protobuf/any.pb.h
中:
bool PackFrom(const ::PROTOBUF_NAMESPACE_ID::Message& message);bool UnpackTo(::PROTOBUF_NAMESPACE_ID::Message* message);template<typename T>
bool Is();
这三个接口是Any
最核心的接口:
PackFrom
:将一个message
类型转化为Any
类型UnpackTo
:将一个Any
类型转化回message
Is
:传入模板参数,判断当前Any
存储的类型是否和模板参数一样
这个用法有点复杂,示例:
#include <iostream>
#include "test.pb.h"using namespace std;
using namespace test_pkg;int main()
{test_pkg::Person ps;ps.set_id(123456);ps.set_name("张三");// repeat字段,可以设置多个值ps.add_phone("123456");ps.add_phone("654321");ps.set_type(Type::STU); // 学生类型stuInfo stuinfo;stuinfo.set_classname("六年级一班");stuinfo.set_score(95);google::protobuf::Any any_message;if (any_message.PackFrom(stuinfo)) *ps.mutable_info() = any_message;stuInfo getStuinfo;if (ps.info().Is<stuInfo>())ps.info().UnpackTo(&getStuinfo);return 0;
}
首先初始化了一个Person
,随后初始化了该对象的初始信息,确定该对象是一个学生。所以他的info
要存储一个stuInfo
类型,用于表示详细信息。
stuInfo stuinfo;
stuinfo.set_classname("六年级一班");
stuinfo.set_score(95);
这段代码初始化了一个stuinfo
对象。
随后将这个stuInfo
对象设置到ps.info
成员中:
google::protobuf::Any any_message;
if (any_message.PackFrom(stuinfo)) *ps.mutable_info() = any_message;
设置Any
成员时,首先要定义一个Any
对象,然后通过PackFrom
函数,把stuInfo
对象转化为Any
对象。如果PackFrom
返回true
,说明转化成功,此时any_message
内部就已经是stuInfo
的内容了。最后通过*ps.mutable_info() = any_message
设置info
字段为any_message
对象。
如果后续想要把Any
对象变回stuInfo
对象:
stuInfo getStuinfo;
if (ps.info().Is<stuInfo>())ps.info().UnpackTo(&getStuinfo);
定义一个getStuinfo
接收返回值。随后执行ps.info().Is<stuInfo>()
,判断当前的info
内存储的变量类型是不是stuInfo
,如果是,那么执行ps.info().UnpackTo(&getStuinfo)
,将info
的内容解析到getStuinfo
中。
不论用Any
存储或提取任何一个message
类型,都是这一套逻辑。
Any
接口总结:
接口 | 功能 |
---|---|
has_xxx() | 检查该字段是否为空 |
clear_xxx() | 清空该字段的值 |
xxx() | 获取该字段的值 |
release_xxx() | 获取字段内的值后,清空字段内的值 |
mutable_xxx() | 返回指向元素的指针,可通过指针修改元素 |
PackFrom(message) | 将一个message 类型转化为Any 类型 |
UnpackTo(&message) | 将一个Any 类型转化回message |
Is<messageType>() | 传入模板参数,判断当前Any 存储的类型是否和模板参数一样 |
oneof
有时候,message
中的字段在多个中选择一个,就可以使用oneof
类型。
示例:
message Person {int32 id = 1;string name = 2;oneof contact {int32 qq = 3;int32 tel = 4;}
}
此处的Person
,增加了一个联系方式contact
,用户可以在qq
和tel
中二选一。
在oneof
内部的字段编号,不能与外部的message
冲突,比如qq
和tel
的字段编号就不可以是1
和2
。
另外的,在oneof
内部,不允许使用repeated
修饰字段,比如这样:
message Person {int32 id = 1;string name = 2;oneof contact {repeated int32 qq = 3; // errrepeated int32 tel = 4; // err}
}
如果设置了repeated
,该修饰会失效,变量依然只能保存一个值。
对以下protoc
代码编译:
message Person {int32 id = 1;string name = 2;oneof contact {int32 qq = 3;int32 tel = 4;}
}
结果:
// int32 qq = 3;
bool has_qq() const;
private:
bool _internal_has_qq() const;
public:
void clear_qq();
int32_t qq() const;
void set_qq(int32_t value);public:
// int32 tel = 4;
bool has_tel() const;
private:
bool _internal_has_tel() const;
public:
void clear_tel();
int32_t tel() const;
void set_tel(int32_t value);void clear_contact();
看起来这个oneof
好像没有生效?此处就好像是单独定义了qq
和tel
两个字段,也没有什么oneof
的相关方法。
其实这个地方,qq
和tel
确实是直接当作两个字段处理的,但是两个字段有点像C语言
中的联合体,最后一次设置的是那个变量,那么存储的就是哪一个类型。
示例:
test_pkg::Person ps;
ps.set_id(123456);
ps.set_name("张三");
ps.set_qq(111111);
ps.set_tel(222222);if (ps.has_qq())cout << "qq: " << ps.qq() << endl;if (ps.has_tel())cout << "tel: " << ps.tel() << endl;
定义了一个Person
变量后,先后设置了qq(111111)
和tel(222222)
,随后通过has_xxx
方法分别检测两个值,随后输出。
输出结果:
tel: 222222
最后只输出了tel
,因为后来的tel
覆盖了qq
,两者是二选一的关系。
当然,oneof
类型也不是完全没有接口,比如这个接口:
void clear_contact();
在需要清除oneof
内部的值的时候,如果不确定具体是哪一个类型,就无法确定是caler_qq
还是clear_tel
,如果一个个通过has
判断还是有点麻烦的,就可以通过clear_contact
一键清除。
所有的oneof
的字段都被保存在一个枚举中:
enum ContactCase {kQq = 3,kTel = 4,CONTACT_NOT_SET = 0,
};ContactCase contact_case() const;
此处的kQq
和kTel
分别就是之前设置的qq
和tel
,而CONTACT_NOT_SET
表示未设置参数。
其实在判断当前oneof
存储的是哪一个字段时,不用一个个进行has_xxx
的判断。通过contact_case
函数,会返回一个枚举类型,只需要判断枚举类型是哪一个变量即可:
switch (ps.contact_case())
{case Person::ContactCase::kQq:// 处理qq字段break;case Person::ContactCase::kTel:// 处理tel字段break;case Person::ContactCase::CONTACT_NOT_SET:// 没有字段被设置break;
}
oneof
接口总结:
接口 | 功能 |
---|---|
clear_xxx() | 不论字段存储的是什么,清空该字段的值 |
xxx_case() | 返回当前存储的类型的枚举值 |
map
map
用于创建一个键值对的映射关系,语法:
map<key_tyep, value_type> map_name = N;
其实语法和C++
的std::map
是一样的。
map
有以下注意点:
key_type
:可以是处理float
和bytes
之外的任意标量类型value_type
:可以是任意类型map
中的元素是无序的
示例:
message score {map<string, int32> chinese = 1;map<string, int32> english = 2;map<string, int32> math = 3;
}
这是一个记录学生成绩的表格,三个成员分别代表不同科目的成绩。
编译结果以chinese
为例:
// map<string, int32> chinese = 1;
int chinese_size() const;
private:
int _internal_chinese_size() const;
public:
void clear_chinese();
private:
const ::PROTOBUF_NAMESPACE_ID::Map< std::string, int32_t >&_internal_chinese() const;
::PROTOBUF_NAMESPACE_ID::Map< std::string, int32_t >*_internal_mutable_chinese();
public:
const ::PROTOBUF_NAMESPACE_ID::Map< std::string, int32_t >&chinese() const;
::PROTOBUF_NAMESPACE_ID::Map< std::string, int32_t >*mutable_chinese();
chinese_size()
:获取map
内部元素的个数clear_chinese()
:清空map
的元素chinese()
:获取map
的const
引用mutable_chinese()
:获取map
的指针,可通过指针修改map
此处map
的类型不是std::map
,而是protobuf
自己封装的::PROTOBUF_NAMESPACE_ID::Map
,但是其用法和std::map
没有很大区别。
::PROTOBUF_NAMESPACE_ID::Map
也重载了operator[]
,拿到变量后,可以直接通过[]
进行访问。
示例:
test_pkg::Score sc;auto chinese_mp = *sc.mutable_chinese();
auto english_mp = *sc.mutable_english();
auto math_mp = *sc.mutable_math();chinese_mp["张三"] = 66;
chinese_mp["李四"] = 88;
chinese_mp["王五"] = 98;english_mp["张三"] = 97;
english_mp["李四"] = 77;
english_mp["王五"] = 82;math_mp["张三"] = 46;
math_mp["李四"] = 25;
math_mp["王五"] = 79;cout << "张三-chinese: " << chinese_mp["张三"] << endl;
cout << "张三-english: " << english_mp["张三"] << endl;
cout << "张三-math: " << math_mp["张三"] << endl;
通过auto chinese_mp = *sc.mutable_chinese()
,获取到map
,由于mutable_chinese
返回的是一个指针,所以还要进行解引用。
随后通过chinese_mp[]
设置与获取元素了。
输出结果:
张三-chinese: 66
张三-english: 97
张三-math: 46
map
接口总结:
接口 | 功能 |
---|---|
xxx_size() | 获取map 内部元素的个数 |
clear_xxx() | 清空map 的元素 |
xxx() | 获取map 的const 引用 |
mutable_xxx() | 获取map 的指针,可通过指针修改map |
map.operator[] | 直接通过[] 获取与设置元素 |
相关文章:
Protobuf:复杂类型接口
Protobuf:复杂类型接口 package字段规则复杂类型enumAnyoneofmap 本博客基于proto3语法,讲解protobuf中的复杂类型。 package 在.proto文件中,支持导入其它.proto文件的内容,例如: test.proto: syntax …...
Git Push 深度解析:命令的区别与实践
目录 命令一:git push origin <branch-name>命令二:git push Factory_sound_detection_tool test工作流程:两者的主要区别实践中的应用总结 Git 是一种分布式版本控制系统,它允许用户对代码进行版本管理。在 Git 中…...

大数据开发基础实训室设备
大数据实验实训一体机 大数据实验教学一体机是一种专为大数据教育设计的软硬件融合产品,其基于华为机架服务器进行了调优设计,从而提供了卓越的性能和稳定性。这一产品将企业级虚拟化管理系统与实验实训教学信息化平台内置于一体,通过软硬件…...
【数据结构】string(C++模拟实现)
string构造 string::string(const char* str):_size(strlen(str)) {_str new char[_size 1];_capacity _size;strcpy(_str, str); }// s2(s1) string::string(const string& s) {_str new char[s._capacity 1];strcpy(_str, s._str);_size s._size;_capacity s._cap…...

【笔记】I/O总结王道强化视频笔记
文章目录 从中断控制器的角度来理解整个中断处理的过程复习 处理器的中断处理机制**中断驱动I/O方式** printf——从系统调用到I/O控制方式的具体实现1轮询方式下输出一个字符串(程序查询)中断驱动方式下输出一个字符串中断服务程序中断服务程序与设备驱动程序之间的关系 DMA方…...
XML XSLT:转换与呈现数据的力量
XML XSLT:转换与呈现数据的力量 XML(可扩展标记语言)和XSLT(XML样式表转换语言)是现代信息技术中不可或缺的工具,它们在数据交换、存储和呈现方面发挥着重要作用。本文将深入探讨XML和XSLT的概念、应用及其在信息技术领域的重要性。 XML:数据交换的标准 XML是一种用于…...

ES6总结
1.let和const以及与var区别 1.1 作用域 var: 变量提升(Hoisting):var 声明的变量会被提升到其作用域的顶部,但赋值不会提升。这意味着你可以在声明之前引用该变量(但会得到 undefined)。 con…...

晶体匹配测试介绍
一、晶体参数介绍 晶体的电气规格相对比较简单,如下: 我们逐一看看每个参数, FL就是晶体的振动频率,这个晶体是24.576MHz的。 CL就是负载电容,决定了晶体频率是否准确,包括外接的实际电容、芯片的等效电容以及PCB走线的寄生电容等,核心参数。 Frequency Tolerance是…...

超声波清洗机靠谱吗?适合学生党入手的四款眼镜清洗机品牌推荐!
有没有学生党还不知道双十一买什么?其实可以去看看超声波清洗机,说实话它的实用性真的很高,对于日常用于清洗眼镜真的非常合适,不仅可以帮助大家节约时间而且还能把眼镜清洗的干净透亮,接下来我就来为大家带来四款好用…...

Java生成图片_基于Spring AI
Spring AI 优势 过去,使用Java编写AI应用时面临的主要困境是没有统一且标准的封装库,开发者需自行对接各个AI服务提供商的接口,导致代码复杂度高、迁移成本大。如今,Spring AI Alibaba的出现极大地缓解了这一问题,它提…...

程序传入单片机的过程,以Avrdude为例分析
在市场上有各式各样的单片机,例如Arduino,51单片机,STM等。通常,我们都用其对应的IDE软件进行单片机的编程。这些软件既负责将程序代码转写成二进制代码,即机器语言,也负责将该二进制代码导入单片机。与此同…...

用YOLO和LLM增强的OCR
虽然最近我花了很多时间在大型语言模型 (LLM) 上进行实验,但我对计算机视觉的热情始终未减。因此,当我有机会将两者融合在一起时,我迫不及待地想要立即开始。在 Goodreads 上扫描书籍封面并将其标记为已读一直感觉有点神奇,我很兴…...

开源的云平台有哪些?
开源云平台为用户提供了构建、管理和运行云基础设施及应用的能力,同时允许社区参与开发和改进。以下是一些知名的开源云平台: 1. OpenStack 简介:OpenStack:一个广泛使用的开源云平台,它由多个组件组成,提…...

Spring Boot学习资源库:微服务架构的加速器
3 系统分析 3.1可行性分析 在进行可行性分析时,我们通常根据软件工程里方法,通过四个方面来进行分析,分别是技术、经济、操作和法律可行性。因此,在基于对目标系统的基本调查和研究后,对提出的基本方案进行可行性分析。…...
Pi4+wfb-ng+8812au
sudo apt update将如下文件拷入树莓派4 linux-6f921e98008589258f97243fb6658d09750f0a2f.tar.gz libsodium.zip rtl8812au.zip wfb-ng_Pi4_2.zip 安装libsodium unzip libsodium.zip cd libsodium ./configure make && make check sudo make install#安装8812AU驱动 …...

基于单片机的非接触智能测温系统设计
本设计主要由单片机STC8A8K64S4A12、OLED显示屏、非接触式测温模块MLX9061、无线通讯模块ESP8266以及声光报警模块等构成。系统通过非接触式测温模块MLX9061测量当前人员温度,温度通过OLED显示屏进行实时显示,当被测温度高于阈值,声光报警模块…...

第二十三篇:网络拥塞了,TCP/IP如何解决的?
一.显示拥塞通知 当发生网络拥塞时,发送主机应该减少数据包的发送量。作为IP上层协议,TCP虽然也能控制网络拥塞,不过它是通过数据包的实际损坏情况来判断是否发生拥塞。然而这种方法不能在数据包损坏之前减少数据包的发送量。 为了解决这个…...

登录注册静态网页实现(HTML,CSS)
实现效果图 实现效果 使用HTML编写页面结构,CSS美化界面,点击注册,跳转到注册界面,均为静态网页,是课上的一个小作业~ 使用正则表达式对输入进行验证,包括邮箱格式验证,用户名格式验证。 正则…...

基于FPGA的以太网设计(二)
一.以太网硬件架构概述 前文讲述了以太网的一些相关知识,本文将详细讲解以太网的硬件架构 以太网的电路架构一般由MAC、PHY、变压器、RJ45和传输介质组成,示意图如下所示: PHY:Physical Layer,即物理层。物理层定义了…...

OJ在线评测系统 后端微服务架构 注册中心 Nacos入门到启动
注册中心 服务架构中的注册中心是一个关键组件,用于管理和协助微服务之间的通信。注册中心的主要职责是服务的注册和发现,确保各个微服务能够相互找到并进行调用。 主要功能: 服务注册:微服务在启动时,将自身信息&am…...

RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...

【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...

零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...

使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化
是不是受够了安装了oracle database之后sqlplus的简陋,无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话,配置.bahs_profile后也能解决上下翻页这些,但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可,…...