Давайте продолжим изучение библиотеки Protobuf. В предыдущем видео мы ее себе установили и настроили. Сейчас давайте узнаем, как писать proto-файлы, каким синтаксисом описывать данные, которые мы хотим сериализовать и десериализовать. Это сделать довольно просто с помощью официального сайта библиотеки Protobuf, который у меня сейчас открыт. Это главная страница сайта, здесь можно нажать "Learn more" и прочитать общее описание библиотеки, где рассказывается о ее возможностях, о ее преимуществах и о том, как она упрощает разработку кода, связанного с сериализацией. Мы немного погрузимся в синтаксис. Мы, конечно, не будем рассматривать все аспекты синтаксис Protobuf, но рассмотрим основные моменты. Нужно обратить внимание, что в Protobuf есть история, немного похожая на Python. Есть Python 2 и Python 3, так и в Protobuf есть синтаксис proto2 и синтаксис proto3. Мы будем рассматривать proto3, потому что это более новый, более современный формат, и будем опираться на него. Можно открыть эту ссылку, и здесь описано, как в синтаксисе proto3 создавать proto-файлы. Мы этим и займемся, и начнем разрабатывать адресную книгу, в которая будет выполняться сериализация и десериализация. Начнем с того, что создадим наш первый proto-файл. Давайте создадим файл, назовем его "person.proto". Первым делом напишем, что этот файл будет иметь "syntax = proto3". У меня в редакторе есть плагин, который подсвечивает синтаксис Protobuf. И объявим первое сообщение "message Person {}", и скажем, что у нашего контакта есть имя и целочисленный возраст. У вас сразу может возникнуть вопрос, что значат эти "1" и "2", потому что их можно спутать с инициализаторами переменных в C++. Эти числа — номера полей в сообщении, и они обладают важными свойствами. Первое: они используются при сериализации и десериализации сообщений. Достаточно просто используются — когда мы формируем бинарный поток, сначала пишем единичку. Это означает, что дальше будет записано поле "name". Дальше, допустим, записываем двойку. И, когда это будем разбирать, мы поймем, что мы встретили двойку, значит, дальше будет сохранено поле "age", и так далее. Эти номера полей должны быть уникальны внутри одного сообщения. Это понятно, мы только что сказали, что они используются для того, чтобы отличать поля друг от друга в процессе десериализации, поэтому внутри одного сообщения эти номера должны быть уникальными. И, наконец, они не должны меняться со временем. Это тоже понятно, мы только что сказали, что когда мы включаем, например, двойку, то дальше идет поле "возраст". Соответственно, если мы эти номера поменяем, то наша десериализация может сломаться. Давайте теперь посмотрим, как из этого proto-файла генерируется код на C++. Для этого мы откроем консоль, у меня открыта консоль в папке, где лежит мой файл "person.proto" и запустим компилятор Protobuf, который мы установили в предыдущем видео. Он установлен в "C:\dev\protobuf\bin\protoc.exe". Дальше мы указываем параметр "-I", говорим, что нам нужен вывод на языке C++, и передаем наш файл "person.proto". Запускаем компиляцию и видим, что в третьей строчке, которая на самом деле не третья, а первая, у нас не хватает точки с запятой. Запускаем компиляцию, и все отлично компилируется. Если мы посмотрим содержимое папки, то у нас появились файлы "person.pb.h" и "person.pb.cc". Это то, как Protobuf именует генерируемые им источники. Он добавляет суффикс "pb", как сокращение от "protobuf", собственное расширение "h" и немного необычное для нашего курса расширение "cc", а не "cpp". Иногда источники на C++ используют такое расширение. Давайте посмотрим, что в этих файлах хранится. Я открыл файл "person.pb.h", и мы здесь видим достаточно много кода, даже перейдем в конец файла, 275 строк в этом файле, и здесь достаточно много разных функций. Но на что нам надо обратить внимание? Мы в одном из предыдущих видео говорили, что для каждого сообщения в proto-файле генерируется класс на C++. И вот здесь, в 62 строке, мы видим, что у нас есть сообщение, которое называется "Person", и в соответствующем файле на C++ появился класс "Person". В нем тоже много методов: здесь есть и оператор присваивания, и конструктор перемещения, и много разных других служебных методов. Давайте долистаем до методов, которые соответствуют полям нашего сообщения. Здесь даже в комментарии написано, что дальше идут методы, относящиеся к полю "name", вот поле "name" с номером один, и мы видим, что есть метод, который также называется "name", который позволяет прочитать содержимое поля "name", есть методы "set_name", позволяющие установить его значение, и так далее. Дальше есть методы для поля "age", и мы в следующих видео разберем, какие методы генерируются для разного типа полей. Давайте продолжим и скажем, что нам надо развивать схему данных. У нашего контакта должен появиться адрес. При этом адрес можно представить как отдельное сообщение "message Address {}", здесь у нас есть "street" и есть номер дома "2". Мы сказали, что эти номера полей должны быть уникальными внутри сообщения, но между сообщениями они могут отличаться. Давайте добавим адрес в сообщение "Person". Снова выполним компиляцию. Она успешно прошла. Кстати, если мы здесь используем двойку, то при компиляции компилятор protoc сразу напишет, что "Field number 2 has already been used in Person". Он говорит, что должна поддерживаться уникальность номеров полей. Выполняем компиляцию, компиляция прошла успешно, смотрим как изменился "person.pb.h". Во-первых, появился класс "Address", который соответствует сообщению "Адрес". У него здесь также есть служебные методы, и есть методы, которые относятся к полям, вот "string street = 1", и поле "building". Если мы посмотрим на класс "Person", у него, помимо методов для поля "name" и поля "age", появились методы для работы с полем "address", которые позволяют обращаться к нему, читать его, настраивать и так далее. Давайте вот что еще сделаем — мы скажем, что у нашего контакта есть номер телефона "string phone_number". У людей иногда бывает несколько номеров телефона, и, чтобы это выразить, мы добавим ключевое слово "repeated", которое позволяет в proto-сообщении задать массив. Снова запускаем компиляцию, компиляция успешна, открываем файл "person.pb.h" и видим, что в классе "Person" появился набор методов для работы с массивом номеров телефонов. В частности, мы можем узнать, сколько у нас номеров телефонов, мы можем обращаться к номеру по индексу, мы можем менять номер телефона по индексу, и так далее. Мы видим, что, развивая наше proto-сообщение, мы наполняем соответствующие классы все новыми и новыми методами, которые позволяют работать с полями этих сообщений. Давайте подведем итоги. В этом видео мы познакомились с синтаксисом proto3. Мы узнали, что для описания формата данных нужно в proto-файле создавать сообщения, при этом сообщения состоят из полей, и у каждого поля есть свой уникальный целочисленный номер. Для создания массивов синтаксисе proto3 используется ключевое слово "repeated". И мы посмотрели и воспользовались компилятором protoc, который используется для того, чтобы превращать proto-файлы в исходный код на языке C++ и на других языках программирования.