ПРИМЕЧАНИЕ. Этот сайт устарел. Сайт будет отключен после 31 января 2023 года, и трафик будет перенаправлен на новый сайт по адресу https://protobuf.dev . А пока обновления будут производиться только для protobuf.dev.

Сгенерированный код C++

Оптимизируйте свои подборки Сохраняйте и классифицируйте контент в соответствии со своими настройками.

На этой странице точно описывается, какой код C++ генерирует компилятор буфера протокола для любого заданного определения протокола. Любые различия между сгенерированным кодом proto2 и proto3 выделены — обратите внимание, что эти различия находятся в сгенерированном коде, как описано в этом документе, а не в базовых классах/интерфейсах сообщений, которые одинаковы в обеих версиях. Прежде чем читать этот документ, вы должны прочитать руководство по языку proto2 и/или руководство по языку proto3 .

Вызов компилятора

Компилятор буфера протокола создает выходные данные C++ при вызове с флагом командной строки --cpp_out= . Параметр опции --cpp_out= — это каталог, в который вы хотите, чтобы компилятор записал ваши выходные данные C++. Компилятор создает файл заголовка и файл реализации для каждого входного файла .proto . Имена выходных файлов вычисляются путем взятия имени файла .proto и внесения двух изменений:

  • Расширение ( .proto ) заменяется на .pb.h или .pb.cc для файла заголовка или реализации соответственно.
  • Прото-путь (указанный флагом командной строки --proto_path= или -I ) заменяется выходным путем (указанным флагом --cpp_out= ).

Итак, допустим, вы вызываете компилятор следующим образом:

protoc --proto_path=src --cpp_out=build/gen src/foo.proto src/bar/baz.proto

Компилятор прочитает файлы src/foo.proto и src/bar/baz.proto и создаст четыре выходных файла: build/gen/foo.pb.h , build/gen/foo.pb.cc , build/gen/bar/baz.pb.h . build/gen/bar/baz.pb.h , build/gen/bar/baz.pb.cc . Компилятор автоматически создаст каталог build/gen/bar , если это необходимо, но не создаст build или build/gen ; они должны уже существовать.

Пакеты

Если файл .proto содержит объявление package , все содержимое файла будет помещено в соответствующее пространство имен C++. Например, учитывая объявление package :

package foo.bar;

Все объявления в файле будут находиться в пространстве имен foo::bar .

Сообщения

Учитывая простое объявление сообщения:

message Foo {}

Компилятор буфера протокола создает класс с именем Foo , который публично наследуется от google::protobuf::Message . Класс — это конкретный класс; никакие чисто виртуальные методы не остаются нереализованными. Методы, которые являются виртуальными в Message , но не чисто виртуальными, могут быть или не быть переопределены Foo , в зависимости от режима оптимизации. По умолчанию Foo реализует специализированные версии всех методов для максимальной скорости. Однако, если файл .proto содержит строку:

option optimize_for = CODE_SIZE;

тогда Foo переопределит только минимальный набор методов, необходимых для работы, и положится на реализации остальных на основе отражения. Это значительно уменьшает размер генерируемого кода, но также снижает производительность. В качестве альтернативы, если файл .proto содержит:

option optimize_for = LITE_RUNTIME;

тогда Foo будет включать быстрые реализации всех методов, но будет реализовывать интерфейс google::protobuf::MessageLite , который содержит только подмножество методов Message . В частности, он не поддерживает дескрипторы или отражение. Однако в этом режиме сгенерированный код должен быть связан только с libprotobuf-lite.so ( libprotobuf-lite.lib в Windows) вместо libprotobuf.so ( libprotobuf.lib ). Облегченная библиотека намного меньше полной библиотеки и больше подходит для систем с ограниченными ресурсами, таких как мобильные телефоны.

Вы не должны создавать свои собственные подклассы Foo . Если вы подклассируете этот класс и переопределяете виртуальный метод, переопределение может быть проигнорировано, так как многие сгенерированные вызовы методов девиртуализируются для повышения производительности.

Интерфейс Message определяет методы, которые позволяют вам проверять, обрабатывать, читать или записывать все сообщение целиком, включая синтаксический анализ и сериализацию в двоичные строки.

  • bool ParseFromString(const string& data) : анализировать сообщение из заданной сериализованной двоичной строки (также известной как проводной формат).
  • bool SerializeToString(string* output) const : сериализовать данное сообщение в двоичную строку.
  • string DebugString() : возвращает строку, дающую представление `text_format` прототипа (следует использовать только для отладки).

В дополнение к этим методам класс Foo определяет следующие методы:

  • Foo() : конструктор по умолчанию.
  • ~Foo() : деструктор по умолчанию.
  • Foo(const Foo& other) : конструктор копирования.
  • Foo(Foo&& other) : Переместить конструктор.
  • Foo& operator=(const Foo& other) : оператор присваивания.
  • Foo& operator=(Foo&& other) : оператор присваивания перемещения.
  • void Swap(Foo* other) : заменить содержимое другим сообщением.
  • const UnknownFieldSet & unknown_fields() const : возвращает набор неизвестных полей, обнаруженных при анализе этого сообщения.
  • UnknownFieldSet * mutable_unknown_fields() : возвращает указатель на изменяемый набор неизвестных полей, обнаруженных при анализе этого сообщения.

Класс также определяет следующие статические методы:

  • static const Descriptor * descriptor() : Возвращает дескриптор типа. Он содержит информацию о типе, в том числе о том, какие поля он имеет и каковы их типы. Это можно использовать с отражением для проверки полей программно.
  • static const Foo& default_instance() : Возвращает экземпляр const singleton Foo , который идентичен вновь созданному экземпляру Foo (поэтому все единичные поля не установлены, а все повторяющиеся поля пусты). Обратите внимание, что экземпляр сообщения по умолчанию можно использовать в качестве фабрики, вызвав его метод New() .

