Protobuf使用

Protobuf使用

github地址

目录

  • proto3的更新
  • 定义协议格式
  • 编译protobuf
  • protobuf_API
    • 枚举和嵌套类
    • 标准消息方法
    • 解析和序列化
  • 写一条消息
  • 阅读消息
  • 编译
  • Protobuf扩展
  • 优化
  • 高级用法

proto3的更新

  • 在第一行非空白非注释行,必须写:
syntax  = "proto3";
  • 字段规则移除了 required,并把 optional 改名为 singular;
    proto2required 也是不推荐使用的。proto3 直接从语法层面上移除了 required规则。其实可以做的更彻底,把所有字段规则描述都撤销,原来的repeated 改为在类型或字段名后加一对中括号。这样是不是更简洁?

  • repeated字段默认采用 packed 编码;
    proto2 中,需要明确使用 [packed=true] 来为字段指定比较紧凑的 packed 编码方式。

  • 移除了default 选项;
    proto2 中,可以使用 default 选项为某一字段指定默认值。在 proto3 中,字段的默认值只能根据字段类型由系统决定。也就是说,默认值全部是约定好的,而不再提供指定默认值的语法。
    在字段被设置为默认值的时候,该字段不会被序列化。这样可以节省空间,提高效率。
    但这样就无法区分某字段是根本没赋值,还是赋值了默认值。这在 proto3 中问题不大,但在 proto2 中会有问题。
    比如,在更新协议的时候使用 default 选项为某个字段指定了一个与原来不同的默认值,旧代码获取到的该字段的值会与新代码不一样。

  • 枚举类型的第一个字段必须为 0 ;

  • 移除了对分组的支持;
    分组的功能完全可以用消息嵌套的方式来实现,并且更清晰。在 proto2 中已经把分组语法标注为『过期』了。这次也算清理垃圾了。

  • 移除了对扩展的支持,新增了 Any 类型;
    Any 类型是用来替代 proto2 中的扩展的。目前还在开发中。
    proto2 中的扩展特性很像 Swift 语言中的扩展。理解起来有点困难,使用起来更是会带来不少混乱。
    相比之下,proto3 中新增的 Any 类型有点像 C/C++ 中的 void* ,好理解,使用起来逻辑也更清晰。

  • 增加了 JSON 映射特性;
    语言的活力来自于与时俱进。当前,JSON 的流行有其充分的理由。很多『现代化』的语言都内置了对 JSON 的支持,比如 GoPHP 等。而 C++ 这种看似包罗万象的学院派语言,因循守旧、故步自封,以致于现出了式微的苗头。

  • map支持

map<key_type, value_type> map_field = N;
example:
map<string, Project> projects = 3;
  • 在 proto3 中,纯数字类型的 repeated 字段编码时候默认采用 packed 编码(具体原因见 Protocol Buffer 编码原理 这一章节)

定义协议格式

.proto文件中的定义很简单:为要序列化的每个数据结构添加消息,然后为消息中的每个字段指定名称和类型。这是.proto定义您的消息的文件addressbook.proto

(好的.proto文件命名风格是:packagename.messagename.proto)

syntax = "proto3";package tutorial;message Person {string name = 1;int32 id = 2;string email = 3;enum PhoneType {MOBILE = 0;HOME = 1;WORK = 2;}message PhoneNumber {string number = 1;PhoneType type = 2;}repeated PhoneNumber phones = 4;
}message AddressBook {repeated Person people = 1;
}

.proto文件以包声明开头,这有助于防止不同项目之间的命名冲突。在C++中,生成的类将放在与包名匹配的命名空间中。

每个元素上的“= 1”,“= 2”标记标识该字段在二进制编码中使用的唯一“标记”。标签号1-15需要少于一个字节来编码而不是更高的数字,因此作为优化,您可以决定将这些标签用于常用或重复的元素,将标签16和更高版本留给不太常用的可选元素。重复字段中的每个元素都需要重新编码标记号,因此重复字段特别适合此优化。

可以指定的最小字段编号为1,最大字段编号为229-1 或 536,870,911。也不能使用数字 19000 到 19999(FieldDescriptor :: kFirstReservedNumber 到 FieldDescriptor :: kLastReservedNumber),因为它们是为 Protocol Buffers实现保留的。

