NanoPB: Cómo manejar tipos oneof (unión) en C++
Ver también: Versión en C: Cómo manejar tipos oneof/unión en C
NanoPB es una implementación de Protocol Buffers optimizada para el tamaño de código en sistemas embebidos. Esta entrada muestra cómo manejar tipos oneof (unión) en C++ con NanoPB.
Definición Proto
Primero, crea un archivo .proto con campos oneof:
syntax = "proto3";
package example;
message OneofMessage {
oneof value {
uint32 int_value = 1;
float float_value = 2;
string string_value = 3;
}
}Generar código NanoPB
Genera el código NanoPB con un archivo .options para especificar los tamaños de búfer de cadena:
Crea oneof.options:
example.OneofMessage.string_value max_size:32Luego genera:
protoc --nanopb_out=. oneof.protoEsto generará oneof.pb.h y oneof.pb.c.
Ejemplo en C++
Aquí tienes un ejemplo completo en C++ que maneja campos oneof:
#include <stdio.h>
#include <string.h>
#include "oneof.pb.h"
#include "pb_encode.h"
#include "pb_decode.h"
int main() {
// Búfer para el mensaje codificado
uint8_t buffer[128];
size_t message_length;
// --- CODIFICACIÓN (int_value) ---
example_OneofMessage message = example_OneofMessage_init_zero;
// Establecer campo oneof (solo uno puede establecerse a la vez)
message.which_value = example_OneofMessage_int_value_tag;
message.value.int_value = 42;
// Crear flujo para codificación
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
// Codificar el mensaje
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);
// Imprimir volcado hexadecimal
printf("Encoded data: ");
for (size_t i = 0; i < message_length; i++) {
printf("%02x ", buffer[i]);
}
printf("\n");
// --- DECODIFICACIÓN ---
example_OneofMessage decoded = example_OneofMessage_init_zero;
// Crear flujo para decodificación
pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
// Decodificar el mensaje
if (!pb_decode(&istream, example_OneofMessage_fields, &decoded)) {
printf("Decoding failed: %s\n", PB_GET_ERROR(&istream));
return 1;
}
// Imprimir valor decodificado
printf("Decoded value:\n");
switch (decoded.which_value) {
case example_OneofMessage_int_value_tag:
printf(" int_value: %u\n", 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 compilación
Compila el ejemplo con nanopb. NanoPB se usa típicamente incluyendo los archivos fuente directamente en tu proyecto:
g++ -o oneof_example oneof_example.cpp oneof.pb.c pb_common.c pb_encode.c pb_decode.c -I.Nota: Los archivos fuente de NanoPB (pb_common.c, pb_encode.c, pb_decode.c) deben compilarse directamente con tu proyecto. Puedes obtenerlos del repositorio de GitHub de NanoPB.
Script de prueba en Python
Para verificar la codificación, puedes usar la biblioteca protobuf de Python:
import oneof_pb2
# Leer los datos binarios
with open('encoded.bin', 'rb') as f:
data = f.read()
# Decodificar
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}")Primero, compila las definiciones protobuf de Python:
protoc --python_out=. oneof.protoLuego modifica el ejemplo en C++ para guardar los datos codificados en un archivo:
// Después de codificar, añade esto:
FILE *f = fopen("encoded.bin", "wb");
fwrite(buffer, 1, message_length, f);
fclose(f);Ejemplo con todos los tipos oneof
Aquí tienes un ejemplo que muestra todos los tipos oneof:
#include <stdio.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", 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;
}Puntos clave
- Definición oneof: Usa la palabra clave
oneofen proto para definir tipos unión - which_field generado: NanoPB genera un enum
which_fieldnamepara rastrear qué campo está establecido - Codificación: Establece
which_*con el tag apropiado, luego establece el valor del campo - Decodificación: Verifica
which_*para determinar qué campo se estableció - Excluyentes mutuamente: Solo un campo en un oneof puede establecerse a la vez
- Semántica de unión: Similar a las uniones de C pero con seguridad de tipos
- Eficiencia de memoria: Solo se asigna memoria para un campo
Cuándo usar oneof
- Cuando solo uno de varios valores posibles debe estar presente
- Cuando quieres ahorrar memoria no asignando espacio para todos los campos
- Cuando necesitas uniones con seguridad de tipos
- Cuando representas tipos variantes o uniones etiquetadas
- Cuando implementas mensajes polimórficos
Salida esperada
Encoded 2 bytes (int_value)
Encoded data: 08 2a
Decoded value:
int_value: 42Salida esperada (todos los tipos)
Testing all oneof types:
int_value: 42
float_value: 3.141590
string_value: HelloMás entradas de NanoPB
- Tipos escalares básicos en C++
- Tipos escalares básicos en C
- Tipos de cadena en C++
- Tipos de cadena en C
- Tipos bytes en C++
- Tipos bytes en C
- Campos opcionales en C++
- Campos opcionales en C
- Campos repetidos (arreglos) en C++
- Campos repetidos (arreglos) en C
- Enums en C++
- Enums en C
- Mensajes anidados en C++
- Mensajes anidados en C
- Tipos oneof/unión en C
- Convertidores de arreglo personalizados en C++
- Convertidores de arreglo personalizados en C