Вложенные типы

Сообщение может быть объявлено внутри другого сообщения. Например: message Foo { message Bar { } }

В этом случае компилятор генерирует два класса: Foo и Foo_Bar . Кроме того, внутри Foo компилятор генерирует typedef следующим образом:

typedef Foo_Bar Bar;

Это означает, что вы можете использовать класс вложенного типа, как если бы это был вложенный класс Foo::Bar . Однако обратите внимание, что C++ не позволяет объявлять вложенные типы заранее. Если вы хотите предварительно объявить Bar в другом файле и использовать это объявление, вы должны идентифицировать его как Foo_Bar .

Поля

В дополнение к методам, описанным в предыдущем разделе, компилятор буфера протокола создает набор методов доступа для каждого поля, определенного в сообщении в файле .proto . Эти методы написаны строчными/змеиными буквами, например has_foo() и clear_foo() .

Как и методы доступа, компилятор генерирует целочисленную константу для каждого поля, содержащего его номер поля. Имя константы — это буква k , за которой следует имя поля, преобразованное в верблюжий регистр, за которым следует FieldNumber . Например, для поля optional int32 foo_bar = 5; , компилятор сгенерирует константу static const int kFooBarFieldNumber = 5; .

Для средств доступа к полям, возвращающих const ссылку, эта ссылка может стать недействительной при следующем доступе к сообщению с изменением. Сюда входит вызов любого const метода доступа к любому полю, вызов любого const метода, унаследованного от Message , или изменение сообщения другими способами (например, с использованием сообщения в качестве аргумента функции Swap() ). Соответственно, гарантируется, что адрес возвращаемой ссылки будет одинаковым при различных вызовах метода доступа только в том случае, если за это время к сообщению не было выполнено доступа для изменения.

Для средств доступа к полям, возвращающих указатель, этот указатель может стать недействительным при следующем доступе к сообщению с изменением или без изменения. Это включает, независимо от константности, вызов любого метода доступа любого поля, вызов любого метода, унаследованного от Message , или доступ к сообщению другими способами (например, путем копирования сообщения с помощью конструктора копирования). Соответственно никогда не гарантируется, что значение возвращаемого указателя будет одинаковым при двух разных вызовах метода доступа.

Сингулярные числовые поля (proto2)

Для любого из этих определений полей:

optional int32 foo = 1;
required int32 foo = 1;

Компилятор сгенерирует следующие методы доступа:

  • bool has_foo() const : возвращает true , если поле установлено.
  • int32 foo() const : возвращает текущее значение поля. Если поле не задано, возвращает значение по умолчанию.
  • void set_foo(int32 value) : устанавливает значение поля. После этого has_foo() вернет true , а foo() вернет value .
  • void clear_foo() : очищает значение поля. После вызова has_foo() вернет false , а foo() вернет значение по умолчанию.

Для других числовых типов полей (включая bool ) int32 заменяется соответствующим типом C++ в соответствии с таблицей типов скалярных значений .

Сингулярные числовые поля (proto3)

Для этого определения поля:

int32 foo = 1;

Компилятор сгенерирует следующие методы доступа:

  • int32 foo() const : возвращает текущее значение поля. Если поле не задано, возвращает 0.
  • void set_foo(int32 value) : устанавливает значение поля. После этого foo() вернет value .
  • void clear_foo() : очищает значение поля. После этого foo() вернет 0.

Для других числовых типов полей (включая bool ) int32 заменяется соответствующим типом C++ в соответствии с таблицей типов скалярных значений .

Сингулярные строковые поля (proto2)

Для любого из этих определений полей:

optional string foo = 1;
required string foo = 1;
optional bytes foo = 1;
required bytes foo = 1;

Компилятор сгенерирует следующие методы доступа:

  • bool has_foo() const : возвращает true , если поле установлено.
  • const string& foo() const : возвращает текущее значение поля. Если поле не задано, возвращает значение по умолчанию.
  • void set_foo(const string& value) : устанавливает значение поля. После вызова has_foo() вернет true , а foo() вернет копию value .
  • void set_foo(string&& value) (C++11 и выше): устанавливает значение поля, исходя из переданной строки. После вызова has_foo() вернет true , а foo() вернет копию value .
  • void set_foo(const char* value) : устанавливает значение поля, используя строку с завершающим нулем в стиле C. После вызова has_foo() вернет true , а foo() вернет копию value .
  • void set_foo(const char* value, int size) : как и выше, но размер строки задается явно, а не определяется путем поиска нулевого байта-терминатора.
  • string* mutable_foo() : возвращает указатель на изменяемый string объект, в котором хранится значение поля. Если поле не было задано до вызова, то возвращаемая строка будет пустой ( не значение по умолчанию). После вызова has_foo() вернет true , а foo() вернет любое значение, записанное в данную строку.
  • void clear_foo() : очищает значение поля. После вызова has_foo() вернет false , а foo() вернет значение по умолчанию.
  • void set_allocated_foo(string* value) : устанавливает string объект в поле и освобождает предыдущее значение поля, если оно существует. Если указатель string не равен NULL , сообщение становится владельцем выделенного string объекта, и has_foo() возвращает значение true . Сообщение может удалить выделенный string объект в любое время, поэтому ссылки на объект могут быть признаны недействительными. В противном случае, если value равно NULL , поведение такое же, как при вызове clear_foo() .
  • string* release_foo() : освобождает право собственности на поле и возвращает указатель string объекта. После этого вызывающая сторона становится владельцем выделенного string объекта, has_foo() возвращает false , а foo() возвращает значение по умолчанию.

