Veja também: Versão C++: Como lidar com tipos oneof/união em C++
NanoPB é uma implementação de Protocol Buffers otimizada para tamanho de código, destinada a sistemas embarcados. Este post mostra como lidar com tipos oneof (união) em C com NanoPB.
Definição Proto
Primeiro, crie um arquivo .proto com campos oneof:
syntax = "proto3";
package example;
message OneofMessage {
oneof value {
uint32 int_value = 1;
float float_value = 2;
string string_value = 3;
}
}Gerar código NanoPB
Gere o código NanoPB com um arquivo .options para especificar os tamanhos dos buffers de string:
Crie oneof.options:
example.OneofMessage.string_value max_size:32Depois gere:
protoc --nanopb_out=. oneof.protoIsso irá gerar oneof.pb.h e oneof.pb.c.
Exemplo em C
Aqui está um exemplo completo em C lidando com campos oneof:
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "oneof.pb.h"
#include "pb_encode.h"
#include "pb_decode.h"
int main() {
// Buffer para a mensagem codificada
uint8_t buffer[128];
size_t message_length;
// --- CODIFICAÇÃO (int_value) ---
example_OneofMessage message = example_OneofMessage_init_zero;
// Define o campo oneof (apenas um pode ser definido por vez)
message.which_value = example_OneofMessage_int_value_tag;
message.value.int_value = 42;
// Cria stream para codificação
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
// Codifica a mensagem
if (!pb_encode(&ostream, example_OneofMessage_fields, &message)) {
printf("Encoding failed: %s\n", PB_GET_ERROR(&ostream));
return 1;
}
message_length = ostream.bytes_written;
printf("Encoded %zu bytes (int_value)\n", message_length);
// Imprime hex dump
printf("Encoded data: ");
for (size_t i = 0; i < message_length; i++) {
printf("%02x ", buffer[i]);
}
printf("\n");
// --- DECODIFICAÇÃO ---
example_OneofMessage decoded = example_OneofMessage_init_zero;
// Cria stream para decodificação
pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
// Decodifica a mensagem
if (!pb_decode(&istream, example_OneofMessage_fields, &decoded)) {
printf("Decoding failed: %s\n", PB_GET_ERROR(&istream));
return 1;
}
// Imprime o valor decodificado
printf("Decoded value:\n");
switch (decoded.which_value) {
case example_OneofMessage_int_value_tag:
printf(" int_value: %u\n", (unsigned int)decoded.value.int_value);
break;
case example_OneofMessage_float_value_tag:
printf(" float_value: %f\n", decoded.value.float_value);
break;
case example_OneofMessage_string_value_tag:
printf(" string_value: %s\n", decoded.value.string_value);
break;
default:
printf(" (none)\n");
break;
}
return 0;
}Comando de compilação
Compile o exemplo com nanopb. NanoPB é tipicamente usado incluindo os arquivos fonte diretamente no seu projeto:
gcc -o oneof_example oneof_example.c oneof.pb.c pb_common.c pb_encode.c pb_decode.c -I.Nota: Os arquivos fonte do NanoPB (pb_common.c, pb_encode.c, pb_decode.c) precisam ser compilados diretamente com seu projeto. Você pode obtê-los no repositório GitHub do NanoPB.
Script de teste em Python
Para verificar a codificação, você pode usar a biblioteca protobuf do Python:
import oneof_pb2
# Lê os dados binários
with open('encoded.bin', 'rb') as f:
data = f.read()
# Decodifica
msg = oneof_pb2.OneofMessage()
msg.ParseFromString(data)
print("Python decoded values:")
print(f" Which oneof: {msg.WhichOneof('value')}")
if msg.HasField('int_value'):
print(f" int_value: {msg.int_value}")
if msg.HasField('float_value'):
print(f" float_value: {msg.float_value}")
if msg.HasField('string_value'):
print(f" string_value: {msg.string_value}")Primeiro, compile as definições protobuf do Python:
protoc --python_out=. oneof.protoDepois modifique o exemplo em C para salvar os dados codificados em um arquivo:
// Após a codificação, adicione isto:
FILE *f = fopen("encoded.bin", "wb");
fwrite(buffer, 1, message_length, f);
fclose(f);Exemplo com todos os tipos oneof
Aqui está um exemplo mostrando todos os tipos oneof:
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "oneof.pb.h"
#include "pb_encode.h"
#include "pb_decode.h"
void encode_decode_int_value() {
uint8_t buffer[128];
size_t message_length;
example_OneofMessage message = example_OneofMessage_init_zero;
message.which_value = example_OneofMessage_int_value_tag;
message.value.int_value = 42;
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
if (!pb_encode(&ostream, example_OneofMessage_fields, &message)) {
printf("Encoding failed\n");
return;
}
message_length = ostream.bytes_written;
example_OneofMessage decoded = example_OneofMessage_init_zero;
pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
if (!pb_decode(&istream, example_OneofMessage_fields, &decoded)) {
printf("Decoding failed\n");
return;
}
printf("int_value: %u\n", (unsigned int)decoded.value.int_value);
}
void encode_decode_float_value() {
uint8_t buffer[128];
size_t message_length;
example_OneofMessage message = example_OneofMessage_init_zero;
message.which_value = example_OneofMessage_float_value_tag;
message.value.float_value = 3.14159f;
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
if (!pb_encode(&ostream, example_OneofMessage_fields, &message)) {
printf("Encoding failed\n");
return;
}
message_length = ostream.bytes_written;
example_OneofMessage decoded = example_OneofMessage_init_zero;
pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
if (!pb_decode(&istream, example_OneofMessage_fields, &decoded)) {
printf("Decoding failed\n");
return;
}
printf("float_value: %f\n", decoded.value.float_value);
}
void encode_decode_string_value() {
uint8_t buffer[128];
size_t message_length;
example_OneofMessage message = example_OneofMessage_init_zero;
message.which_value = example_OneofMessage_string_value_tag;
strncpy(message.value.string_value, "Hello", sizeof(message.value.string_value) - 1);
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
if (!pb_encode(&ostream, example_OneofMessage_fields, &message)) {
printf("Encoding failed\n");
return;
}
message_length = ostream.bytes_written;
example_OneofMessage decoded = example_OneofMessage_init_zero;
pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
if (!pb_decode(&istream, example_OneofMessage_fields, &decoded)) {
printf("Decoding failed\n");
return;
}
printf("string_value: %s\n", decoded.value.string_value);
}
int main() {
printf("Testing all oneof types:\n");
encode_decode_int_value();
encode_decode_float_value();
encode_decode_string_value();
return 0;
}Pontos principais
- Definição de oneof: Use a palavra-chave
oneofno proto para definir tipos união - which_field gerado: NanoPB gera o enum
which_fieldnamepara rastrear qual campo está definido - Codificação: Defina
which_*com a tag apropriada, depois defina o valor do campo - Decodificação: Verifique
which_*para determinar qual campo foi definido - Mutuamente exclusivo: Apenas um campo em um oneof pode ser definido por vez
- Semântica de união: Similar às unions do C, mas com segurança de tipo
- Eficiência de memória: Apenas a memória para um campo é alocada
Quando usar oneof
- Quando apenas um de vários valores possíveis deve estar presente
- Quando você quer economizar memória não alocando espaço para todos os campos
- Quando você precisa de uniões com segurança de tipo
- Quando estiver representando tipos variantes ou uniões marcadas
- Quando estiver implementando mensagens polimórficas
Saída esperada
Encoded 2 bytes (int_value)
Encoded data: 08 2a
Decoded value:
int_value: 42Saída esperada (todos os tipos)
Testing all oneof types:
int_value: 42
float_value: 3.141590
string_value: HelloDiferenças do C++
A versão em C é quase idêntica à versão em C++:
- Use cast explícito para especificadores de formato printf em C
- Não são necessários recursos específicos de C++ para lidar com oneof
- O código gerado é idêntico para ambas as linguagens
- O gerenciamento de memória é o mesmo (alocação estática em ambos os casos)
Mais posts sobre NanoPB
- Basic scalar types in C++
- Basic scalar types in C
- String types in C++
- String types in C
- Bytes types in C++
- Bytes types in C
- Optional fields in C++
- Optional fields in C
- Repeated fields/arrays in C++
- Repeated fields/arrays in C
- Enums in C++
- Enums in C
- Nested messages in C++
- Nested messages in C
- Oneof/union types in C++
- Custom array converters in C++
- Custom array converters in C