NanoPB: Gérer les types oneof (union) en C++
Voir aussi : Version C : Comment gérer les types oneof/union en C
NanoPB est une implémentation de Protocol Buffers optimisée pour la taille du code, destinée aux systèmes embarqués. Cet article montre comment gérer les types oneof (union) en C++ avec NanoPB.
Définition Proto
Créez d’abord un fichier .proto avec des champs oneof :
syntax = "proto3";
package example;
message OneofMessage {
oneof value {
uint32 int_value = 1;
float float_value = 2;
string string_value = 3;
}
}Génération du code NanoPB
Générez le code NanoPB avec un fichier .options pour spécifier les tailles de tampon de chaîne :
Créez oneof.options :
example.OneofMessage.string_value max_size:32Puis générez :
protoc --nanopb_out=. oneof.protoCela générera oneof.pb.h et oneof.pb.c.
Exemple C++
Voici un exemple C++ complet gérant les champs oneof :
#include <stdio.h>
#include <string.h>
#include "oneof.pb.h"
#include "pb_encode.h"
#include "pb_decode.h"
int main() {
// Tampon pour le message encodé
uint8_t buffer[128];
size_t message_length;
// --- ENCODAGE (int_value) ---
example_OneofMessage message = example_OneofMessage_init_zero;
// Définir le champ oneof (un seul peut être défini à la fois)
message.which_value = example_OneofMessage_int_value_tag;
message.value.int_value = 42;
// Créer le flux pour l'encodage
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
// Encoder le message
if (!pb_encode(&ostream, example_OneofMessage_fields, &message)) {
printf("Échec de l'encodage : %s\n", PB_GET_ERROR(&ostream));
return 1;
}
message_length = ostream.bytes_written;
printf("Encodé %zu octets (int_value)\n", message_length);
// Afficher le dump hexadécimal
printf("Données encodées : ");
for (size_t i = 0; i < message_length; i++) {
printf("%02x ", buffer[i]);
}
printf("\n");
// --- DÉCODAGE ---
example_OneofMessage decoded = example_OneofMessage_init_zero;
// Créer le flux pour le décodage
pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
// Décoder le message
if (!pb_decode(&istream, example_OneofMessage_fields, &decoded)) {
printf("Échec du décodage : %s\n", PB_GET_ERROR(&istream));
return 1;
}
// Afficher la valeur décodée
printf("Valeur décodée :\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(" (aucun)\n");
break;
}
return 0;
}Commande de compilation
Compilez l’exemple avec nanopb. NanoPB est généralement utilisé en incluant directement les fichiers sources dans votre projet :
g++ -o oneof_example oneof_example.cpp oneof.pb.c pb_common.c pb_encode.c pb_decode.c -I.Note : Les fichiers sources NanoPB (pb_common.c, pb_encode.c, pb_decode.c) doivent être compilés directement avec votre projet. Vous pouvez les obtenir depuis le dépôt GitHub de NanoPB.
Script de test Python
Pour vérifier l’encodage, vous pouvez utiliser la bibliothèque protobuf de Python :
import oneof_pb2
# Lire les données binaires
with open('encoded.bin', 'rb') as f:
data = f.read()
# Décoder
msg = oneof_pb2.OneofMessage()
msg.ParseFromString(data)
print("Valeurs décodées par Python :")
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}")D’abord, compilez les définitions protobuf Python :
protoc --python_out=. oneof.protoModifiez ensuite l’exemple C++ pour enregistrer les données encodées dans un fichier :
// Après l'encodage, ajoutez ceci :
FILE *f = fopen("encoded.bin', "wb");
fwrite(buffer, 1, message_length, f);
fclose(f);Exemple avec tous les types oneof
Voici un exemple montrant tous les types 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("Échec de l'encodage\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("Échec du décodage\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("Échec de l'encodage\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("Échec du décodage\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("Échec de l'encodage\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("Échec du décodage\n");
return;
}
printf("string_value : %s\n", decoded.value.string_value);
}
int main() {
printf("Test de tous les types oneof :\n");
encode_decode_int_value();
encode_decode_float_value();
encode_decode_string_value();
return 0;
}Points clés
- Définition oneof : Utilisez le mot-clé
oneofdans le proto pour définir des types union - Champ which_field généré : NanoPB génère une énumération
which_fieldnamepour suivre quel champ est défini - Encodage : Définissez
which_*avec le tag approprié, puis définissez la valeur du champ - Décodage : Vérifiez
which_*pour déterminer quel champ a été défini - Exclusion mutuelle : Un seul champ d’un oneof peut être défini à la fois
- Sémantique d’union : Similaire aux unions C mais avec une sécurité de type
- Efficacité mémoire : Seule la mémoire pour un champ est allouée
Quand utiliser oneof
- Quand une seule parmi plusieurs valeurs possibles doit être présente
- Quand vous voulez économiser de la mémoire en n’allouant pas d’espace pour tous les champs
- Quand vous avez besoin d’unions sûres au niveau des types
- Quand vous représentez des types variants ou des unions étiquetées
- Quand vous implémentez des messages polymorphes
Sortie attendue
Encoded 2 bytes (int_value)
Encoded data: 08 2a
Decoded value:
int_value: 42Sortie attendue (tous les types)
Testing all oneof types:
int_value: 42
float_value: 3.141590
string_value: HelloPlus d’articles sur NanoPB
- Types scalaires de base en C++
- Types scalaires de base en C
- Types chaîne en C++
- Types chaîne en C
- Types bytes en C++
- Types bytes en C
- Champs optionnels en C++
- Champs optionnels en C
- Champs répétés/tableaux en C++
- Champs répétés/tableaux en C
- Enums en C++
- Enums en C
- Messages imbriqués en C++
- Messages imbriqués en C
- Types oneof/union en C
- Convertisseurs de tableaux personnalisés en C++
- Convertisseurs de tableaux personnalisés en C