Сингулярные строковые поля (proto3)

Для любого из этих определений полей:

string foo = 1;
bytes foo = 1;

Компилятор сгенерирует следующие методы доступа:

  • const string& foo() const : возвращает текущее значение поля. Если поле не задано, возвращает пустую строку/пустые байты.
  • void set_foo(const string& value) : устанавливает значение поля. После этого foo() вернет копию value .
  • void set_foo(string&& value) (C++11 и выше): устанавливает значение поля, исходя из переданной строки. После этого foo() вернет копию value .
  • void set_foo(const char* value) : устанавливает значение поля, используя строку с завершающим нулем в стиле C. После этого foo() вернет копию value .
  • void set_foo(const char* value, int size) : как и выше, но размер строки задается явно, а не определяется путем поиска нулевого байта-терминатора.
  • string* mutable_foo() : возвращает указатель на изменяемый string объект, в котором хранится значение поля. Если поле не было задано до вызова, то возвращаемая строка будет пустой. После этого foo() вернет любое значение, записанное в данную строку.
  • void clear_foo() : очищает значение поля. После этого foo() вернет пустую строку/пустые байты.
  • void set_allocated_foo(string* value) : устанавливает string объект в поле и освобождает предыдущее значение поля, если оно существует. Если указатель string не равен NULL , сообщение становится владельцем выделенного string объекта. Сообщение может удалить выделенный string объект в любое время, поэтому ссылки на объект могут быть признаны недействительными. В противном случае, если value равно NULL , поведение такое же, как при вызове clear_foo() .
  • string* release_foo() : освобождает право собственности на поле и возвращает указатель string объекта. После этого вызывающий объект становится владельцем выделенного string объекта, а foo() возвращает пустую строку/пустые байты.

Сингулярные поля Enum (proto2)

Учитывая тип перечисления:

enum Bar {
  BAR_VALUE = 0;
  OTHER_VALUE = 1;
}

Для любого из этих определений полей:

optional Bar foo = 1;
required Bar foo = 1;

Компилятор сгенерирует следующие методы доступа:

  • bool has_foo() const : возвращает true , если поле установлено.
  • Bar foo() const : возвращает текущее значение поля. Если поле не задано, возвращает значение по умолчанию.
  • void set_foo(Bar value) : устанавливает значение поля. После этого has_foo() вернет true , а foo() вернет value . В режиме отладки (т.е. NDEBUG не определен), если value не соответствует ни одному из значений, определенных для Bar , этот метод прервет процесс.
  • void clear_foo() : очищает значение поля. После вызова has_foo() вернет false , а foo() вернет значение по умолчанию.

Сингулярные поля Enum (proto3)

Учитывая тип перечисления:

enum Bar {
  BAR_VALUE = 0;
  OTHER_VALUE = 1;
}

Для определения этого поля:

Bar foo = 1;

Компилятор сгенерирует следующие методы доступа:

  • Bar foo() const : возвращает текущее значение поля. Если поле не задано, возвращает значение по умолчанию (0).
  • void set_foo(Bar value) : устанавливает значение поля. После этого foo() вернет value .
  • void clear_foo() : очищает значение поля. После этого foo() вернет значение по умолчанию.

Отдельные встроенные поля сообщений

Учитывая тип сообщения:

message Bar {}

Для любого из этих определений полей:

//proto2
optional Bar foo = 1;
required Bar foo = 1;
//proto3
Bar foo = 1;

Компилятор сгенерирует следующие методы доступа:

  • bool has_foo() const : возвращает true , если поле установлено.
  • const Bar& foo() const : возвращает текущее значение поля. Если поле не задано, возвращает Bar без установленных полей (возможно, Bar::default_instance() ).
  • Bar* mutable_foo() : возвращает указатель на изменяемый объект Bar , в котором хранится значение поля. Если поле не было задано до вызова, то в возвращаемом Bar не будет установлено ни одно из полей (т. е. он будет идентичен вновь выделенному Bar ). После вызова has_foo() вернет true , а foo() вернет ссылку на тот же экземпляр Bar .
  • void clear_foo() : очищает значение поля. После вызова has_foo() вернет false , а foo() вернет значение по умолчанию.
  • void set_allocated_foo(Bar* bar) : устанавливает объект Bar в поле и освобождает предыдущее значение поля, если оно существует. Если указатель Bar не равен NULL , сообщение становится владельцем выделенного объекта Bar , а has_foo() возвращает true . В противном случае, если Bar имеет NULL , поведение такое же, как при вызове clear_foo() .
  • Bar* release_foo() : освобождает право собственности на поле и возвращает указатель на объект Bar . После этого вызывающий объект становится владельцем выделенного объекта Bar , has_foo() возвращает false , а foo() возвращает значение по умолчанию.

Повторяющиеся числовые поля

Для этого определения поля:

repeated int32 foo = 1;

