NanoPB : Comment gérer les enums en C

English Français

Voir aussi : Version C++ : Comment gérer les enums 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 enum en C avec NanoPB.

Définition proto

Créez d’abord un fichier .proto avec des enums :

enums.proto
syntax = "proto3";

package example;

enum Status {
  UNKNOWN = 0;
  OK = 1;
  ERROR = 2;
  BUSY = 3;
}

message EnumMessage {
  Status status = 1;
}

Générer le code NanoPB

Générez le code NanoPB :

generate_nanopb_enums.sh
protoc --nanopb_out=. enums.proto

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

Exemple en C

Voici un exemple complet en C gérant les enums :

enums_example.c
#include <stdio.h>
#include <stdint.h>
#include "enums.pb.h"
#include "pb_encode.h"
#include "pb_decode.h"

// Fonction utilitaire pour convertir un enum en chaîne
const char* status_to_string(example_Status status) {
    switch (status) {
        case example_Status_UNKNOWN: return "UNKNOWN";
        case example_Status_OK: return "OK";
        case example_Status_ERROR: return "ERROR";
        case example_Status_BUSY: return "BUSY";
        default: return "INVALID";
    }
}

int main() {
    // Tampon pour le message encodé
    uint8_t buffer[64];
    size_t message_length;
    
    // --- ENCODAGE ---
    example_EnumMessage message = example_EnumMessage_init_zero;
    
    // Définir la valeur de l'enum
    message.status = example_Status_OK;
    
    // Créer un flux pour l'encodage
    pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
    
    // Encoder le message
    if (!pb_encode(&ostream, example_EnumMessage_fields, &message)) {
        printf("Encoding failed: %s\n", PB_GET_ERROR(&ostream));
        return 1;
    }
    
    message_length = ostream.bytes_written;
    printf("Encoded %zu bytes\n", message_length);
    
    // Afficher le dump hexadécimal des données encodées
    printf("Encoded data: ");
    for (size_t i = 0; i < message_length; i++) {
        printf("%02x ", buffer[i]);
    }
    printf("\n");
    
    // --- DÉCODAGE ---
    example_EnumMessage decoded = example_EnumMessage_init_zero;
    
    // Créer un 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_EnumMessage_fields, &decoded)) {
        printf("Decoding failed: %s\n", PB_GET_ERROR(&istream));
        return 1;
    }
    
    // Afficher la valeur décodée
    printf("Decoded value:\n");
    printf("  status: %s (enum value: %d)\n", 
           status_to_string(decoded.status), (int)decoded.status);
    
    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_enums_example.sh
gcc -o enums_example enums_example.c enums.pb.c pb_common.c pb_encode.c pb_decode.c -I.

Remarque : 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 NanoPB.

Script de test Python

Pour vérifier l’encodage, vous pouvez utiliser la bibliothèque protobuf de Python :

test_enums.py
import enums_pb2

# Lire les données binaires
with open('encoded.bin', 'rb') as f:
    data = f.read()

# Décoder
msg = enums_pb2.EnumMessage()
msg.ParseFromString(data)

print("Python decoded values:")
print(f"  status: {enums_pb2.Status.Name(msg.status)}")

Compilez d’abord les définitions protobuf Python :

compile_python_enums.sh
protoc --python_out=. enums.proto

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

save_encoded_enums.c
// Après l'encodage, ajoutez ceci :
FILE *f = fopen("encoded.bin", "wb");
fwrite(buffer, 1, message_length, f);
fclose(f);

Exemple avec enum dans une instruction switch

Voici un exemple montrant comment utiliser les enums dans des instructions switch :

enums_switch_example.c
#include <stdio.h>
#include <stdint.h>
#include "enums.pb.h"
#include "pb_encode.h"
#include "pb_decode.h"

void handle_status(example_Status status) {
    printf("Handling status: ");
    
    switch (status) {
        case example_Status_UNKNOWN:
            printf("UNKNOWN - Default state\n");
            break;
        case example_Status_OK:
            printf("OK - Operation successful\n");
            break;
        case example_Status_ERROR:
            printf("ERROR - Operation failed\n");
            break;
        case example_Status_BUSY:
            printf("BUSY - System busy\n");
            break;
        default:
            printf("INVALID - Unknown enum value\n");
            break;
    }
}

int main() {
    uint8_t buffer[64];
    size_t message_length;
    
    // Tester toutes les valeurs de l'enum
    example_Status test_values[] = {
        example_Status_UNKNOWN,
        example_Status_OK,
        example_Status_ERROR,
        example_Status_BUSY
    };
    
    for (size_t i = 0; i < 4; i++) {
        example_EnumMessage message = example_EnumMessage_init_zero;
        message.status = test_values[i];
        
        pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
        
        if (!pb_encode(&ostream, example_EnumMessage_fields, &message)) {
            printf("Encoding failed: %s\n", PB_GET_ERROR(&ostream));
            return 1;
        }
        
        message_length = ostream.bytes_written;
        
        example_EnumMessage decoded = example_EnumMessage_init_zero;
        pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
        
        if (!pb_decode(&istream, example_EnumMessage_fields, &decoded)) {
            printf("Decoding failed: %s\n", PB_GET_ERROR(&istream));
            return 1;
        }
        
        handle_status(decoded.status);
    }
    
    return 0;
}

Points clés

Quand utiliser les enums

Sortie attendue

enums_expected_output.txt
Encoded 2 bytes
Encoded data: 08 01 
Decoded value:
  status: OK (enum value: 1)

Sortie attendue (exemple switch)

enums_switch_expected_output.txt
Handling status: UNKNOWN - Default state
Handling status: OK - Operation successful
Handling status: ERROR - Operation failed
Handling status: BUSY - System busy

Différences par rapport au C++

La version C est presque identique à la version C++ :

Plus d’articles sur NanoPB


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