必须使用以下修饰符之一注释每个字段:

  • required(proto3中移除):必须提供该字段的值,否则该消息将被视为“未初始化”。如果libprotobuf在调试模式下编译,则序列化未初始化的消息将导致断言失败。在优化的构建中,将跳过检查并始终写入消息。但是,解析未初始化的消息将始终失败(通过false从解析方法返回)。除此之外,必填字段的行为与可选字段完全相同。

  • optional(proto3中为singular):该字段可能已设置,也可能未设置。如果未设置可选字段值,则使用默认值。对于简单类型,您可以指定自己的默认值,就像我们type在示例中为电话号码所做的那样。否则,使用系统默认值:数字类型为0,字符串为空字符串,boolsfalse。对于嵌入式消息,默认值始终是消息的“默认实例”或“原型”,其中没有设置其字段。调用访问器以获取尚未显式设置的可选(或必需)字段的值始终返回该字段的默认值。

  • repeated(proto3默认采用 packed 编码):该字段可以重复任意次数(包括零)。重复值的顺序将保留在协议缓冲区中。将重复字段视为动态大小的数组。

  • proto3 中移除了default选项:字段的默认值只能根据字段类型由系统决定。也就是说,默认值全部是约定好的,而不再提供指定默认值的语法。在字段被设置为默认值的时候,该字段不会被序列化。这样可以节省空间,提高效率。

编译protobuf

现在运行编译器,指定源目录(应用程序的源代码所在的位置 - 如果不提​​供值,则使用当前目录),目标目录(您希望生成的代码在哪里;通常相同$SRC_DIR) ,以及你的道路.proto。:

protoc -I = $ SRC_DIR --cpp_out = $ DST_DIR $ SRC_DIR / addressbook.proto

这里都生成到当前目录,输入

protoc -I=. --cpp_out=. ./addressbook.proto
protoc --cpp_out=. addressbook.proto // 这种也可以

因为您需要C++类,所以使用该--cpp_out选项 - 为其他支持的语言提供了类似的选项。

这将在指定的目标目录中生成以下文件:

  • addressbook.pb.h,标头声明您生成的类。
  • addressbook.pb.cc,其中包含您的类的实现。

protobuf_API

addressbook.pb.h中,可以看到指定的每条消息都有一个类addressbook.proto。对于Person类,可以看到编译器已为每个字段生成了访问器。 例如,对于名称,ID,电子邮件和电话字段,有以下方法:

// name
inline bool has_name() const;
inline void clear_name();
inline const ::std::string& name() const;
inline void set_name(const ::std::string& value);
inline void set_name(const char* value);
inline ::std::string* mutable_name();// id
inline bool has_id() const;
inline void clear_id();
inline int32_t id() const;
inline void set_id(int32_t value);// email
inline bool has_email() const;
inline void clear_email();
inline const ::std::string& email() const;
inline void set_email(const ::std::string& value);
inline void set_email(const char* value);
inline ::std::string* mutable_email();// phones
inline int phones_size() const;
inline void clear_phones();
inline const ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >& phones() const;
inline ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >* mutable_phones();
inline const ::tutorial::Person_PhoneNumber& phones(int index) const;
inline ::tutorial::Person_PhoneNumber* mutable_phones(int index);
inline ::tutorial::Person_PhoneNumber* add_phones();

对于字符串 : 一个mutable_让你获得指向字符串的直接指针的getter,以及一个额外的setter。请注意,mutable_email()即使email尚未设置,您也可以进行呼叫; 它将自动初始化为空字符串。如果你在这个例子中有一个单数的消息字段,它也有一个mutable_方法但不是一个set_方法。

重复的字段也有一些特殊的方法 - 如果你看一下repeated phones字段的方法,你会发现你可以

  • 检查重复的字段的_size (换句话说,有多少电话号码与此相关联的 Person).

  • 使用索引获取指定的电话号码.

  • 更新指定索引处的现有电话号码.

  • 在邮件中添加另一个电话号码然后可以编辑(重复的标量类型add_只允许您传入新值).

有关协议编译器为任何特定字段定义生成的确切成员的详细信息,请参阅C ++生成的代码参考。

枚举和嵌套类

生成的代码包含PhoneType与您的.proto枚举对应的枚举。您可以参考这个类型Person::PhoneType及其作为值的Person::MOBILEPerson::HOMEPerson::WORK(实现细节是稍微复杂一点,但你并不需要了解他们使用ENUM)。