Компилятор сгенерирует следующие методы доступа:

  • int foo_size() const : возвращает количество элементов, находящихся в данный момент в поле.
  • int32 foo(int index) const : возвращает элемент с заданным индексом, отсчитываемым от нуля. Вызов этого метода с индексом за пределами [0, foo_size()) приводит к неопределенному поведению.
  • void set_foo(int index, int32 value) : устанавливает значение элемента по заданному индексу, начинающемуся с нуля.
  • void add_foo(int32 value) : Добавляет новый элемент в конец поля с заданным значением.
  • void clear_foo() : удаляет все элементы из поля. После этого foo_size() вернет ноль.
  • const RepeatedField <int32>& foo() const : возвращает лежащий в основе RepeatedField , в котором хранятся элементы поля. Этот класс-контейнер предоставляет STL-подобные итераторы и другие методы.
  • RepeatedField <int32>* mutable_foo() : возвращает указатель на базовый изменяемый RepeatedField , в котором хранятся элементы поля. Этот класс-контейнер предоставляет STL-подобные итераторы и другие методы.

Для других числовых типов полей (включая bool ) int32 заменяется соответствующим типом C++ в соответствии с таблицей типов скалярных значений .

Повторяющиеся строковые поля

Для любого из этих определений полей:

repeated string foo = 1;
repeated bytes foo = 1;

Компилятор сгенерирует следующие методы доступа:

  • int foo_size() const : возвращает количество элементов, находящихся в данный момент в поле.
  • const string& foo(int index) const : возвращает элемент с заданным индексом, отсчитываемым от нуля. Вызов этого метода с индексом за пределами [0, foo_size()) приводит к неопределенному поведению.
  • void set_foo(int index, const string& value) : устанавливает значение элемента в заданном индексе, отсчитываемом от нуля.
  • void set_foo(int index, const char* value) : устанавливает значение элемента в заданном индексе, начинающемся с нуля, с использованием строки с завершающим нулем в стиле C.
  • void set_foo(int index, const char* value, int size) : как и выше, но размер строки задается явно, а не определяется поиском байта нулевого терминатора.
  • string* mutable_foo(int index) : возвращает указатель на изменяемый string объект, в котором хранится значение элемента с заданным индексом, отсчитываемым от нуля. Вызов этого метода с индексом за пределами [0, foo_size()) приводит к неопределенному поведению.
  • void add_foo(const string& value) : Добавляет новый элемент в конец поля с заданным значением.
  • void add_foo(const char* value) : Добавляет новый элемент в конец поля, используя строку с завершающим нулем в стиле C.
  • void add_foo(const char* value, int size) : как и выше, но размер строки задается явно, а не определяется путем поиска байта нулевого терминатора.
  • string* add_foo() : добавляет новый пустой строковый элемент в конец поля и возвращает указатель на него.
  • void clear_foo() : удаляет все элементы из поля. После этого foo_size() вернет ноль.
  • const RepeatedPtrField <string>& foo() const : возвращает лежащий в основе RepeatedPtrField , в котором хранятся элементы поля. Этот класс-контейнер предоставляет STL-подобные итераторы и другие методы.
  • RepeatedPtrField <string>* mutable_foo() : возвращает указатель на базовый изменяемый RepeatedPtrField , в котором хранятся элементы поля. Этот класс-контейнер предоставляет STL-подобные итераторы и другие методы.

Повторяющиеся поля Enum

Учитывая тип перечисления:

enum Bar {
  BAR_VALUE = 0;
  OTHER_VALUE = 1;
}

Для этого определения поля:

repeated Bar foo = 1;

Компилятор сгенерирует следующие методы доступа:

  • int foo_size() const : возвращает количество элементов, находящихся в данный момент в поле.
  • Bar foo(int index) const : возвращает элемент с заданным индексом, отсчитываемым от нуля. Вызов этого метода с индексом за пределами [0, foo_size()) приводит к неопределенному поведению.
  • void set_foo(int index, Bar value) : устанавливает значение элемента по заданному индексу, отсчитываемому от нуля. В режиме отладки (т.е. NDEBUG не определен), если value не соответствует ни одному из значений, определенных для Bar , этот метод прервет процесс.
  • void add_foo(Bar value) : Добавляет новый элемент в конец поля с заданным значением. В режиме отладки (т.е. NDEBUG не определен), если value не соответствует ни одному из значений, определенных для Bar , этот метод прервет процесс.
  • void clear_foo() : удаляет все элементы из поля. После этого foo_size() вернет ноль.
  • const RepeatedField <int>& foo() const : возвращает лежащий в основе RepeatedField , в котором хранятся элементы поля. Этот класс-контейнер предоставляет STL-подобные итераторы и другие методы.
  • RepeatedField <int>* mutable_foo() : возвращает указатель на базовый изменяемый RepeatedField , в котором хранятся элементы поля. Этот класс-контейнер предоставляет STL-подобные итераторы и другие методы.

Повторяющиеся встроенные поля сообщений

Учитывая тип сообщения:

message Bar {}

Для определения этого поля:

repeated Bar foo = 1;

Компилятор сгенерирует следующие методы доступа:

  • int foo_size() const : возвращает количество элементов, находящихся в данный момент в поле.
  • const Bar& foo(int index) const : возвращает элемент с заданным индексом, отсчитываемым от нуля. Вызов этого метода с индексом за пределами [0, foo_size()) приводит к неопределенному поведению.
  • Bar* mutable_foo(int index) : возвращает указатель на изменяемый объект Bar , в котором хранится значение элемента с заданным индексом, отсчитываемым от нуля. Вызов этого метода с индексом за пределами [0, foo_size()) приводит к неопределенному поведению.
  • Bar* add_foo() : добавляет новый элемент в конец поля и возвращает указатель на него. Возвращенный Bar является изменяемым, и ни одно из его полей не будет установлено (т. е. он будет идентичен вновь выделенному Bar ).
  • void clear_foo() : удаляет все элементы из поля. После этого foo_size() вернет ноль.
  • const RepeatedPtrField <Bar>& foo() const : возвращает лежащий в основе RepeatedPtrField , в котором хранятся элементы поля. Этот класс-контейнер предоставляет STL-подобные итераторы и другие методы.
  • RepeatedPtrField <Bar>* mutable_foo() : возвращает указатель на базовый изменяемый RepeatedPtrField , в котором хранятся элементы поля. Этот класс-контейнер предоставляет STL-подобные итераторы и другие методы.

