Inizializzazione Struct attraverso l'uso eccessivo di macro
-
16-09-2020 - |
Domanda
Ho alcune strutture da inizializzare, il che sarebbe noioso da fare manualmente.Mi piacerebbe creare una macro che mi aiuterà con esso...ma non sono sicuro che il C il preprocessore è abbastanza buono per questo.
Ho strutture che rappresentano i menu.Sono costituiti solo da puntatori di funzione:
typedef uint8_t (*button_handler) (uint8_t);
typedef void (*pedal_handler) (void);
typedef void (*display_handler) (void);
typedef void (*menu_switch_handler) (void);
#define ON_BUTTON(x) uint8_t menu_frame_##x##_button (uint8_t button)
#define ON_PEDAL(x) void menu_frame_##x##_pedal (void)
#define ON_DISPLAY(x) void menu_frame_##x##_display (void)
#define ON_SWITCH(x) void menu_frame_##x##_switch (void)
typedef struct menu_frame {
button_handler on_button;
pedal_handler on_pedal;
display_handler on_display;
menu_switch_handler on_switch;
} menu_frame;
Questo mi permette di scrivere le funzioni e le funzioni separate come (.file c):
ON_BUTTON(blah) { ... }
e menu come (.file h):
ON_BUTTON(blah);
ON_DISPLAY(blah);
menu_frame menu_frame_blah = {
menu_frame_blah_button,
NULL,
menu_frame_blah_display,
NULL
};
C'è un modo in cui posso piegare la definizione del menu in una definizione?Potrei fare qualcosa che si espande MENU(blah, menu_frame_blah_button, NULL, menu_frame_blah_display, NULL)
certo, ma c'è un modo per:
- rendilo più breve (NULL o qualche nome)
- rimuovere la necessità di
ON_BUTTON(...);
da prima della struct
Idealmente, mi piacerebbe MENU(blah, button, NULL, display, NULL)
per definire entrambi i gestori e la struttura del menu stessa.Non so per esempio come evitare di espandere l'ultimo termine in ON_SWITCH(NULL)
.
O forse dovrei avvicinarmi da qualche altro modo?
Soluzione
Non è possibile eseguire l'espansione macro condizionale in C, in modo che la macro venga espansa in modo diverso a seconda degli argomenti, come in:non è possibile utilizzare #se all'interno della definizione di macro.
Credo che il meglio che si potrebbe ottenere sarebbe qualcosa di simile MENU(blah, ITEM(blah,button), NULL, ITEM(blah,display), NULL)
, e ancora ha bisogno di un insieme separato per prototipi a causa di mancanza di espansione condizionale.
Personalmente, scriverei un semplice script per generare quel tipo di codice C boilerplate.Uno che capirebbe la sintassi desiderata.In Python o quello che ti si addice meglio…
Altri suggerimenti
Ho scritto script Python per generare questo tipo di codice per me prima.Potresti voler seguire quella strada e semplicemente lavorare lo script nel tuo processo di compilazione.
È possibile programmare condizionali, cicli finiti, argomenti predefiniti e tutte queste cose nel solo preprocessore.La libreria Boost ha un'implementazione di alcuni di questi nella loro sezione preprocessore.Boost è principalmente per C++, ma le cose del preprocessore dovrebbero fondamentalmente funzionare anche in C.
Con tali tecniche è possibile scrivere macro complicate ma che sono semplici da usare.Diventa un po ' più semplice da implementare quando si utilizza C99 invece di C89 (hai chiamato inizializzatori e VA_ARGS), ma ancora.