编译器还为您生成了一个嵌套类Person::PhoneNumber。如果查看代码,可以看到实际调用了“真实”类Person_PhoneNumber,但是在内部定义的typedef Person允许您将其视为嵌套类。唯一不同的情况是,如果你想在另一个文件中转发声明类 - 你不能在C++中转发声明嵌套类型,但你可以转发声明Person_PhoneNumber

标准消息方法

每个消息类还包含许多其他方法,可用于检查或操作整个消息,包括:

  • bool IsInitialized() const; 检查是否已设置所有必填字段。

  • string DebugString() const; 返回消息的人类可读表示,对调试特别有用。

  • void CopyFrom(const Person& from); 使用给定消息的值覆盖消息。

  • void Clear(); 清除所有元素回到空状态。

以下部分中描述的这些和I / O方法实现了Message所有C ++协议缓冲区类共享的接口。有关详细信息,请参阅完整的API文档Message。

解析和序列化

最后,每个协议缓冲区类都有使用协议缓冲区二进制格式编写和读取所选类型消息的方法。这些包括:

  • bool SerializeToString(string output) const;* 序列化消息并将字节存储在给定的字符串中。请注意,字节是二进制的,而不是文本; 我们只将该 string 类用作方便的容器。

  • bool ParseFromString(const string& data); 解析给定字符串中的消息。

  • bool SerializeToOstream(ostream output) const;* 将消息写入给定的 C++ ostream

  • bool ParseFromIstream(istream input);* 解析来自给定 C++ 的消息 istream

这些只是解析和序列化提供的几个选项。再次,请参阅MessageAPI参考以获取完整列表。

写一条消息

现在尝试使用协议缓冲类。地址簿应用程序能够做的第一件事是将个人详细信息写入地址簿文件。为此,需要创建并填充协议缓冲区类的实例,然后将它们写入输出流。

这是一个程序,它从文件中读取一个AddressBook,根据用户输入在AddressBook文件中添加一个新的Person,然后再将新文本写回文件。直接调用或引用协议编译器生成的代码的部分将突出显示。

#include <iostream>
#include <fstream>
#include <string>
#include "addressbook.pb.h"
using namespace std;// This function fills in a Person message based on user input.
void PromptForAddress(tutorial::Person* person) {cout << "Enter person ID number: ";int id;cin >> id;person->set_id(id);cin.ignore(256, '\n');cout << "Enter name: ";getline(cin, *person->mutable_name());cout << "Enter email address (blank for none): ";string email;getline(cin, email);if (!email.empty()) {person->set_email(email);}while (true) {cout << "Enter a phone number (or leave blank to finish): ";string number;getline(cin, number);if (number.empty()) {break;}tutorial::Person::PhoneNumber* phone_number = person->add_phones();phone_number->set_number(number);cout << "Is this a mobile, home, or work phone? ";string type;getline(cin, type);if (type == "mobile") {phone_number->set_type(tutorial::Person::MOBILE);} else if (type == "home") {phone_number->set_type(tutorial::Person::HOME);} else if (type == "work") {phone_number->set_type(tutorial::Person::WORK);} else {cout << "Unknown phone type.  Using default." << endl;}}
}// Main function:  Reads the entire address book from a file,
//   adds one person based on user input, then writes it back out to the same
//   file.
int main(int argc, char* argv[]) {// Verify that the version of the library that we linked against is// compatible with the version of the headers we compiled against.GOOGLE_PROTOBUF_VERIFY_VERSION;if (argc != 2) {cerr << "Usage:  " << argv[0] << " ADDRESS_BOOK_FILE" << endl;return -1;}tutorial::AddressBook address_book;{// Read the existing address book.fstream input(argv[1], ios::in | ios::binary);if (!input) {cout << argv[1] << ": File not found.  Creating a new file." << endl;} else if (!address_book.ParseFromIstream(&input)) {cerr << "Failed to parse address book." << endl;return -1;}}// Add an address.PromptForAddress(address_book.add_people());{// Write the new address book back to disk.fstream output(argv[1], ios::out | ios::trunc | ios::binary);if (!address_book.SerializeToOstream(&output)) {cerr << "Failed to write address book." << endl;return -1;}}// Optional:  Delete all global objects allocated by libprotobuf.google::protobuf::ShutdownProtobufLibrary();return 0;
}

