NanoPB: Gérer les types oneof (union) en C++

English Français

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 :

oneof.proto
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 :

oneof.options
example.OneofMessage.string_value max_size:32

Puis générez :

generate_nanopb_oneof.sh
protoc --nanopb_out=. oneof.proto

Cela générera oneof.pb.h et oneof.pb.c.

Exemple C++

Voici un exemple C++ complet gérant les champs oneof :

oneof_example.cpp
#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 :

compile_oneof_example.sh
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 :

test_oneof.py
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 :

compile_python_oneof.sh
protoc --python_out=. oneof.proto

Modifiez ensuite l’exemple C++ pour enregistrer les données encodées dans un fichier :

save_encoded_oneof.cpp
// 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 :

oneof_all_example.cpp
#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

Quand utiliser oneof

Sortie attendue

oneof_expected_output.txt
Encoded 2 bytes (int_value)
Encoded data: 08 2a 
Decoded value:
  int_value: 42

Sortie attendue (tous les types)

oneof_all_expected_output.txt
Testing all oneof types:
int_value: 42
float_value: 3.141590
string_value: Hello

Plus d’articles sur NanoPB


Check out similar posts by category: Embedded, C/C++, Protocol Buffers