Одно из числовых полей

Для этого определения одного из полей:

oneof example_name {
    int32 foo = 1;
    ...
}

Компилятор сгенерирует следующие методы доступа:

  • bool has_foo() const (только для proto2): возвращает true , если один из регистров равен kFoo .
  • int32 foo() const : Возвращает текущее значение поля, если одно из значений равно kFoo . В противном случае возвращает значение по умолчанию.
  • void set_foo(int32 value) :
    • Если установлено любое другое поле oneof в том же oneof, вызывается clear_example_name() .
    • Устанавливает значение этого поля и устанавливает значение oneof в kFoo .
    • has_foo() (только для proto2) вернет true, foo() вернет value , а example_name_case() вернет kFoo .
  • void clear_foo() :
    • Ничего не изменится, если один из регистров не kFoo .
    • Если oneof case равен kFoo , очищает значение поля и oneof case. has_foo() (только для proto2) вернет false , foo() вернет значение по умолчанию, а example_name_case() вернет EXAMPLE_NAME_NOT_SET .

Для других числовых типов полей (включая bool ) int32 заменяется соответствующим типом C++ в соответствии с таблицей типов скалярных значений .

Одно из строковых полей

Для любого из этих определений одного из полей: l10n

oneof example_name {
    string foo = 1;
    …
}
oneof example_name {
    bytes foo = 1;
    ….
}

Компилятор сгенерирует следующие методы доступа:

  • bool has_foo() const : Возвращает true , если один из регистров равен kFoo .
  • const string& foo() const : Возвращает текущее значение поля, если одно из значений равно kFoo . В противном случае возвращает значение по умолчанию.
  • void set_foo(const string& value) :
    • Если установлено любое другое поле oneof в том же oneof, вызывается clear_example_name() .
    • Устанавливает значение этого поля и устанавливает значение oneof в kFoo .
    • has_foo() вернет true , foo() вернет копию value а example_name_case() вернет kFoo .
  • void set_foo(const char* value) :
    • Если установлено любое другое поле oneof в том же oneof, вызывается clear_example_name() .
    • Устанавливает значение поля, используя строку с завершающим нулем в стиле C, и устанавливает значение oneof для kFoo .
    • has_foo() вернет true , foo() вернет копию value а example_name_case() вернет kFoo .
  • void set_foo(const char* value, int size) : как и выше, но размер строки задается явно, а не определяется путем поиска нулевого байта-терминатора.
  • string* mutable_foo() :
    • Если установлено любое другое поле oneof в том же oneof, вызывается clear_example_name() .
    • Устанавливает для случая oneof значение kFoo и возвращает указатель на изменяемый строковый объект, в котором хранится значение поля. Если перед вызовом значение oneof не было kFoo , то возвращаемая строка будет пустой (не значение по умолчанию).
    • has_foo() вернет true , foo() вернет любое значение, записанное в заданную строку, а example_name_case() вернет kFoo .
  • void clear_foo() :
    • Если один из регистров не kFoo , ничего не изменится.
    • Если oneof case равен kFoo , освобождает поле и очищает oneof case. has_foo() вернет false , foo() вернет значение по умолчанию, а example_name_case() вернет EXAMPLE_NAME_NOT_SET .
  • void set_allocated_foo(string* value) :
    • Вызывает clear_example_name() .
    • Если указатель строки не равен NULL : Устанавливает строковый объект в поле и устанавливает значение oneof в kFoo . Сообщение становится владельцем выделенного строкового объекта, has_foo() вернет true , а example_name_case() вернет kFoo .
    • Если указатель строки равен NULL , has_foo() вернет false , а example_name_case() вернет EXAMPLE_NAME_NOT_SET .
  • string* release_foo() :
    • Возвращает NULL , если один из регистров не kFoo .
    • Очищает регистр oneof, освобождает владельца поля и возвращает указатель строкового объекта. После этого вызывающий объект становится владельцем выделенного строкового объекта, has_foo() возвращает false, foo() возвращает значение по умолчанию, а example_name_case() возвращает EXAMPLE_NAME_NOT_SET .

Одно из полей Enum

Учитывая тип перечисления:

enum Bar {
  BAR_VALUE = 0;
  OTHER_VALUE = 1;
}

Для определения поля oneof :

oneof example_name {
    Bar foo = 1;
    ...
}

Компилятор сгенерирует следующие методы доступа:

  • bool has_foo() const (только для proto2): возвращает true , если один из регистров равен kFoo .
  • Bar foo() const : возвращает текущее значение поля, если один из регистров равен kFoo . В противном случае возвращает значение по умолчанию.
  • void set_foo(Bar value) :
    • Если установлено любое другое поле oneof в том же oneof, вызывается clear_example_name() .
    • Устанавливает значение этого поля и устанавливает значение oneof в kFoo .
    • has_foo() (только для proto2) вернет true , foo() вернет value , а example_name_case() вернет kFoo .
    • В режиме отладки (т.е. NDEBUG не определен), если value не соответствует ни одному из значений, определенных для Bar , этот метод прервет процесс.
  • void clear_foo() :
    • Ничего не изменится, если один из регистров не kFoo .
    • Если oneof case равен kFoo , очищает значение поля и oneof case. has_foo() (только для proto2) вернет false , foo() вернет значение по умолчанию, а example_name_case() вернет EXAMPLE_NAME_NOT_SET .