注意GOOGLE_PROTOBUF_VERIFY_VERSION宏。在使用C ++协议缓冲区库之前执行此宏是一种很好的做法 - 尽管不是绝对必要的。它验证您没有意外链接到与您编译的标头版本不兼容的库版本。如果检测到版本不匹配,程序将中止。请注意,每个.pb.cc文件在启动时都会自动调用此宏。

还要注意ShutdownProtobufLibrary()程序结束时的调用。所有这一切都是删除协议缓冲区库分配的所有全局对象。对于大多数程序来说这是不必要的,因为该过程无论如何都要退出,操作系统将负责回收其所有内存。但是,如果您使用需要释放每个最后一个对象的内存泄漏检查程序,或者您正在编写可以由单个进程多次加载和卸载的库,那么您可能希望强制协议缓冲区清除所有内容。

阅读消息

当然,如果无法从中获取任何信息,那么地址簿就不会有多大用处!此示例读取上面示例创建的文件并打印其中的所有信息。

#include <iostream>
#include <fstream>
#include <string>
#include "addressbook.pb.h"
using namespace std;// Iterates though all people in the AddressBook and prints info about them.
void ListPeople(const tutorial::AddressBook& address_book) {for (int i = 0; i < address_book.people_size(); i++) {const tutorial::Person& person = address_book.people(i);cout << "Person ID: " << person.id() << endl;cout << "  Name: " << person.name() << endl;if (person.email() !=  "") {cout << "  E-mail address: " << person.email() << endl;}for (int j = 0; j < person.phones_size(); j++) {const tutorial::Person::PhoneNumber& phone_number = person.phones(j);switch (phone_number.type()) {case tutorial::Person::MOBILE:cout << "  Mobile phone #: ";break;case tutorial::Person::HOME:cout << "  Home phone #: ";break;case tutorial::Person::WORK:cout << "  Work phone #: ";break;}cout << phone_number.number() << endl;}}
}// Main function:  Reads the entire address book from a file and prints all
//   the information inside.
int main(int argc, char* argv[]) {// Verify that the version of the library that we linked against is// compatible with the version of the headers we compiled against.GOOGLE_PROTOBUF_VERIFY_VERSION;if (argc != 2) {cerr << "Usage:  " << argv[0] << " ADDRESS_BOOK_FILE" << endl;return -1;}tutorial::AddressBook address_book;{// Read the existing address book.fstream input(argv[1], ios::in | ios::binary);if (!address_book.ParseFromIstream(&input)) {cerr << "Failed to parse address book." << endl;return -1;}}ListPeople(address_book);// Optional:  Delete all global objects allocated by libprotobuf.google::protobuf::ShutdownProtobufLibrary();return 0;
}

编译

g++ -Wall -std=c++11 write.cpp addressbook.pb.cc -o write `pkg-config --cflags --libs protobuf`
g++ -Wall -std=c++11 read.cpp addressbook.pb.cc -o read `pkg-config --cflags --libs protobuf`

Protobuf扩展

在释放使用协议缓冲区的代码之后迟早,您无疑会想要“改进”协议缓冲区的定义。如果你希望你的新缓冲区向后兼容,并且你的旧缓冲区是向前兼容的 - 而且你几乎肯定想要这个 - 那么你需要遵循一些规则。在新版本的协议缓冲区中:

  • 不得更改任何现有字段的标记号。

  • 不得添加或删除任何必填字段。

  • 可以删除可选或重复的字段。

  • 可以添加新的可选或重复字段,但必须使用新的标记号(即从未在此协议缓冲区中使用的标记号,甚至不包括已删除的字段)。

(这些规则有一些例外,但它们很少使用。)

如果您遵循这些规则,旧代码将很乐意阅读新消息并简单地忽略任何新字段。对于旧代码,已删除的可选字段将只具有其默认值,删除的重复字段将为空。新代码也将透明地读取旧消息。但是,请记住旧的消息中不会出现新的可选字段,因此您需要明确检查它们是否已设置has_,或者在.proto文件中提供合理的默认值[default = value]标签号后面。如果未为可选元素指定默认值,则使用特定于类型的默认值:对于字符串,默认值为空字符串。对于布尔值,默认值为false。对于数字类型,默认值为零。另请注意,如果添加了新的重复字段,则新代码将无法判断它是否为空(通过新代码)或从未设置(通过旧代码),因为没有has_标记。

