NanoPB: Gérer les messages imbriqués en C++
Voir aussi : Version C : Comment gérer les messages imbriqués 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 messages imbriqués en C++ avec NanoPB.
Définition Proto
Créez d’abord un fichier .proto avec des messages imbriqués :
syntax = "proto3";
package example;
message Address {
string street = 1;
string city = 2;
string country = 3;
}
message Person {
string name = 1;
uint32 age = 2;
Address address = 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 nested.options :
example.Address.street max_size:64
example.Address.city max_size:32
example.Address.country max_size:32
example.Person.name max_size:64Puis générez :
protoc --nanopb_out=. nested.protoCela générera nested.pb.h et nested.pb.c.
Exemple C++
Voici un exemple C++ complet gérant les messages imbriqués :
#include <stdio.h>
#include <string.h>
#include "nested.pb.h"
#include "pb_encode.h"
#include "pb_decode.h"
int main() {
// Tampon pour le message encodé
uint8_t buffer[256];
size_t message_length;
// --- ENCODAGE ---
example_Person person = example_Person_init_zero;
// Définir les valeurs du message imbriqué
strncpy(person.name, "John Doe", sizeof(person.name) - 1);
person.has_address = true;
strncpy(person.address.street, "123 Main St", sizeof(person.address.street) - 1);
strncpy(person.address.city, "Springfield", sizeof(person.address.city) - 1);
strncpy(person.address.country, "USA", sizeof(person.address.country) - 1);
// 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_Person_fields, &person)) {
printf("Échec de l'encodage : %s\n", PB_GET_ERROR(&ostream));
return 1;
}
message_length = ostream.bytes_written;
printf("Encodé %zu octets\n", message_length);
// Afficher le dump hexadécimal des données encodées
printf("Données encodées : ");
for (size_t i = 0; i < message_length; i++) {
printf("%02x ", buffer[i]);
}
printf("\n");
// --- DÉCODAGE ---
example_Person decoded = example_Person_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_Person_fields, &decoded)) {
printf("Échec du décodage : %s\n", PB_GET_ERROR(&istream));
return 1;
}
// Afficher les valeurs décodées
printf("Valeurs décodées :\n");
printf(" name : %s\n", decoded.name);
printf(" age : %u\n", decoded.age);
printf(" address :\n");
printf(" street : %s\n", decoded.address.street);
printf(" city : %s\n", decoded.address.city);
printf(" country : %s\n", decoded.address.country);
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 nested_example nested_example.cpp nested.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 nested_pb2
# Lire les données binaires
with open('encoded.bin', 'rb') as f:
data = f.read()
# Décoder
msg = nested_pb2.Person()
msg.ParseFromString(data)
print("Valeurs décodées par Python :")
print(f" name : {msg.name}")
print(f" age : {msg.age}")
print(f" address :")
print(f" street : {msg.address.street}")
print(f" city : {msg.address.city}")
print(f" country : {msg.address.country}")D’abord, compilez les définitions protobuf Python :
protoc --python_out=. nested.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 un message imbriqué optionnel
Voici un exemple avec un message imbriqué optionnel :
syntax = "proto3";
package example;
message Address {
string street = 1;
string city = 2;
}
message Person {
string name = 1;
optional Address address = 2;
}Créez nested_optional.options :
example.Address.street max_size:64
example.Address.city max_size:32
example.Person.name max_size:64#include <stdio.h>
#include <string.h>
#include "nested_optional.pb.h"
#include "pb_encode.h"
#include "pb_decode.h"
int main() {
uint8_t buffer[256];
size_t message_length;
// --- ENCODAGE ---
example_Person person = example_Person_init_zero;
strncpy(person.name, "Jane Doe", sizeof(person.name) - 1);
// Définir le message imbriqué optionnel
strncpy(person.address.street, "456 Oak Ave", sizeof(person.address.street) - 1);
strncpy(person.address.city, "Boston", sizeof(person.address.city) - 1);
person.has_address = true; // Important : définir le drapeau has_*
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
if (!pb_encode(&ostream, example_Person_fields, &person)) {
printf("Échec de l'encodage : %s\n", PB_GET_ERROR(&ostream));
return 1;
}
message_length = ostream.bytes_written;
printf("Encodé %zu octets\n", message_length);
// --- DÉCODAGE ---
example_Person decoded = example_Person_init_zero;
pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
if (!pb_decode(&istream, example_Person_fields, &decoded)) {
printf("Échec du décodage : %s\n", PB_GET_ERROR(&istream));
return 1;
}
printf("Valeurs décodées :\n");
printf(" name : %s\n", decoded.name);
if (decoded.has_address) {
printf(" address :\n");
printf(" street : %s\n", decoded.address.street);
printf(" city : %s\n", decoded.address.city);
} else {
printf(" address : (non défini)\n");
}
return 0;
}Points clés
- Messages imbriqués : Les messages peuvent contenir d’autres messages en tant que champs
- Initialisation : Utilisez
*_init_zeropour initialiser à la fois les messages externes et imbriqués - Encodage : Définissez directement les champs du message imbriqué, puis encodez le message externe
- Décodage : Décodez le message externe, les messages imbriqués sont décodés automatiquement
- Message imbriqué optionnel : Utilisez le mot-clé
optionalet définissez le drapeauhas_* - Structure : Les messages imbriqués fournissent une organisation hiérarchique des données
- Tailles de tampon : Spécifiez les tailles de tampon pour tous les champs chaîne dans le fichier .options
Quand utiliser les messages imbriqués
- Quand vous avez des données hiérarchiques ou groupées
- Quand vous voulez réutiliser des définitions de messages
- Quand vous avez besoin d’organiser des champs liés ensemble
- Quand vous voulez améliorer la lisibilité des messages
- Quand vous avez des structures de données complexes
Sortie attendue
Encoded 35 bytes
Encoded data: 0a 08 4a 6f 68 6e 20 44 6f 65 10 1e 1a 1b 0a 0b 31 32 33 20 4d 61 69 6e 20 53 74 12 08 4e 65 77 20 59 6f 72 6b 1a 03 55 53 41
Decoded values:
name: John Doe
age: 30
address:
street: 123 Main St
city: New York
country: USASortie attendue (message imbriqué optionnel)
Encoded 27 bytes
Decoded values:
name: Jane Doe
address:
street: 456 Oak Ave
city: BostonPlus 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
- Types oneof/union en C++
- Types oneof/union en C
- Convertisseurs de tableaux personnalisés en C++
- Convertisseurs de tableaux personnalisés en C