- Invocação do compilador
- Pacotes
- Mensagens
- Campos
- Tudo
- Um
- Enumerações
- Extensões
- Alocação da área
- Serviços
- Pontos de inserção do plug-in
Esta página descreve exatamente o código C++ que o compilador de buffer de protocolo gera para qualquer definição de protocolo. Qualquer diferença entre o código gerado por proto2 e proto3 está destacada. Observe que essas diferenças estão no código gerado conforme descrito neste documento, não nas classes/interfaces de mensagem base, que são as mesmas nas duas versões. Leia o guia de idiomas do proto2 e/ou o guia do idioma do proto3 antes de ler este documento.
Invocação do compilador
O compilador de buffer de protocolo produz a saída C++ quando invocado com a sinalização de linha de comando --cpp_out=
. O parâmetro para a opção --cpp_out=
é o diretório em que você quer que o compilador grave sua saída C++. O compilador cria um arquivo principal e um arquivo de implementação para cada entrada de arquivo .proto
. Os nomes dos arquivos de saída são calculados com base no nome do arquivo .proto
e fazendo duas mudanças:
- A extensão (
.proto
) é substituída por.pb.h
ou.pb.cc
para o arquivo de cabeçalho ou implementação, respectivamente. - O caminho do proto (especificado com a sinalização de linha de comando
--proto_path=
ou-I
) é substituído pelo caminho de saída (especificado com a sinalização--cpp_out=
).
Então, por exemplo, digamos que você invoque o compilador da seguinte maneira:
protoc --proto_path=src --cpp_out=build/gen src/foo.proto src/bar/baz.proto
O compilador lerá os arquivos src/foo.proto
e src/bar/baz.proto
e produzirá quatro arquivos de saída: build/gen/foo.pb.h
, build/gen/foo.pb.cc
, build/gen/bar/baz.pb.h
, build/gen/bar/baz.pb.cc
. O compilador criará automaticamente o diretório build/gen/bar
, se necessário, mas não criará build
ou build/gen
. Eles já devem existir.
Entrega de pacotes
Se um arquivo .proto
contiver uma declaração package
, todo o conteúdo do arquivo será colocado em um namespace C++ correspondente. Por exemplo, considerando a declaração package
:
package foo.bar;
Todas as declarações no arquivo ficarão no namespace foo::bar
.
Mensagens
Se a declaração for simples:
message Foo {}
O compilador de buffer de protocolo gera uma classe chamada Foo
, que deriva publicamente de google::protobuf::Message
. A classe é concreta e nenhum método puramente virtual é implementado. Os métodos que são virtuais em Message
, mas não puramente virtuais, podem ou não ser substituídos por Foo
, dependendo do modo de otimização. Por padrão, o Foo
implementa versões especializadas de todos os métodos para alcançar a velocidade máxima. No entanto, se o arquivo .proto
contiver a linha:
option optimize_for = CODE_SIZE;
em seguida, o Foo
substituirá apenas o conjunto mínimo de métodos necessários para funcionar e confiar nas implementações baseadas em reflexão do restante. Isso reduz significativamente o tamanho do código gerado, mas também reduz o desempenho. Como alternativa, se o arquivo .proto
contiver:
option optimize_for = LITE_RUNTIME;
o Foo
incluirá implementações rápidas de todos os métodos, mas implementará a interface google::protobuf::MessageLite
, que contém apenas um subconjunto dos métodos da Message
. Especificamente, ele não suporta descritores ou reflexão. No entanto, nesse modo, o código gerado só precisa ser vinculado a libprotobuf-lite.so
(libprotobuf-lite.lib
no Windows) em vez de libprotobuf.so
(libprotobuf.lib
). A biblioteca "lite" é muito menor que a biblioteca completa e é mais adequada para sistemas com recursos limitados, como smartphones.
Não crie suas próprias subclasses Foo
. Se você criar uma subclasse para essa classe e substituir um método virtual, a modificação poderá ser ignorada, porque muitas chamadas de métodos geradas serão removidas para melhorar o desempenho.
A interface Message
define métodos que permitem verificar, manipular, ler ou gravar toda a mensagem, incluindo a análise e serialização para strings binárias.
bool ParseFromString(const string& data)
: analisa a mensagem da string binária serializada fornecida (também conhecida como formato de transmissão).bool SerializeToString(string* output) const
: serializa a mensagem fornecida para uma string binária.string DebugString()
: retorna uma string que fornece a representação `text_format` do proto. Precisa ser usada apenas para depuração.
Além desses métodos, a classe Foo
define os seguintes métodos:
Foo()
: construtor padrão.~Foo()
: destrutor padrão.Foo(const Foo& other)
: copiar o construtor.Foo(Foo&& other)
: move o construtor.Foo& operator=(const Foo& other)
: operador de atribuição.Foo& operator=(Foo&& other)
: operador de atribuição de movimentação.void Swap(Foo* other)
: troca conteúdo por outra mensagem.const UnknownFieldSet& unknown_fields() const
: retorna o conjunto de campos desconhecidos encontrados ao analisar essa mensagem.UnknownFieldSet* mutable_unknown_fields()
: retorna um ponteiro para o conjunto mutável de campos desconhecidos encontrado ao analisar essa mensagem.
A classe também define os seguintes métodos estáticos:
static const Descriptor* descriptor()
: retorna o descritor do tipo. Ela contém informações sobre o tipo, incluindo quais campos ele tem e quais são os tipos. Isso pode ser usado com a reflexão para inspecionar os campos de forma programática.static const Foo& default_instance()
: retorna uma instância singleton const deFoo
que é idêntica a uma instância recém-construída deFoo
. Portanto, todos os campos singulares não estão definidos e todos os campos repetidos estão vazios. A instância padrão de uma mensagem pode ser usada como fábrica chamando o métodoNew()
.
Tipos aninhados
Uma mensagem pode ser declarada dentro de outra. Por exemplo:
message Foo {
message Bar {
}
}
Nesse caso, o compilador gera duas classes: Foo
e Foo_Bar
. Além disso, o compilador gera um typedef dentro de Foo
da seguinte maneira:
typedef Foo_Bar Bar;
Isso significa que você pode usar a classe do tipo aninhado como se fosse a classe aninhada Foo::Bar
. No entanto, o C++ não permite que tipos aninhados sejam declarados para frente. Se você quiser declarar Bar
em outro arquivo e usar essa declaração, vai precisar identificá-la como Foo_Bar
.
Campos
Além dos métodos descritos na seção anterior, o compilador de buffer de protocolo gera um conjunto de métodos do acessador para cada campo definido na mensagem no arquivo .proto
. Esses métodos ficam em letras minúsculas ou em letras minúsculas, como has_foo()
e clear_foo()
.
Além dos métodos de acessador, o compilador gera uma constante de número inteiro para cada campo que contém o número do campo. O nome da constante é a letra k
, seguida pelo nome do campo convertido em letras concatenadas e por FieldNumber
. Por exemplo, dado o campo optional int32 foo_bar = 5;
, o compilador vai gerar a constante static const int kFooBarFieldNumber = 5;
.
Para os acessadores de campo que retornam uma referência const
, essa referência poderá ser invalidada quando o próximo acesso de modificação for realizado na mensagem. Isso inclui chamar qualquer acessador que não seja const
de qualquer campo, chamar qualquer método que não seja const
herdado de Message
ou modificar a mensagem de outras maneiras (por exemplo, usando a mensagem como argumento de Swap()
). Da mesma forma, o endereço da referência retornada só será o mesmo em invocações diferentes do acessador se nenhum acesso de modificação tiver sido feito na mensagem nesse período.
Para os acessadores de campo que retornam um ponteiro, esse ponteiro pode ser invalidado quando o próximo acesso de modificação ou não modificação é feito à mensagem. Isso inclui, seja qual for o estado, chamar qualquer acessador de qualquer campo, chamar qualquer método herdado de Message
ou acessar a mensagem de outras maneiras (por exemplo, copiando a mensagem usando o construtor de cópias). Da mesma forma, não é garantido que o valor do ponteiro retornado seja o mesmo em duas invocações diferentes do acessador.
Campos numéricos em singular (proto2)
Para qualquer uma dessas definições de campo:
optional int32 foo = 1; required int32 foo = 1;
O compilador vai gerar os seguintes métodos de acessador:
bool has_foo() const
: retornatrue
se o campo estiver definido.int32 foo() const
: retorna o valor atual do campo. Se o campo não for definido, retornará o valor padrão.void set_foo(int32 value)
: define o valor do campo. Depois disso,has_foo()
retornarátrue
, efoo()
retornarávalue
.void clear_foo()
: limpa o valor do campo. Depois de chamar isso,has_foo()
retornaráfalse
, efoo()
retornará o valor padrão.
Para outros tipos de campo numérico (incluindo bool
), int32
é substituído pelo tipo C++ correspondente de acordo com a tabela de tipos de valores escalares.
Campos numéricos no formato Singular (proto3)
Para esta definição de campo:
int32 foo = 1;
O compilador vai gerar os seguintes métodos de acessador:
int32 foo() const
: retorna o valor atual do campo. Se o campo não for definido, retornará 0.void set_foo(int32 value)
: define o valor do campo. Depois de chamar isso,foo()
retornarávalue
.void clear_foo()
: limpa o valor do campo. Depois de chamar isso,foo()
retornará 0.
Para outros tipos de campo numérico (incluindo bool
), int32
é substituído pelo tipo C++ correspondente de acordo com a tabela de tipos de valores escalares.
Campos de string Singular (proto2)
Para qualquer uma dessas definições de campo:
optional string foo = 1; required string foo = 1; optional bytes foo = 1; required bytes foo = 1;
O compilador vai gerar os seguintes métodos de acessador:
bool has_foo() const
: retornatrue
se o campo estiver definido.const string& foo() const
: retorna o valor atual do campo. Se o campo não for definido, retornará o valor padrão.void set_foo(const string& value)
: define o valor do campo. Depois disso,has_foo()
retornarátrue
, efoo()
retornará uma cópia devalue
.void set_foo(string&& value)
(C++11 e mais recentes): define o valor do campo, movendo da string transmitida. Depois disso,has_foo()
retornarátrue
, efoo()
retornará uma cópia devalue
.void set_foo(const char* value)
: define o valor do campo usando uma string encerrada com valores nulos no estilo C. Depois disso,has_foo()
retornarátrue
, efoo()
retornará uma cópia devalue
.void set_foo(const char* value, int size)
: como acima, mas o tamanho da string é informado explicitamente em vez de determinado por um byte nulo do terminador.string* mutable_foo()
: retorna um ponteiro para o objetostring
mutável que armazena o valor do campo. Se o campo não tiver sido definido antes da chamada, a string retornada estará vazia (não é o valor padrão). Depois de chamar isso,has_foo()
retornarátrue
, efoo()
retornará o valor que estiver gravado na string especificada.void clear_foo()
: limpa o valor do campo. Depois de chamar isso,has_foo()
retornaráfalse
, efoo()
retornará o valor padrão.void set_allocated_foo(string* value)
: define o objetostring
para o campo e libera o valor de campo anterior, se ele existir. Se o ponteirostring
não forNULL
, a mensagem assumirá a propriedade do objetostring
alocado, ehas_foo()
retornarátrue
. A mensagem pode excluir o objetostring
alocado a qualquer momento. Portanto, as referências ao objeto podem ser invalidadas. Caso contrário, sevalue
forNULL
, o comportamento será o mesmo que chamarclear_foo()
.string* release_foo()
: libera a propriedade do campo e retorna o ponteiro do objetostring
. Depois de chamar isso, o autor da chamada assume a propriedade do objetostring
alocado,has_foo()
retornafalse
efoo()
retorna o valor padrão.
Campos de string singular (proto3)
Para qualquer uma dessas definições de campo:
string foo = 1; bytes foo = 1;
O compilador vai gerar os seguintes métodos de acessador:
const string& foo() const
: retorna o valor atual do campo. Se o campo não for definido, retornará a string vazia/bytes vazios.void set_foo(const string& value)
: define o valor do campo. Depois disso,foo()
retornará uma cópia davalue
.void set_foo(string&& value)
(C++11 e mais recentes): define o valor do campo, movendo da string transmitida. Depois disso,foo()
retornará uma cópia davalue
.void set_foo(const char* value)
: define o valor do campo usando uma string encerrada com valores nulos no estilo C. Depois disso,foo()
retornará uma cópia davalue
.void set_foo(const char* value, int size)
: como acima, mas o tamanho da string é informado explicitamente em vez de determinado por um byte nulo do terminador.string* mutable_foo()
: retorna um ponteiro para o objetostring
mutável que armazena o valor do campo. Se o campo não tiver sido definido antes da chamada, a string retornada estará vazia. Depois de chamar isso,foo()
retornará o valor que estiver gravado na string especificada.void clear_foo()
: limpa o valor do campo. Depois de chamar isso,foo()
retornará a string vazia/bytes vazios.void set_allocated_foo(string* value)
: define o objetostring
para o campo e libera o valor de campo anterior, se ele existir. Se o ponteirostring
não forNULL
, a mensagem assumirá a propriedade do objetostring
alocado. A mensagem pode excluir o objetostring
alocado a qualquer momento. Portanto, as referências ao objeto podem ser invalidadas. Caso contrário, sevalue
forNULL
, o comportamento será o mesmo que chamarclear_foo()
.string* release_foo()
: libera a propriedade do campo e retorna o ponteiro do objetostring
. Depois de chamar isso, o autor da chamada assume a propriedade do objetostring
alocado efoo()
retornará a string/bytes vazios vazios.
Campos de enumeração de Singular (proto2)
Considerando o tipo de enumeração:
enum Bar { BAR_VALUE = 0; OTHER_VALUE = 1; }
Para qualquer uma dessas definições de campo:
optional Bar foo = 1; required Bar foo = 1;
O compilador vai gerar os seguintes métodos de acessador:
bool has_foo() const
: retornatrue
se o campo estiver definido.Bar foo() const
: retorna o valor atual do campo. Se o campo não for definido, retornará o valor padrão.void set_foo(Bar value)
: define o valor do campo. Depois disso,has_foo()
retornarátrue
, efoo()
retornarávalue
. No modo de depuração (ou seja, NDEBUG não está definido), o método vai cancelar o processo sevalue
não corresponder a nenhum dos valores definidos paraBar
.void clear_foo()
: limpa o valor do campo. Depois de chamar isso,has_foo()
retornaráfalse
, efoo()
retornará o valor padrão.
Campos de enumeração de Singular (proto3)
Considerando o tipo de enumeração:
enum Bar { BAR_VALUE = 0; OTHER_VALUE = 1; }
Para estas definições de campo:
Bar foo = 1;
O compilador vai gerar os seguintes métodos de acessador:
Bar foo() const
: retorna o valor atual do campo. Se o campo não for definido, retornará o valor padrão (0).void set_foo(Bar value)
: define o valor do campo. Depois de chamar isso,foo()
retornarávalue
.void clear_foo()
: limpa o valor do campo. Depois de chamar isso,foo()
retornará o valor padrão.
Campos de mensagens incorporadas no Singular
Considerando o tipo de mensagem:
message Bar {}
Para qualquer uma dessas definições de campo:
//proto2 optional Bar foo = 1; required Bar foo = 1;
//proto3 Bar foo = 1;
O compilador vai gerar os seguintes métodos de acessador:
bool has_foo() const
: retornatrue
se o campo estiver definido.const Bar& foo() const
: retorna o valor atual do campo. Se o campo não for definido, retornará umBar
sem nenhum dos campos definidos (possivelmenteBar::default_instance()
).Bar* mutable_foo()
: retorna um ponteiro para o objetoBar
mutável que armazena o valor do campo. Se o campo não tiver sido definido antes da chamada, oBar
retornado não terá nenhum dos campos definidos (ou seja, será idêntico a umBar
recém-alocado). Depois de chamar isso,has_foo()
retornarátrue
efoo()
retornará uma referência para a mesma instância deBar
.void clear_foo()
: limpa o valor do campo. Depois de chamar isso,has_foo()
retornaráfalse
, efoo()
retornará o valor padrão.void set_allocated_foo(Bar* bar)
: define o objetoBar
para o campo e libera o valor de campo anterior, se ele existir. Se o ponteiroBar
não forNULL
, a mensagem assumirá a propriedade do objetoBar
alocado, ehas_foo()
retornarátrue
. Caso contrário, seBar
forNULL
, o comportamento será o mesmo que chamarclear_foo()
.Bar* release_foo()
: libera a propriedade do campo e retorna o ponteiro do objetoBar
. Depois de chamar isso, o autor da chamada assume a propriedade do objetoBar
alocado,has_foo()
retornafalse
efoo()
retorna o valor padrão.
Campos numéricos repetidos
Para esta definição de campo:
repeated int32 foo = 1;
O compilador vai gerar os seguintes métodos de acessador:
int foo_size() const
: retorna o número de elementos atualmente em campo.int32 foo(int index) const
: retorna o elemento no índice com base em zero especificado. Chamar esse método com índice fora de [0, foo_size()) gera um comportamento indefinido.void set_foo(int index, int32 value)
: define o valor do elemento no índice baseado em zero específico.void add_foo(int32 value)
: anexa um novo elemento ao final do campo com o valor informado.void clear_foo()
: remove todos os elementos do campo. Depois de chamar isso,foo_size()
retornará zero.const RepeatedField<int32>& foo() const
: retorna oRepeatedField
subjacente que armazena os elementos do campo. Esta classe de contêiner fornece iteradores semelhantes aos do STL e outros métodos.RepeatedField<int32>* mutable_foo()
: retorna um ponteiro para oRepeatedField
mutável subjacente que armazena os elementos do campo. Esta classe de contêiner fornece iteradores semelhantes aos do STL e outros métodos.
Para outros tipos de campo numérico (incluindo bool
), int32
é substituído pelo tipo C++ correspondente de acordo com a tabela de tipos de valores escalares.
Campos de string repetidos
Para qualquer uma dessas definições de campo:
repeated string foo = 1; repeated bytes foo = 1;
O compilador vai gerar os seguintes métodos de acessador:
int foo_size() const
: retorna o número de elementos atualmente em campo.const string& foo(int index) const
: retorna o elemento no índice com base em zero especificado. Chamar esse método com índice fora de [0, foo_size()) gera um comportamento indefinido.void set_foo(int index, const string& value)
: define o valor do elemento no índice baseado em zero específico.void set_foo(int index, const char* value)
: define o valor do elemento no índice baseado em zero usando uma string com terminação nula no estilo C.void set_foo(int index, const char* value, int size)
: como acima, mas o tamanho da string é informado explicitamente em vez de determinado por um byte nulo do terminador.string* mutable_foo(int index)
: retorna um ponteiro para o objetostring
mutável que armazena o valor do elemento no índice baseado em zero especificado. Chamar esse método com índice fora de [0, foo_size()) gera um comportamento indefinido.void add_foo(const string& value)
: anexa um novo elemento ao final do campo com o valor informado.void add_foo(const char* value)
: anexa um novo elemento ao final do campo usando uma string com terminação nula no estilo C.void add_foo(const char* value, int size)
: como acima, mas o tamanho da string é informado explicitamente em vez de determinado por um byte nulo do terminador.string* add_foo()
: adiciona um novo elemento de string vazio ao final do campo e retorna um ponteiro para ele.void clear_foo()
: remove todos os elementos do campo. Depois de chamar isso,foo_size()
retornará zero.const RepeatedPtrField<string>& foo() const
: retorna oRepeatedPtrField
subjacente que armazena os elementos do campo. Esta classe de contêiner fornece iteradores semelhantes aos do STL e outros métodos.RepeatedPtrField<string>* mutable_foo()
: retorna um ponteiro para oRepeatedPtrField
mutável subjacente que armazena os elementos do campo. Esta classe de contêiner fornece iteradores semelhantes aos do STL e outros métodos.
Campos de enumeração repetidos
Considerando o tipo de enumeração:
enum Bar { BAR_VALUE = 0; OTHER_VALUE = 1; }
Para esta definição de campo:
repeated Bar foo = 1;
O compilador vai gerar os seguintes métodos de acessador:
int foo_size() const
: retorna o número de elementos atualmente em campo.Bar foo(int index) const
: retorna o elemento no índice com base em zero especificado. Chamar esse método com índice fora de [0, foo_size()) gera um comportamento indefinido.void set_foo(int index, Bar value)
: define o valor do elemento no índice baseado em zero específico. No modo de depuração (ou seja, NDEBUG não está definido), o método vai cancelar o processo sevalue
não corresponder a nenhum dos valores definidos paraBar
.void add_foo(Bar value)
: anexa um novo elemento ao final do campo com o valor informado. No modo de depuração (ou seja, NDEBUG não está definido), o método vai cancelar o processo sevalue
não corresponder a nenhum dos valores definidos paraBar
.void clear_foo()
: remove todos os elementos do campo. Depois de chamar isso,foo_size()
retornará zero.const RepeatedField<int>& foo() const
: retorna oRepeatedField
subjacente que armazena os elementos do campo. Esta classe de contêiner fornece iteradores semelhantes aos do STL e outros métodos.RepeatedField<int>* mutable_foo()
: retorna um ponteiro para oRepeatedField
mutável subjacente que armazena os elementos do campo. Esta classe de contêiner fornece iteradores semelhantes aos do STL e outros métodos.
Campos repetidos de mensagem incorporada
Considerando o tipo de mensagem:
message Bar {}
Para estas definições de campo:
repeated Bar foo = 1;
O compilador vai gerar os seguintes métodos de acessador:
int foo_size() const
: retorna o número de elementos atualmente em campo.const Bar& foo(int index) const
: retorna o elemento no índice com base em zero especificado. Chamar esse método com índice fora de [0, foo_size()) gera um comportamento indefinido.Bar* mutable_foo(int index)
: retorna um ponteiro para o objetoBar
mutável que armazena o valor do elemento no índice baseado em zero especificado. Chamar esse método com índice fora de [0, foo_size()) gera um comportamento indefinido.Bar* add_foo()
: adiciona um novo elemento ao final do campo e retorna um ponteiro para ele. OBar
retornado é mutável e não terá nenhum dos campos definido (ou seja, será idêntico a umBar
recém-alocado).void clear_foo()
: remove todos os elementos do campo. Depois de chamar isso,foo_size()
retornará zero.const RepeatedPtrField<Bar>& foo() const
: retorna oRepeatedPtrField
subjacente que armazena os elementos do campo. Esta classe de contêiner fornece iteradores semelhantes aos do STL e outros métodos.RepeatedPtrField<Bar>* mutable_foo()
: retorna um ponteiro para oRepeatedPtrField
mutável subjacente que armazena os elementos do campo. Esta classe de contêiner fornece iteradores semelhantes aos do STL e outros métodos.
Um dos campos numéricos
Para esta definição de campo oneof:
oneof example_name { int32 foo = 1; ... }
O compilador vai gerar os seguintes métodos de acessador:
bool has_foo() const
(somente proto2): retornatrue
se um dos casos forkFoo
.int32 foo() const
: retorna o valor atual do campo se um dos casos forkFoo
. Caso contrário, retorna o valor padrão.void set_foo(int32 value)
:- Se qualquer outro campo oneof no mesmo for definido, chamará
clear_example_name()
. - Define o valor deste campo e define o único caso como
kFoo
. has_foo()
(somente proto2) retornará "true",foo()
retornarávalue
, eexample_name_case()
retornarákFoo
.
- Se qualquer outro campo oneof no mesmo for definido, chamará
void clear_foo()
:- Nada vai mudar se um dos casos não for
kFoo
. - Se um caso for
kFoo
, limpará o valor do campo e um caso.has_foo()
(somente proto2) retornaráfalse
,foo()
retornará o valor padrão eexample_name_case()
retornaráEXAMPLE_NAME_NOT_SET
.
- Nada vai mudar se um dos casos não for
Para outros tipos de campo numérico (incluindo bool
), int32
é substituído pelo tipo de C++ correspondente de acordo com a tabela de tipos de valor escalar.
Campos de string do Oneof
Para qualquer uma dessas definições de campo oneof:
oneof example_name { string foo = 1; … } oneof example_name { bytes foo = 1; …. }
O compilador vai gerar os seguintes métodos de acessador:
bool has_foo() const
: retornatrue
se o único caso forkFoo
.const string& foo() const
: retornará o valor atual do campo se o caso único forkFoo
. Caso contrário, retorna o valor padrão.void set_foo(const string& value)
:- Se qualquer outro campo oneof no mesmo for definido, chamará
clear_example_name()
. - Define o valor deste campo e define o único caso como
kFoo
. has_foo()
retornarátrue
,foo()
retornará uma cópia devalue
eexample_name_case()
retornarákFoo
.
- Se qualquer outro campo oneof no mesmo for definido, chamará
void set_foo(const char* value)
:- Se qualquer outro campo oneof no mesmo for definido, chamará
clear_example_name()
. - Define o valor do campo usando uma string com terminação nula no estilo C e define oneof case como
kFoo
. has_foo()
retornarátrue
,foo()
retornará uma cópia devalue
eexample_name_case()
retornarákFoo
.
- Se qualquer outro campo oneof no mesmo for definido, chamará
void set_foo(const char* value, int size)
: como acima, mas o tamanho da string é informado explicitamente em vez de determinado por um byte nulo do terminador.string* mutable_foo()
:- Se qualquer outro campo oneof no mesmo for definido, chamará
clear_example_name()
. - Define o único caso como
kFoo
e retorna um ponteiro para o objeto de string mutável que armazena o valor do campo. Se o caso único não tiver sidokFoo
antes da chamada, a string retornada vai estar vazia (não o valor padrão). has_foo()
retornarátrue
,foo()
retornará o valor que estiver gravado na string especificada eexample_name_case()
retornarákFoo
.
- Se qualquer outro campo oneof no mesmo for definido, chamará
void clear_foo()
:- Se o caso único não for
kFoo
, nada será alterado . - Se o caso um for
kFoo
, libera o campo e limpa o caso um .has_foo()
retornaráfalse
,foo()
retornará o valor padrão, eexample_name_case()
retornaráEXAMPLE_NAME_NOT_SET
.
- Se o caso único não for
void set_allocated_foo(string* value)
:- Chama
clear_example_name()
. - Se o ponteiro de string não for
NULL
: define o objeto de string como o campo e define o único caso comokFoo
. A mensagem assume a propriedade do objeto de string alocado,has_foo()
retornarátrue
eexample_name_case()
retornarákFoo
. - Se o ponteiro de string for
NULL
,has_foo()
retornaráfalse
eexample_name_case()
retornaráEXAMPLE_NAME_NOT_SET
.
- Chama
string* release_foo()
:- Retornará
NULL
se um dos casos não forkFoo
. - Limpa o caso único, libera a propriedade do campo e retorna o ponteiro do objeto de string. Depois de chamar esse método, o autor da chamada assume a propriedade do objeto de string alocado,
has_foo()
retornará falso,foo()
retornará o valor padrão eexample_name_case()
retornaráEXAMPLE_NAME_NOT_SET
.
- Retornará
Campos Oneof Enum
Considerando o tipo de enumeração:
enum Bar { BAR_VALUE = 0; OTHER_VALUE = 1; }
Para a definição de campo oneof:
oneof example_name { Bar foo = 1; ... }
O compilador vai gerar os seguintes métodos de acessador:
bool has_foo() const
(somente proto2): retornatrue
se um dos casos forkFoo
.Bar foo() const
: retorna o valor atual do campo se um dos casos forkFoo
. Caso contrário, retorna o valor padrão.void set_foo(Bar value)
:- Se qualquer outro campo oneof no mesmo for definido, chamará
clear_example_name()
. - Define o valor deste campo e define o único caso como
kFoo
. has_foo()
(somente proto2) retornarátrue
,foo()
retornarávalue
eexample_name_case()
retornarákFoo
.- No modo de depuração (ou seja, NDEBUG não está definido), o método vai cancelar o processo se
value
não corresponder a nenhum dos valores definidos paraBar
.
- Se qualquer outro campo oneof no mesmo for definido, chamará
void clear_foo()
:- Nada vai mudar se o caso não for
kFoo
. - Se um dos casos for
kFoo
, limpará o valor do campo e do caso.has_foo()
(somente proto2) retornaráfalse
,foo()
retornará o valor padrão eexample_name_case()
retornaráEXAMPLE_NAME_NOT_SET
.
- Nada vai mudar se o caso não for
Um dos campos de mensagem incorporados
Considerando o tipo de mensagem:
message Bar {}
Para a definição de campo oneof:
oneof example_name { Bar foo = 1; ... }
O compilador vai gerar os seguintes métodos de acessador:
bool has_foo() const
: retorna "true" se um dos casos forkFoo
.const Bar& foo() const
: retorna o valor atual do campo se um dos casos forkFoo
. Caso contrário, retornaBar::default_instance()
.Bar* mutable_foo()
:- Se qualquer outro campo oneof no mesmo for definido, chamará
clear_example_name()
. - Define o único caso como
kFoo
e retorna um ponteiro para o objeto de barra mutável que armazena o valor do campo. Se o caso não erakFoo
antes da chamada, a barra retornada não terá nenhum dos campos definidos (ou seja, será idêntico a uma barra recém-alocada). - Depois de chamar isso,
has_foo()
retornarátrue
,foo()
retornará uma referência para a mesma instância deBar
eexample_name_case()
retornarákFoo
.
- Se qualquer outro campo oneof no mesmo for definido, chamará
void clear_foo()
:- Nada vai mudar se o caso não for
kFoo
. - Se a capitalização for igual a
kFoo
, o campo vai ser liberado e apagado.has_foo()
retornaráfalse
,foo()
retornará o valor padrão eexample_name_case()
retornaráEXAMPLE_NAME_NOT_SET
.
- Nada vai mudar se o caso não for
void set_allocated_foo(Bar* bar)
:- Chama
clear_example_name()
. - Se o ponteiro
Bar
não forNULL
: define o objetoBar
como o campo e define o único caso comokFoo
. A mensagem assume a propriedade do objetoBar
alocado, has_foo() retornará verdadeiro e example_name_case() retornarákFoo
. - Se o ponteiro for
NULL
,has_foo()
retornaráfalse
eexample_name_case()
retornaráEXAMPLE_NAME_NOT_SET
. O comportamento é como chamarclear_example_name()
.
- Chama
Bar* release_foo()
:- Retornará
NULL
se um dos casos não forkFoo
. - Se um caso for
kFoo
, limpará o caso um, libera a propriedade do campo e retorna o ponteiro do objetoBar
. Depois de chamar isso, o autor da chamada assume a propriedade do objetoBar
alocado,has_foo()
retornaráfalse
,foo()
retornará o valor padrão eexample_name_case()
retornaráEXAMPLE_NAME_NOT_SET
.
- Retornará
Campos do mapa
Para esta definição de campo do mapa:
map<int32, int32> weight = 1;
O compilador vai gerar os seguintes métodos de acessador:
const google::protobuf::Map<int32, int32>& weight();
: retorna umMap
imutável.google::protobuf::Map<int32, int32>* mutable_weight();
: retorna umMap
mutável.
Um google::protobuf::Map
é um tipo de contêiner especial usado em buffers de protocolo para armazenar campos do mapa. Como você pode ver na interface abaixo, ela usa um subconjunto usado com frequência nos métodos std::map
e 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); }
A maneira mais fácil de adicionar dados é usar a sintaxe de mapa normal, por exemplo:
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)
causa uma cópia profunda da instância value_type
implicitamente. A maneira mais eficiente de inserir um novo valor em um google::protobuf::Map
é a seguinte:
T& operator[](const Key& key): map[new_key] = new_mapped;
Como usar google::protobuf::Map
com mapas padrão
google::protobuf::Map
oferece suporte à mesma API de iterador que std::map
e std::unordered_map
. Se você não quiser usar o google::protobuf::Map
diretamente, converta um google::protobuf::Map
em um mapa padrão da seguinte maneira:
std::map<int32, int32> standard_map(message.weight().begin(), message.weight().end());
Isso vai fazer uma cópia detalhada de todo o mapa.
Também é possível construir um google::protobuf::Map
a partir de um mapa padrão da seguinte maneira:
google::protobuf::Map<int32, int32> weight(standard_map.begin(), standard_map.end());
Como analisar valores desconhecidos
Na transmissão, um mapa .proto é equivalente a uma mensagem de entrada de mapa para cada par de chave-valor, enquanto o próprio mapa é um campo repetido de entradas de mapa. Assim como os tipos de mensagens comuns, é possível que uma mensagem de entrada de mapa analisada tenha campos desconhecidos, por exemplo, um campo do tipo int64
em um mapa definido como map<int32, string>
.
Se houver campos desconhecidos no formato de transmissão de uma mensagem de entrada no mapa, eles serão descartados.
Se houver um valor de enumeração desconhecido no formato de transmissão de uma mensagem de entrada do mapa, ele será processado de forma diferente em proto2 e proto3. No proto2, toda a mensagem de entrada do mapa é colocada no conjunto de campos desconhecido da mensagem contida. No proto3, ele é colocado em um campo de mapa como se fosse um valor de enumeração conhecido.
Qualquer
Dado um campo Any
como este:
import "google/protobuf/any.proto"; message ErrorStatus { string message = 1; google.protobuf.Any details = 2; }
No código gerado, o getter para o campo details
retorna uma instância de google::protobuf::Any
. Isso fornece os seguintes métodos especiais para empacotar e descompactar os valores de 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
Considere uma das seguintes definições:oneof example_name { int32 foo_int = 4; string foo_string = 9; ... }
O compilador gerará o seguinte tipo de enumeração C++:
enum ExampleNameCase { kFooInt = 4, kFooString = 9, EXAMPLE_NAME_NOT_SET = 0 }
Além disso, ele gera estes métodos:
ExampleNameCase example_name_case() const
: retorna a enumeração indicando qual campo está definido. RetornaráEXAMPLE_NAME_NOT_SET
se nenhum deles estiver definido.void clear_example_name()
: libera o objeto se o conjunto de campos oneof usar um ponteiro (mensagem ou string) e define o caso oneof comoEXAMPLE_NAME_NOT_SET
.
Enumerações
Considerando uma definição de enumeração como:
enum Foo { VALUE_A = 0; VALUE_B = 5; VALUE_C = 1234; }
O compilador de buffer de protocolo gerará um tipo de enumeração C++ chamado Foo
com o mesmo conjunto de valores. Além disso, o compilador gerará as seguintes funções:
const EnumDescriptor* Foo_descriptor()
: retorna o descritor do tipo, que contém informações sobre quais valores esse tipo de enumeração define.bool Foo_IsValid(int value)
: retornatrue
se o valor numérico fornecido corresponder a um dos valores definidos deFoo
. No exemplo acima, ele retornariatrue
se a entrada fosse 0, 5 ou 1234.const string& Foo_Name(int value)
: retorna o nome de determinado valor numérico. Retorna uma string vazia se esse valor não existir. Se esse campo tiver vários valores, o primeiro definido será retornado. No exemplo acima,Foo_Name(5)
retornaria"VALUE_B"
.bool Foo_Parse(const string& name, Foo* value)
: sename
for um nome de valor válido para essa enumeração, atribui esse valor avalue
e retorna verdadeiro. Caso contrário, retorna "false". No exemplo acima,Foo_Parse("VALUE_C", &some_foo)
retorna "true" e definesome_foo
como 1234.const Foo Foo_MIN
: o menor valor válido da enumeração (VALUE_A no exemplo).const Foo Foo_MAX
: o maior valor válido da enumeração (VALUE_C no exemplo).const int Foo_ARRAYSIZE
: sempre definido comoFoo_MAX + 1
.
Cuidado ao converter números inteiros em enumerações proto2. Se um número inteiro for transmitido para um valor de enumeração proto2, ele precisará ser um dos valores válidos para esse tipo. Caso contrário, os resultados poderão ser indefinidos. Em caso de dúvida, use a função Foo_IsValid()
gerada para testar se a transmissão é válida. Definir um campo do tipo enum de uma mensagem proto2 como um valor inválido pode causar uma falha na declaração. Se um valor de enumeração inválido for lido ao analisar uma mensagem proto2, ele será tratado como um campo desconhecido. Essas semânticas foram alteradas no proto3. É seguro converter qualquer número inteiro para um valor de enumeração proto3, desde que ele se encaixe em int32. Valores de enumeração inválidos também serão mantidos ao analisar uma mensagem proto3 e retornados por acessadores de campo de enumeração.
Cuidado ao usar enumerações proto3 em instruções de mudança. Os tipos enumerados de Proto3 são tipos de enum abertos com possíveis valores fora do intervalo de símbolos especificados. Os valores de enumeração não reconhecidos serão mantidos ao analisar uma mensagem proto3 e retornados pelos acessadores do campo enum. Uma instrução switch em uma enumeração proto3 sem um caso padrão não será capaz de capturar todos os casos, mesmo que todos os campos conhecidos estejam listados. Isso pode causar um comportamento inesperado, incluindo corrupção de dados e falhas de execução. Sempre adicione um caso padrão ou chame Foo_IsValid(int)
explicitamente fora da chave para processar valores de enumeração desconhecidos.
É possível definir uma enumeração dentro de um tipo de mensagem. Nesse caso, o compilador de buffer de protocolo gera um código que faz parecer que o próprio tipo de enumeração foi declarado aninhado dentro da classe da mensagem. As funções Foo_descriptor()
e Foo_IsValid()
são declaradas como métodos estáticos. Na realidade, o tipo de enumeração em si e seus valores são declarados no escopo global com nomes corrompidos e importados para o escopo da classe com um typedef e uma série de definições constantes. Isso é feito apenas para contornar problemas com a ordem da declaração. Não dependa de nomes de nível superior corrompidos. Imagine que a enumeração realmente está aninhada na classe da mensagem.
Extensões (somente proto2)
Você recebeu uma mensagem com um intervalo de extensões:
message Foo { extensions 100 to 199; }
O compilador de buffer de protocolo gerará alguns métodos adicionais para Foo
: HasExtension()
, ExtensionSize()
, ClearExtension()
, GetExtension()
, SetExtension()
, MutableExtension()
, AddExtension()
, SetAllocatedExtension()
e ReleaseExtension()
. Cada um desses métodos usa, como seu primeiro parâmetro, um identificador de extensão (descrito abaixo), que identifica um campo de extensão. Os parâmetros restantes e o valor de retorno são exatamente os mesmos dos métodos do acessador correspondentes que seriam gerados para um campo normal (sem extensão) do mesmo tipo do identificador da extensão. GetExtension()
corresponde aos acessadores sem prefixo especial.
Com uma definição de extensão:
extend Foo { optional int32 bar = 123; repeated int32 repeated_bar = 124; }
Para o campo de extensão singular bar
, o compilador de buffer de protocolo gera um "identificador de extensão" chamado bar
, que você pode usar com os acessadores de extensão de Foo
para acessar esta extensão, desta maneira:
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));
Da mesma forma, para o campo de extensão repetido repeated_bar
, o compilador gera um identificador de extensão chamado repeated_bar
, que também pode ser usado com os acessadores de extensão de Foo
:
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) }
A implementação exata dos identificadores de extensão é complicada e envolve o uso mágico de modelos. No entanto, você não precisa se preocupar com como os identificadores de extensão funcionam para usá-los.
As extensões podem ser declaradas aninhadas em outro tipo. Por exemplo, um padrão comum é fazer isto:
message Baz { extend Foo { optional Baz foo_ext = 124; } }
Nesse caso, o identificador de extensão foo_ext
é declarado aninhado dentro de Baz
. Ele pode ser usado da seguinte maneira:
Foo foo; Baz* baz = foo.MutableExtension(Baz::foo_ext); FillInMyBaz(baz);
Alocação de arena
A alocação de arena é um recurso somente C++ que ajuda a otimizar o uso de memória e melhorar o desempenho ao trabalhar com buffers de protocolo. A ativação da alocação de arena no .proto
adiciona mais código para trabalhar com arenas ao código gerado em C++. Saiba mais sobre a API de alocação de arena no Guia de alocação de área.
Serviços
Se o arquivo .proto
contiver a seguinte linha:
option cc_generic_services = true;
Em seguida, o compilador de buffer de protocolo gerará o código com base nas definições de serviço encontradas no arquivo, conforme descrito nesta seção. No entanto, o código gerado pode ser indesejável, porque não está vinculado a nenhum sistema RPC específico e, portanto, exige mais níveis de indireção do que o código personalizado para um sistema. Se você NÃO quiser que esse código seja gerado, adicione esta linha ao arquivo:
option cc_generic_services = false;
Se nenhuma das linhas acima for fornecida, a opção padrão será false
, já que o uso de serviços genéricos foi descontinuado.
Observe que, antes de 2.4.0, a opção padrão é true
.
Os sistemas de RPC baseados em definições de serviço da linguagem .proto
precisam fornecer plug-ins para gerar o código apropriado para o sistema. Esses plug-ins provavelmente exigem
que os serviços abstratos sejam desativados para que possam gerar as próprias
classes com os mesmos nomes.
O restante desta seção descreve o que o compilador de buffer de protocolo gera quando os serviços abstratos são ativados.
Interface
Com uma definição de serviço:
service Foo { rpc Bar(FooRequest) returns(FooResponse); }
O compilador de buffer de protocolo gerará uma classe Foo
para representar esse serviço. O Foo
vai ter um método virtual para cada método na definição do serviço. Nesse caso, o método Bar
é definido como:
virtual void Bar(RpcController* controller, const FooRequest* request, FooResponse* response, Closure* done);
Os parâmetros são equivalentes aos parâmetros de Service::CallMethod()
, exceto que o argumento method
está implícito e que request
e response
especificam o tipo exato deles.
Esses métodos gerados são virtuais, mas não puramente virtuais. As implementações padrão simplesmente chamam controller->SetFailed()
com uma mensagem de erro indicando que o método não foi implementado e, em seguida, invocam o callback done
. Ao implementar seu próprio serviço, você precisa criar uma subclasse para o serviço gerado e implementar os métodos dele, conforme apropriado.
Foo
cria uma subclasse da interface Service
. O compilador de buffer de protocolo gera automaticamente implementações dos métodos de Service
da seguinte maneira:
GetDescriptor
: retorna oServiceDescriptor
do serviço.CallMethod
: determina qual método está sendo chamado com base no descritor de método fornecido e o chama diretamente, transmitindo os objetos de mensagens de solicitação e resposta para os tipos corretos.GetRequestPrototype
eGetResponsePrototype
: retorna a instância padrão da solicitação ou resposta do tipo correto do método especificado.
O seguinte método estático também é gerado:
static ServiceDescriptor descriptor()
: retorna o descritor do tipo, que contém informações sobre quais métodos esse serviço tem e quais são os tipos de entrada e saída.
Stub
O compilador de buffer de protocolo também gera uma implementação de stub de cada interface de serviço, que é usada por clientes que querem enviar solicitações para servidores que implementam o serviço. Para o serviço Foo
(acima), a implementação de stub Foo_Stub
será definida. Assim como nos tipos de mensagens aninhadas, um typedef é usado para que Foo_Stub
também possa ser chamado de Foo::Stub
.
A Foo_Stub
é uma subclasse da Foo
que também implementa os métodos abaixo:
Foo_Stub(RpcChannel* channel)
: cria um novo stub que envia solicitações sobre o canal.Foo_Stub(RpcChannel* channel, ChannelOwnership ownership)
: cria um novo stub que envia solicitações sobre o canal fornecido e possivelmente é o proprietário do canal. Seownership
forService::STUB_OWNS_CHANNEL
, quando o objeto de stub for excluído, o canal também será excluído.RpcChannel* channel()
: retorna o canal do stub, conforme transmitido ao construtor.
O stub também implementa cada um dos métodos do serviço como um wrapper em torno do canal. Chamar um dos métodos simplesmente chama channel->CallMethod()
.
A biblioteca de buffers de protocolo não inclui uma implementação de RPC. No entanto, ele inclui todas as ferramentas necessárias para conectar uma classe de serviço gerada a qualquer implementação de RPC arbitrária. Você só precisa fornecer implementações de RpcChannel
e RpcController
. Consulte a documentação de service.h
para mais informações.
Pontos de inserção do plug-in
Os plug-ins de gerador de código, que querem estender a saída do gerador de código C++, podem inserir o código dos tipos abaixo usando os nomes dos pontos de inserção fornecidos. Cada ponto de inserção aparece nos arquivos .pb.cc
e .pb.h
, a menos que especificado de outra forma.
includes
: inclui diretivas.namespace_scope
: declarações que pertencem ao pacote/namespace do arquivo, mas não em uma classe específica. Aparece após todos os outros códigos de escopo de namespace.global_scope
: declarações que pertencem ao nível superior, fora do namespace do arquivo. Aparece no final do arquivo.class_scope:TYPENAME
: declarações de membro que pertencem a uma classe de mensagem.TYPENAME
é o nome completo do proto. Por exemplo,package.MessageType
. É exibido depois de todas as outras declarações públicas na classe. Esse ponto de inserção só aparece no arquivo.pb.h
.
Não gere código que dependa de membros de classe privada declarados pelo gerador de código padrão, porque esses detalhes de implementação podem mudar em versões futuras dos buffers de protocolo.