优化

C ++协议缓冲区库经过了极大的优化。但是,正确使用可以进一步提高性能。以下是从库中挤出最后一滴速度的一些提示:

尽可能重用消息对象。消息尝试保留它们分配用于重用的任何内存,即使它们被清除。因此,如果您连续处理具有相同类型和类似结构的许多消息,则每次重新使用相同的消息对象来加载内存分配器是个好主意。但是,随着时间的推移,对象会变得臃肿,特别是如果您的消息在“形状”上有所不同,或者您偶尔构造的消息比平常大得多。

您应该通过调用SpaceUsed方法来监视消息对象的大小,并在它们变得太大时删除它们。
您的系统内存分配器可能没有针对从多个线程分配大量小对象进行良好优化。请尝试使用Google的tcmalloc。

高级用法

协议缓冲区的用途不仅仅是简单的访问器和序列化。请务必浏览C ++ API参考,以了解您可以使用它们做些什么。

协议消息类提供的一个关键特性是反射。您可以迭代消息的字段并操纵它们的值,而无需针对任何特定的消息类型编写代码。使用反射的一种非常有用的方法是将协议消息转换为与其他编码(例如XMLJSON)之间的转换。更高级的反射使用可能是找到两个相同类型的消息之间的差异,或者开发一种“协议消息的正则表达式”,您可以在其中编写与某些消息内容匹配的表达式。如果您运用自己的想象力,可以将协议缓冲区应用于比您最初预期更广泛的问题!

Message::Reflection界面 提供反射。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/448093.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大佬(概率期望DP)

首先根据数据范围&#xff0c;可以判断基本上是n^2的复杂度 通过分析我们发现每一次都可以从m个数中任意选&#xff0c;既然任意选&#xff0c;那么此时的概率的分母就是不变的&#xff0c;然而题中涉及的是某一段的最大值&#xff0c;所以我们按套路假设 f[i][j]表示第i天&…

vue 父组件 调用 子组件的方法

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 我们都知道通过$ref可以获取到某个DOM&#xff0c;但是它也可以用来获取子组件的实例&#xff0c;调用子组件的方法 例&#xff1a; 子…

新手开车13招技巧

开车是一个靠经验积累技术的过程&#xff0c;新手们往往会在开车时遇到很多问题&#xff0c;我们用本篇文章和新手讲述开车的各种技巧&#xff0c;希望每个新手都能从中受益。第1招技巧&#xff1a;上车前要先看车  上车前绕车转一圈&#xff0c;看车的外况、轮胎、车底下有没…

高效的数据压缩编码方式 Protobuf

高效的数据压缩编码方式 Protobuf github地址 目录 ProtocolBuffers 是什么为什么要发明 ProtocolBuffersproto3 定义 Message 分配字段编号保留字段默认字段规则各个语言标量类型对应关系枚举枚举中的保留值允许嵌套枚举不兼容性更新 Message未知字段Map 类型JsonMapping p…

解决 VUE前端项目报错:RangeError: Maximum call stack size exceeded

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1. 我点击菜单按钮报错&#xff1a; RangeError: Maximum call stack size exceeded 2. 原因&#xff1a;参数传递有问题或者方法调用有…

新手必须掌握的学车技巧-上坡起步

我们知道&#xff0c;做什么事情都是万事开头难&#xff0c;新手们在学车方面更能体会到这一点&#xff0c;正确掌握学车技巧对于新手来说是非常重要的事情&#xff0c;今天&#xff0c;平安学车网&#xff08;www.paxcw.com&#xff09;就会大家探讨一下我们学车时必须掌握的是…

高效的序列化/反序列化数据方式 Protobuf

高效的序列化/反序列化数据方式 Protobuf github地址 目录 protocolBuffers 序列化 Int32StringMapslice序列化小结 protocolBuffers 反序列化 Int32StringMapslice序列化小结 序列化/反序列化性能最后 protocolBuffers序列化 上篇文章中其实已经讲过了 encode 的过程&…

如何配置一个Oracle服务

1、网络服务名&#xff1a;即填写OracleTNS的值&#xff0c;如OracleTNSorcl_192.168.1.125&#xff0c;填写orcl_192.168.1.1252、主机名&#xff1a;192.168.1.1253、服务名&#xff1a;orcl4、测试成功即可。 转载于:https://www.cnblogs.com/dengshiwei/p/4258719.html

