- Вызов компилятора
- Пакеты
- Сообщения
- Поля
- Любой
- Один из
- Перечисления
- Расширения
- Распределение арены
- Услуги
- Точки вставки плагина
На этой странице точно описывается, какой код 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 singletonFoo
, который идентичен вновь созданному экземпляру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
.
- Если установлено любое другое поле oneof в том же oneof, вызывается
-
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
.
- Если установлено любое другое поле oneof в том же oneof, вызывается
-
void set_foo(const char* value)
:- Если установлено любое другое поле oneof в том же oneof, вызывается
clear_example_name()
. - Устанавливает значение поля, используя строку с завершающим нулем в стиле C, и устанавливает значение oneof для
kFoo
. -
has_foo()
вернетtrue
,foo()
вернет копиюvalue
аexample_name_case()
вернетkFoo
.
- Если установлено любое другое поле oneof в том же oneof, вызывается
-
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
.
- Если установлено любое другое поле oneof в том же oneof, вызывается
-
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
, этот метод прервет процесс.
- Если установлено любое другое поле oneof в том же oneof, вызывается
-
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
.
- Если установлено любое другое поле oneof в том же oneof, вызывается
-
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 toEXAMPLE_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)
: Returnstrue
if the given numeric value matches one ofFoo
's defined values. In the above example, it would returntrue
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)
: Ifname
is a valid value name for this enum, assigns that value intovalue
and returns true. Otherwise returns false. In the above example,Foo_Parse("VALUE_C", &some_foo)
would return true and setsome_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 asFoo_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'sServiceDescriptor
. -
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
andGetResponsePrototype
: 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. Ifownership
isService::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, egpackage.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.