OBSERVAÇÃO:este site foi descontinuado. O site será desativado após 31 de janeiro de 2023, e o tráfego será redirecionado para o novo site em https://protobuf.dev. Enquanto isso, as atualizações serão feitas apenas para protobuf.dev.

Código gerado em C++

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

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 de Foo que é idêntica a uma instância recém-construída de Foo. 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étodo New().

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: retorna true 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, e foo() retornará value.
  • void clear_foo(): limpa o valor do campo. Depois de chamar isso, has_foo() retornará false, e foo() 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: retorna true 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, e foo() retornará uma cópia de value.
  • 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, e foo() retornará uma cópia de value.
  • 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, e foo() retornará uma cópia de value.
  • 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 objeto string 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, e foo() 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, e foo() retornará o valor padrão.
  • void set_allocated_foo(string* value): define o objeto string para o campo e libera o valor de campo anterior, se ele existir. Se o ponteiro string não for NULL, a mensagem assumirá a propriedade do objeto string alocado, e has_foo() retornará true. A mensagem pode excluir o objeto string alocado a qualquer momento. Portanto, as referências ao objeto podem ser invalidadas. Caso contrário, se value for NULL, o comportamento será o mesmo que chamar clear_foo().
  • string* release_foo(): libera a propriedade do campo e retorna o ponteiro do objeto string. Depois de chamar isso, o autor da chamada assume a propriedade do objeto string alocado, has_foo() retorna false e foo() 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 da value.
  • 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 da value.
  • 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 da value.
  • 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 objeto string 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 objeto string para o campo e libera o valor de campo anterior, se ele existir. Se o ponteiro string não for NULL, a mensagem assumirá a propriedade do objeto string alocado. A mensagem pode excluir o objeto string alocado a qualquer momento. Portanto, as referências ao objeto podem ser invalidadas. Caso contrário, se value for NULL, o comportamento será o mesmo que chamar clear_foo().
  • string* release_foo(): libera a propriedade do campo e retorna o ponteiro do objeto string. Depois de chamar isso, o autor da chamada assume a propriedade do objeto string alocado e foo() 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: retorna true 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, e foo() retornará value. 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 para Bar.
  • void clear_foo(): limpa o valor do campo. Depois de chamar isso, has_foo() retornará false, e foo() 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: retorna true se o campo estiver definido.
  • const Bar& foo() const: retorna o valor atual do campo. Se o campo não for definido, retornará um Bar sem nenhum dos campos definidos (possivelmente Bar::default_instance()).
  • Bar* mutable_foo(): retorna um ponteiro para o objeto Bar mutável que armazena o valor do campo. Se o campo não tiver sido definido antes da chamada, o Bar retornado não terá nenhum dos campos definidos (ou seja, será idêntico a um Bar recém-alocado). Depois de chamar isso, has_foo() retornará true e foo() retornará uma referência para a mesma instância de Bar.
  • void clear_foo(): limpa o valor do campo. Depois de chamar isso, has_foo() retornará false, e foo() retornará o valor padrão.
  • void set_allocated_foo(Bar* bar): define o objeto Bar para o campo e libera o valor de campo anterior, se ele existir. Se o ponteiro Bar não for NULL, a mensagem assumirá a propriedade do objeto Bar alocado, e has_foo() retornará true. Caso contrário, se Bar for NULL, o comportamento será o mesmo que chamar clear_foo().
  • Bar* release_foo(): libera a propriedade do campo e retorna o ponteiro do objeto Bar. Depois de chamar isso, o autor da chamada assume a propriedade do objeto Bar alocado, has_foo() retorna false e foo() 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 o RepeatedField 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 o RepeatedField 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 objeto string 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 o RepeatedPtrField 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 o RepeatedPtrField 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 se value não corresponder a nenhum dos valores definidos para Bar.
  • 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 se value não corresponder a nenhum dos valores definidos para Bar.
  • void clear_foo(): remove todos os elementos do campo. Depois de chamar isso, foo_size() retornará zero.
  • const RepeatedField<int>& foo() const: retorna o RepeatedField 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 o RepeatedField 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 objeto Bar 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. O Bar retornado é mutável e não terá nenhum dos campos definido (ou seja, será idêntico a um Bar 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 o RepeatedPtrField 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 o RepeatedPtrField 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): retorna true se um dos casos for kFoo.
  • int32 foo() const: retorna o valor atual do campo se um dos casos for kFoo. 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, e example_name_case() retornará kFoo.
  • 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 e example_name_case() retornará EXAMPLE_NAME_NOT_SET.

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: retorna true se o único caso for kFoo.
  • const string& foo() const: retornará o valor atual do campo se o caso único for kFoo. 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 de value e example_name_case() retornará kFoo.
  • 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 de value e example_name_case() retornará kFoo.
  • 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 sido kFoo 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 e example_name_case() retornará kFoo.
  • 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, e example_name_case() retornará EXAMPLE_NAME_NOT_SET.
  • 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 como kFoo. A mensagem assume a propriedade do objeto de string alocado, has_foo() retornará true e example_name_case() retornará kFoo.
    • Se o ponteiro de string for NULL, has_foo() retornará false e example_name_case() retornará EXAMPLE_NAME_NOT_SET.
  • string* release_foo():
    • Retornará NULL se um dos casos não for kFoo.
    • 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 e example_name_case() retornará EXAMPLE_NAME_NOT_SET.

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): retorna true se um dos casos for kFoo.
  • Bar foo() const: retorna o valor atual do campo se um dos casos for kFoo. 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 e example_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 para Bar.
  • 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 e example_name_case() retornará EXAMPLE_NAME_NOT_SET.

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 for kFoo.
  • const Bar& foo() const: retorna o valor atual do campo se um dos casos for kFoo. Caso contrário, retorna Bar::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 era kFoo 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 de Bar e example_name_case() retornará kFoo.
  • 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 e example_name_case() retornará EXAMPLE_NAME_NOT_SET.
  • void set_allocated_foo(Bar* bar):
    • Chama clear_example_name().
    • Se o ponteiro Bar não for NULL: define o objeto Bar como o campo e define o único caso como kFoo. A mensagem assume a propriedade do objeto Bar alocado, has_foo() retornará verdadeiro e example_name_case() retornará kFoo.
    • Se o ponteiro for NULL, has_foo() retornará false e example_name_case() retornará EXAMPLE_NAME_NOT_SET. O comportamento é como chamar clear_example_name().
  • Bar* release_foo():
    • Retornará NULL se um dos casos não for kFoo.
    • Se um caso for kFoo, limpará o caso um, libera a propriedade do campo e retorna o ponteiro do objeto Bar. Depois de chamar isso, o autor da chamada assume a propriedade do objeto Bar alocado, has_foo() retornará false, foo() retornará o valor padrão e example_name_case() retornará EXAMPLE_NAME_NOT_SET.

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 um Map imutável.
  • google::protobuf::Map<int32, int32>* mutable_weight();: retorna um Map 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 como EXAMPLE_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): retorna true se o valor numérico fornecido corresponder a um dos valores definidos de Foo. No exemplo acima, ele retornaria true 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): se name for um nome de valor válido para essa enumeração, atribui esse valor a value e retorna verdadeiro. Caso contrário, retorna "false". No exemplo acima, Foo_Parse("VALUE_C", &some_foo) retorna "true" e define some_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 como Foo_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 o ServiceDescriptor 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 e GetResponsePrototype: 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. Se ownership for Service::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.