Одно из встроенных полей сообщения

Учитывая тип сообщения:

message Bar {}

Для определения поля oneof :

oneof example_name {
    Bar foo = 1;
    ...
}

Компилятор сгенерирует следующие методы доступа:

  • bool has_foo() const : возвращает true, если один из регистров равен kFoo .
  • const Bar& foo() const : Возвращает текущее значение поля, если одно из значений равно kFoo . В противном случае возвращает Bar::default_instance() .
  • Bar* mutable_foo() :
    • Если установлено любое другое поле oneof в том же oneof, вызывается clear_example_name() .
    • Устанавливает случай oneof в kFoo и возвращает указатель на изменяемый объект Bar, в котором хранится значение поля. Если перед вызовом значение oneof было не kFoo , то в возвращаемом Bar не будет установлено ни одно из его полей (т. е. он будет идентичен вновь выделенному Bar).
    • После этого has_foo() вернет true , foo() вернет ссылку на тот же экземпляр Bar , а example_name_case() вернет kFoo .
  • void clear_foo() :
    • Ничего не изменится, если один из регистров не kFoo .
    • Если oneof case равен kFoo , поле освобождается и очищается oneof case. has_foo() вернет false , foo() вернет значение по умолчанию, а example_name_case() вернет EXAMPLE_NAME_NOT_SET .
  • void set_allocated_foo(Bar* bar) :
    • Вызывает clear_example_name() .
    • Если указатель Bar не равен NULL : Устанавливает объект Bar в поле и устанавливает значение oneof case в kFoo . Сообщение становится владельцем выделенного объекта Bar , has_foo() вернет true, а example_name_case() вернет kFoo .
    • Если указатель равен NULL , has_foo() вернет false , а example_name_case() вернет EXAMPLE_NAME_NOT_SET . (Поведение похоже на вызов clear_example_name() )
  • Bar* release_foo() :
    • Возвращает NULL , если один из регистров не kFoo .
    • Если oneof case равен kFoo , очищает oneof case, освобождает владельца поля и возвращает указатель на объект Bar . После этого вызывающий объект становится владельцем выделенного объекта Bar , has_foo() возвращает false , foo() возвращает значение по умолчанию, а example_name_case() возвращает EXAMPLE_NAME_NOT_SET .

Поля карты

Для этого определения поля карты:

map<int32, int32> weight = 1;

Компилятор сгенерирует следующие методы доступа:

  • const google::protobuf::Map<int32, int32>& weight(); : возвращает неизменяемую Map .
  • google::protobuf::Map<int32, int32>* mutable_weight(); : возвращает изменяемую Map .

google::protobuf::Map — это специальный тип контейнера, используемый в буферах протокола для хранения полей карты. Как вы можете видеть из его интерфейса ниже, он использует часто используемое подмножество методов std::map и std::unordered_map .

