NanoPB : Comment gérer les enums en C
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 :
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 :
protoc --nanopb_out=. enums.protoCela générera enums.pb.h et enums.pb.c.
Exemple en C
Voici un exemple complet en C gérant les enums :
#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 :
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 :
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 :
protoc --python_out=. enums.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 enum dans une instruction switch
Voici un exemple montrant comment utiliser les enums dans des instructions switch :
#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
- Définition d’enum : Définissez les enums dans le fichier
.protoavec des valeurs explicites - Types générés : NanoPB génère des types enum avec la nomenclature
package_EnumName - Encodage : Affectez directement la valeur de l’enum au champ
- Décodage : Les valeurs d’enum sont décodées automatiquement
- Valeurs par défaut : La première valeur de l’enum (généralement 0) est la valeur par défaut
- Instructions switch : Les enums fonctionnent naturellement dans les instructions switch
- Sécurité de type : Les enums offrent une sécurité de type par rapport aux entiers bruts
- Valeurs inconnues : Les valeurs d’enum invalides peuvent être décodées comme leur valeur numérique
Quand utiliser les enums
- Lorsque vous avez un ensemble fixe de valeurs possibles
- Lorsque vous souhaitez une sécurité de type pour les champs d’état ou de mode
- Lorsque vous souhaitez un code auto-documenté
- Lorsque vous devez distinguer différents modes de fonctionnement
- Lorsque vous souhaitez encoder une signification sémantique dans les messages
Sortie attendue
Encoded 2 bytes
Encoded data: 08 01
Decoded value:
status: OK (enum value: 1)Sortie attendue (exemple switch)
Handling status: UNKNOWN - Default state
Handling status: OK - Operation successful
Handling status: ERROR - Operation failed
Handling status: BUSY - System busyDifférences par rapport au C++
La version C est presque identique à la version C++ :
- Les enums fonctionnent de la même manière en C et en C++
- Utilisez une conversion explicite pour les spécificateurs de format printf en C
- Aucune fonctionnalité spécifique au C++ n’est nécessaire pour la gestion des enums
- Le code généré est identique pour les deux langages
Plus 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++
- Types oneof/union en C
- Convertisseurs de tableau personnalisés en C++
- Convertisseurs de tableau personnalisés en C