解决 VUE前端项目报错: Uncaught ReferenceError : initPage is not defined (initPage 方法是有的,依旧报错找不到)

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1. 明明代码中定义了 initPage 这个方法&#xff0c;&#xff0c;却一直报找不到这个方法&#xff1a; Uncaught ReferenceError: init…

掌握新手学车技巧对于新手来说是非常重要的

刚开始学车的时候对于新手来说很多操作不知道从哪里下手&#xff0c;这个时候&#xff0c;如果按照相关的学车技巧来学习的话&#xff0c;对于新手来说是非常有好处的。下面我们就来学习一下让新手们可以快速进入开车状态的学车技巧吧&#xff01;基本上驾校的教练都会教学员把…

iView学习笔记(三):表格搜索,过滤及隐藏列操作

iView学习笔记(三)&#xff1a;表格搜索&#xff0c;过滤及隐藏某列操作 1.后端准备工作 环境说明 python版本&#xff1a;3.6.6 Django版本&#xff1a;1.11.8 数据库&#xff1a;MariaDB 5.5.60 新建Django项目&#xff0c;在项目中新建app&#xff0c;配置好数据库 api_test…

Jenkins自动编译库并上传服务器

Jenkins自动编译库并上传服务器 github地址 首先添加 git 地址&#xff1a; 再添加定时构建&#xff0c;每天夜里构建一次&#xff1a; 执行 shell 脚本进行构建 cd networklayerecho "build json x86" cmake -S . -B cmake-build-release -DCMAKE_BUILD_TYPERele…

解决:The “data“ option should be a function that returns a per-instance value in component definitions

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1. 只是想定义一个变量&#xff0c;方便页面上调用 。 报错&#xff1a; The "data" option should be a function that re…

科目三考试里面的会车,调头,靠边停车通过标准

科目三会车&#xff1a;减速靠道路的右侧边缘线行驶&#xff0c;速度要减到20km/h以下&#xff0c;靠右以不压右侧边缘线为基准尽量靠右。会车结束指令发出后向左打方向回到道路中央。考点&#xff1a;1.速度要降到20km/h&#xff0c;有时考官故意刁难&#xff0c;会在直线行驶…

Esxi直通板载Sata

Esxi安装好后&#xff0c;打开SSH。 解决方法如下&#xff1a; shell下执行&#xff1a; lspci -v | grep "Class 0106"-B 1&#xff0c;查看是否有如下显示&#xff1a;0000:00:1f.2 SATAcontroller Mass storage controller: Intel Corporation Lynx Point AHCICon…

gdb 调试 TuMediaService

gdb 调试 TuMediaService github地址 起因 首先需要有 armgdb 环境运行 ./armgdb ./TuMediaService 进入 gdb 模式b hi3531_trcod_interface.cc:98 打断点r 运行程序print this->vdec_config_path_ 打印关键值 在这里我们关注的值已经被修改&#xff0c;由于程序中没有刻…

jackson 的注解:@JsonProperty、@JsonIgnore、@JsonFormat 用法说明

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1. 导包&#xff1a; <dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-data…

科目三-变更车道,直线行驶和超车的考试标准

直线行驶&#xff1a;这是唯一一个可以提前操作的项目&#xff0c;当听到“下一项考试为直线行驶......”的指令时&#xff0c;可以立即把车身摆正。放在道路的正中间&#xff0c;并踩油门&#xff0c;把速度提至30----50km/h&#xff0c;最好保持在35---40km/h&#xff0c;因为…

PyQt安装和环境配置

PyQt安装和环境配置 github地址 首先安装Pycharm 新建一个空的 python 工程&#xff0c;找到 setting 安装第三方模块 PyQT5 , 点加号&#xff0c;先安 PyQT5 , 再安装 pyqt5-tools &#xff0c;后面包含 qtdesinger 以上模块都安完&#xff0c;设置扩展工具的参数找到 sett…

HZOJ 大佬(kat)

及其水水水的假期望&#xff08;然而我已经被期望吓怕了……&#xff09;。 数据范围及其沙雕导致丢掉5分…… 因为其实每天的期望是一样的&#xff0c;考虑分开。 f[i][j]表示做k道题&#xff0c;难度最大为j的概率。 则f[i][j](f[i-1][j])*(j-1)*temq[j]*tem;q为前缀和&#…