template<typename Key, typename T> {
class Map {
  // Member types
  typedef Key key_type;
  typedef T mapped_type;
  typedef MapPair< Key, T > value_type;

  // Iterators
  iterator begin();
  const_iterator begin() const;
  const_iterator cbegin() const;
  iterator end();
  const_iterator end() const;
  const_iterator cend() const;
  // Capacity
  int size() const;
  bool empty() const;

  // Element access
  T& operator[](const Key& key);
  const T& at(const Key& key) const;
  T& at(const Key& key);

  // Lookup
  int count(const Key& key) const;
  const_iterator find(const Key& key) const;
  iterator find(const Key& key);

  // Modifiers
  pair<iterator, bool> insert(const value_type& value);
  template<class InputIt>
  void insert(InputIt first, InputIt last);
  size_type erase(const Key& Key);
  iterator erase(const_iterator pos);
  iterator erase(const_iterator first, const_iterator last);
  void clear();

  // Copy
  Map(const Map& other);
  Map& operator=(const Map& other);
}

Самый простой способ добавить данные — использовать синтаксис карты нормалей, например:

std::unique_ptr<ProtoName> my_enclosing_proto(new ProtoName);
(*my_enclosing_proto->mutable_weight())[my_key] = my_value;
.

pair<iterator, bool> insert(const value_type& value) неявно вызовет глубокую копию экземпляра value_type . Самый эффективный способ вставить новое значение в google::protobuf::Map выглядит следующим образом:

T& operator[](const Key& key): map[new_key] = new_mapped;

Использование google::protobuf::Map со стандартными картами

google::protobuf::Map поддерживает тот же API-интерфейс итератора, что и std::map и std::unordered_map . Если вы не хотите использовать google::protobuf::Map напрямую, вы можете преобразовать google::protobuf::Map в стандартную карту, выполнив следующие действия:

std::map<int32, int32> standard_map(message.weight().begin(),
                                    message.weight().end());

Обратите внимание, что это сделает глубокую копию всей карты.

Вы также можете создать google::protobuf::Map из стандартной карты следующим образом:

google::protobuf::Map<int32, int32> weight(standard_map.begin(), standard_map.end());

Разбор неизвестных значений

В сети карта .proto эквивалентна сообщению записи карты для каждой пары ключ/значение, а сама карта представляет собой повторяющееся поле записей карты. Как и обычные типы сообщений, проанализированное сообщение записи карты может иметь неизвестные поля: например, поле типа int64 в карте, определенной как map<int32, string> .

Если в проводном формате сообщения о вводе карты есть неизвестные поля, они будут отброшены.

Если в проводном формате сообщения записи карты есть неизвестное значение перечисления, оно обрабатывается по-разному в proto2 и proto3. В proto2 все сообщение записи карты помещается в неизвестный набор полей содержащего сообщения. В proto3 он помещается в поле карты, как если бы это было известное значение перечисления.

Любой

Учитывая Any поле, подобное этому:

import "google/protobuf/any.proto";

message ErrorStatus {
  string message = 1;
  google.protobuf.Any details = 2;
}

В нашем сгенерированном коде геттер для поля details возвращает экземпляр google::protobuf::Any . Это предоставляет следующие специальные методы для упаковки и распаковки значений Any :

class Any {
 public:
  // Packs the given message into this Any using the default type URL
  // prefix “type.googleapis.com”. Returns false if serializing the message failed.
  bool PackFrom(const google::protobuf::Message& message);

  // Packs the given message into this Any using the given type URL
  // prefix. Returns false if serializing the message failed.
  bool PackFrom(const google::protobuf::Message& message,
                const string& type_url_prefix);

  // Unpacks this Any to a Message. Returns false if this Any
  // represents a different protobuf type or parsing fails.
  bool UnpackTo(google::protobuf::Message* message) const;

  // Returns true if this Any represents the given protobuf type.
  template<typename T> bool Is() const;
}

Один из

Учитывая такое определение oneof:
oneof example_name {
    int32 foo_int = 4;
    string foo_string = 9;
    ...
}

Компилятор сгенерирует следующий тип перечисления C++:

enum ExampleNameCase {
  kFooInt = 4,
  kFooString = 9,
  EXAMPLE_NAME_NOT_SET = 0
}
.

Кроме того, он сгенерирует следующие методы:

  • ExampleNameCase example_name_case() const : возвращает перечисление, указывающее, какое поле установлено. Возвращает EXAMPLE_NAME_NOT_SET , если ни один из них не установлен.
  • void clear_example_name() : Frees the object if the oneof field set uses a pointer (Message or String), and sets the oneof case to EXAMPLE_NAME_NOT_SET .

Enumerations

Given an enum definition like:

enum Foo {
  VALUE_A = 0;
  VALUE_B = 5;
  VALUE_C = 1234;
}

The protocol buffer compiler will generate a C++ enum type called Foo with the same set of values. In addition, the compiler will generate the following functions:

  • const EnumDescriptor * Foo_descriptor() : Returns the type's descriptor, which contains information about what values this enum type defines.
  • bool Foo_IsValid(int value) : Returns true if the given numeric value matches one of Foo 's defined values. In the above example, it would return true if the input were 0, 5, or 1234.
  • const string& Foo_Name(int value) : Returns the name for given numeric value. Returns an empty string if no such value exists. If multiple values have this number, the first one defined is returned. In the above example, Foo_Name(5) would return "VALUE_B" .
  • bool Foo_Parse(const string& name, Foo* value) : If name is a valid value name for this enum, assigns that value into value and returns true. Otherwise returns false. In the above example, Foo_Parse("VALUE_C", &some_foo) would return true and set some_foo to 1234.
  • const Foo Foo_MIN : the smallest valid value of the enum (VALUE_A in the example).
  • const Foo Foo_MAX : the largest valid value of the enum (VALUE_C in the example).
  • const int Foo_ARRAYSIZE : always defined as Foo_MAX + 1 .

Be careful when casting integers to proto2 enums. If an integer is cast to a proto2 enum value, the integer must be one of the valid values for that enum, or the results may be undefined. If in doubt, use the generated Foo_IsValid() function to test if the cast is valid. Setting an enum-typed field of a proto2 message to an invalid value may cause an assertion failure. If an invalid enum value is read when parsing a proto2 message, it will be treated as an unknown field . These semantics have been changed in proto3. It's safe to cast any integer to a proto3 enum value as long as it fits into int32. Invalid enum values will also be kept when parsing a proto3 message and returned by enum field accessors.

Be careful when using proto3 enums in switch statements. Proto3 enums are open enum types with possible values outside the range of specified symbols. Unrecognized enum values will be kept when parsing a proto3 message and returned by the enum field accessors. A switch statement on a proto3 enum without a default case will not be able to catch all cases even if all the known fields are listed. This could lead to unexpected behavior including data corruption and runtime crashes. Always add a default case or explicitly call Foo_IsValid(int) outside of the switch to handle unknown enum values.

You can define an enum inside a message type. In this case, the protocol buffer compiler generates code that makes it appear that the enum type itself was declared nested inside the message's class. The Foo_descriptor() and Foo_IsValid() functions are declared as static methods. In reality, the enum type itself and its values are declared at the global scope with mangled names, and are imported into the class's scope with a typedef and a series of constant definitions. This is done only to get around problems with declaration ordering. Do not depend on the mangled top-level names; pretend the enum really is nested in the message class.

Extensions (proto2 only)

Given a message with an extension range:

message Foo {
  extensions 100 to 199;
}

The protocol buffer compiler will generate some additional methods for Foo : HasExtension() , ExtensionSize() , ClearExtension() , GetExtension() , SetExtension() , MutableExtension() , AddExtension() , SetAllocatedExtension() and ReleaseExtension() . Each of these methods takes, as its first parameter, an extension identifier (described below), which identifies an extension field. The remaining parameters and the return value are exactly the same as those for the corresponding accessor methods that would be generated for a normal (non-extension) field of the same type as the extension identifier. ( GetExtension() corresponds to the accessors with no special prefix.)

Given an extension definition:

extend Foo {
  optional int32 bar = 123;
  repeated int32 repeated_bar = 124;
}

For the singular extension field bar , the protocol buffer compiler generates an "extension identifier" called bar , which you can use with Foo 's extension accessors to access this extension, like so:

Foo foo;
assert(!foo.HasExtension(bar));
foo.SetExtension(bar, 1);
assert(foo.HasExtension(bar));
assert(foo.GetExtension(bar) == 1);
foo.ClearExtension(bar);
assert(!foo.HasExtension(bar));

Similarly, for the repeated extension field repeated_bar , the compiler generates an extension identifier called repeated_bar , which you can also use with Foo 's extension accessors:

Foo foo;
for (int i = 0; i < kSize; ++i) {
  foo.AddExtension(repeated_bar, i)
}
assert(foo.ExtensionSize(repeated_bar) == kSize)
for (int i = 0; i < kSize; ++i) {
  assert(foo.GetExtension(repeated_bar, i) == i)
}

(The exact implementation of extension identifiers is complicated and involves magical use of templates – however, you don't need to worry about how extension identifiers work to use them.)

Extensions can be declared nested inside of another type. For example, a common pattern is to do something like this:

message Baz {
  extend Foo {
    optional Baz foo_ext = 124;
  }
}

In this case, the extension identifier foo_ext is declared nested inside Baz . It can be used as follows:

Foo foo;
Baz* baz = foo.MutableExtension(Baz::foo_ext);
FillInMyBaz(baz);

Arena Allocation

Arena allocation is a C++-only feature that helps you optimize your memory usage and improve performance when working with protocol buffers. Enabling arena allocation in your .proto adds additional code for working with arenas to your C++ generated code. You can find out more about the arena allocation API in the Arena Allocation Guide .

Услуги

If the .proto file contains the following line:

option cc_generic_services = true;

Then the protocol buffer compiler will generate code based on the service definitions found in the file as described in this section. However, the generated code may be undesirable as it is not tied to any particular RPC system, and thus requires more levels of indirection than code tailored to one system. If you do NOT want this code to be generated, add this line to the file:

option cc_generic_services = false;

If neither of the above lines are given, the option defaults to false , as generic services are deprecated. (Note that prior to 2.4.0, the option defaults to true )

RPC systems based on .proto -language service definitions should provide plugins to generate code appropriate for the system. These plugins are likely to require that abstract services are disabled, so that they can generate their own classes of the same names.

The remainder of this section describes what the protocol buffer compiler generates when abstract services are enabled.

Interface

Given a service definition:

service Foo {
  rpc Bar(FooRequest) returns(FooResponse);
}

The protocol buffer compiler will generate a class Foo to represent this service. Foo will have a virtual method for each method defined in the service definition. In this case, the method Bar is defined as:

virtual void Bar(RpcController* controller, const FooRequest* request,
                 FooResponse* response, Closure* done);

The parameters are equivalent to the parameters of Service::CallMethod() , except that the method argument is implied and request and response specify their exact type.

These generated methods are virtual, but not pure-virtual. The default implementations simply call controller-> SetFailed() with an error message indicating that the method is unimplemented, then invoke the done callback. When implementing your own service, you must subclass this generated service and implement its methods as appropriate.

Foo subclasses the Service interface. The protocol buffer compiler automatically generates implementations of the methods of Service as follows:

  • GetDescriptor : Returns the service's ServiceDescriptor .
  • CallMethod : Determines which method is being called based on the provided method descriptor and calls it directly, down-casting the request and response messages objects to the correct types.
  • GetRequestPrototype and GetResponsePrototype : Returns the default instance of the request or response of the correct type for the given method.

The following static method is also generated:

  • static ServiceDescriptor descriptor() : Returns the type's descriptor, which contains information about what methods this service has and what their input and output types are.

Stub

The protocol buffer compiler also generates a "stub" implementation of every service interface, which is used by clients wishing to send requests to servers implementing the service. For the Foo service (above), the stub implementation Foo_Stub will be defined. As with nested message types, a typedef is used so that Foo_Stub can also be referred to as Foo::Stub .

Foo_Stub is a subclass of Foo which also implements the following methods:

  • Foo_Stub( RpcChannel * channel) : Constructs a new stub which sends requests on the given channel.
  • Foo_Stub( RpcChannel * channel, ChannelOwnership ownership) : Constructs a new stub which sends requests on the given channel and possibly owns that channel. If ownership is Service::STUB_OWNS_CHANNEL then when the stub object is deleted it will delete the channel as well.
  • RpcChannel * channel() : Returns this stub's channel, as passed to the constructor.

The stub additionally implements each of the service's methods as a wrapper around the channel. Calling one of the methods simply calls channel-> CallMethod() .

The Protocol Buffer library does not include an RPC implementation. However, it includes all of the tools you need to hook up a generated service class to any arbitrary RPC implementation of your choice. You need only provide implementations of RpcChannel and RpcController . See the documentation for service.h for more information.

Plugin Insertion Points

Code generator plugins which want to extend the output of the C++ code generator may insert code of the following types using the given insertion point names. Each insertion point appears in both the .pb.cc file and the .pb.h file unless otherwise noted.

  • includes : Include directives.
  • namespace_scope : Declarations that belong in the file's package/namespace, but not within any particular class. Appears after all other namespace-scope code.
  • global_scope : Declarations that belong at the top level, outside of the file's namespace. Appears at the very end of the file.
  • class_scope:TYPENAME : Member declarations that belong in a message class. TYPENAME is the full proto name, eg package.MessageType . Appears after all other public declarations in the class. This insertion point appears only in the .pb.h file.

Do not generate code which relies on private class members declared by the standard code generator, as these implementation details may change in future versions of Protocol Buffers.