Newsgroup: it.hobby.elettronica.digitale
Titolo: Const e structure

 Addirittura potresti definire tutto const e inizializzare le costanti
 > fuori dal codice, risparmiando appunto il codice di inizializzazione.
In questo momento sto confrontando questa soluzione con una che avevo 
sviluppato qualche settimana fa.
Non riesco però a definire direttamente tutti i campi come costanti. 
Forse sono io che sbaglio oppure è il Dynamic C che non mi permette 
l'operazione.
Devo definire ciascun campo come const? In questo caso il compilatore mi 
dice: "Keyword 'const' can only be used with globals and static locals."
Grazie!
Marco / iw2nzm

 dice: "Keyword 'const' can only be used with globals and static locals."
No, penso che tu debba definire const la variabile struct quando la 
definisci e non il singolo campo all'interno della struct.
Giustamente come fa una struct ad avere campi const ed altri campi non 
const? O tutta la struct è const o non lo è per niente.
La dichiarazione di una struct di questo tipo come const è un'ottima 
cosa perchè sfrutti in questo modo la memoria non volatile senza 
sprecare la preziosa RAM.
Ad oggi, però, ho visto due controindicazioni.
La prima è che è bello parlare di una serie di menu e voci costanti, ma, 
nei fatti, c'è sempre qualche eccezione. A me è capitato spesso di dover 
affrontare casi in cui, a seconda della modalità di funzionamento, la 
voce del menu cambiava nome. A quel punto puoi, se ciò è l'eccezione, 
fare una "porcata" durante la visualizzazione del titolo (se sono in 
questa modalità e sto visualizzando questo menu, scrivi questo titolo 
anzichè quello costante di default). Oppure devi prevedere nella struct 
un puntatore ad una lista di stringhe e all'indice (non costante) della 
stringa valida in quel momento.
Altra difficoltà con la definizione delle struct costanti è quando hai 
un puntatore che, a seconda dei casi, può puntare a diversi tipi a 
seconda di un'altra variabile.
Nell'esempio menuitem di sopra, ptr è puntatore ad una struct param_t se 
l'item del menu è relativo ad un parametro; è puntatore ad una struct 
menu_t, se l'item del menu è relativo ad un submenu.
Lì sopra ho risolto il problema con un bel void *ptr che mi costringe a 
fare dei cast ogni volta che voglio usarlo. Più elegante sarebbe 
definire una union:
   typedef struct {
     char *name;
     int type;
     union {
       menu_t *menu_ptr;
       param_t *param_ptr;
     } ptr;
   } menuitem_t;
   ...
   param_t *p;
   menuitem_t *m;
   if (MENUITEMTYPE_PARAM==mi)
     p = mi->ptr.param_ptr;
   else
     m = mi->ptr.menu_ptr;
Ma, se non hai un compilatore C99 (cosa difficile nel campo embedded, 
tranne se non usi gcc), non riesci ad assegnare campi di una union (ma 
anche di una struct) dopo il primo.

 definire una union:
Già, la mia soluzione (non ricordo se già l'avevo postata) è più o meno 
così:
char * const MN0000[] = {"Main menu", "Menu principale"};
char * const MN1000[] = {"Setup", "Configurazione"};
char * const MN2000[] = {"About", "Informazioni"};
char * const MN1010[] = {"Text", "Testo"};
char * const MN1020[] = {"Graphics", "Grafica"};
char * const MN1011[] = {"White", "Bianco"};
char * const MN1012[] = {"Black", "Nero"};
char ** const MENU[] 	= {MN1000, MN2000};
char ** const SUBMENU[] = {MN1010, MN1020};
char ** const ITEM[] 	= {MN1011, MN1012};
int const MENU_STRUCT[MENU_LEN][2] = {{2, 0}, {2, 0}};
struct {
	int cur_menu;
	int sel_menu;
	int sel_submenu;
	int sel_item;
} menu_state;
Il vantaggio è che hai tutte le stringhe raggruppate e di facile 
aggiornamento. Hai il supporto per menu multilingua e la gestione della 
navigazione nei menu è abbastanza semplice.
Di contro il numero di livelli lo stabilisci a priori e non è così 
flessibile come la tua soluzione.
Non saprei decidere :)
Marco / iw2nzm
ps. però mi ha permesso di capire finalmente quando usare il doppio 
puntatore... a un certo punto mi è venuto spontaneo aggiungerlo e solo 
dopo mi sono reso conto e ho detto: "ma che sto facendo?" :)

 Non saprei decidere :)
Se ti trovi bene con la tua, perchè cambiare?

 Se ti trovi bene con la tua, perchè cambiare?
Perché appunto non è detto che sia la migliore... magari facendo un po' 
più di fatica subito posso in futuro evitare di dover rifare il lavoro :)
Buona domenica!
Marco / iw2nzm

 fare dei cast ogni volta che voglio usarlo. 
Scusa, mi riaggancio qui per un'ultima domanda: la soluzione mi piace e 
la sto adottando con alcune modifiche.
La cosa che mi ha convinto è stata proprio il puntatore di tipo void che 
permette di puntare a qualsiasi cosa... In effetti ogni voce di menu può 
richiedere tante azioni diverse. A parte il discorso parametri / 
sottomenu che dovrei avere chiaro come gestire, mi serve poter puntare a 
funzioni.
Pensavo che fosse già predisposto visto che il prototipo delle mie 
"funzioni menu" è appunto di tipo void.
La dichiarazione la farei così:
void   *mn_about();
const menuitem_t mi_main[] = {
	{{"Setup", "Configurazione"}, MIT_SUBMENU, &mn_setup},
	{{"About", "Informazioni"}, MIT_FUNCTION, mn_about}
};
e per chiamarla:
						(*(*mn_menu).items[mn_item].ptr)();
dove mn_menu è il puntatore alla struttura menu corrente. mn_item è la 
voce di menu corrente.
Quindi (*mn_menu).items[mn_item].ptr mi punta alla funzione mn_about.
Ma il compilatore si in%%%za (sicuramente ha ragione) e mi dice:
"Need a function call" su quest'ultima riga...
Marco / iw2nzm

 "Need a function call" su quest'ultima riga...
Giusto per provare, aggiungi alla struttura un campo che sia veramente 
un puntatore a funzione (e non puntatore a variabile).

 Ooops, ho saltato questo post... provvedo subito :)
Tranquillo, no problem!
Anzi, nel frattempo il frammento di codice è evoluto e mi piace assai. 
Solo che sto diventando scemo tra puntatori, struct, ecc....
Riassunto delle ultime puntate: dopo un divertente (?) quiproquo con gli 
amici d'oltreoceano sull'uso delle union abbiamo trovato questo bel 
ritrovato che mette tutti d'accordo:
typedef struct {
	char  *name[2];
	int    type;
	void **data;
} menu_t;
#define SUBMENU_PTR(m_ptr)  ((menu_t *)(m_ptr)->data)
#define STRINGS_PTR(m_ptr)  ((char **)(m_ptr)->data)
#define FUNCTION_PTR(m_ptr) ((menu_function *)(m_ptr)->data)
Semplice, elegante e funzionale - spero.
In pratica uso una sola struttura per qualsiasi tipo di campo: 
sottomenu, parametro, funzione ecc.... Il valore assegnato a 'type' mi 
permette di decidere cosa fare. Posso inizializzarla a design-time e 
l'array di puntatori a void contiene l'insieme dei sottomenu o dei 
parametri o della funzione da chiamare. L'ultimo elemento dell'array 
sarà un NULL così da riconoscere che è finito senza bisogno di numerarli.
Esempio di inizializzazione:
const menu_t mn_about = {
	{"About", "Informazioni"},
	MIT_FUNCTION,
	NULL	// giusto per riempire il campo
};
const menu_t mn_setup = {
	{"Setup", "Impostazioni"},
	MIT_FUNCTION,
	NULL
};
const menu_t *items[] = {&mn_setup, &mn_about}; // btw, mi genera un 
warning che devo inserire "const" poiché ho inizializzato la variabile 
durante la dichiarazione... ma io l'ho messo const!!!
const menu_t mn_main = {
	{"Main Menu", "Menu Principale"},
	MIT_SUBMENU,
	items
};
Ora il primo problema è che non riesco a dichiarare inline il terzo 
campo di mn_main. Ok che così funziona, ma fa schifo a vedersi. Tentando:
const menu_t mn_main = {
	{"Main Menu", "Menu Principale"},
	MIT_SUBMENU,
	&{&mn_setup, &mn_about}
};
Il compilatore inveisce. Ma un *items[] non è un array di puntatori e 
quindi l'indirizzo base un puntatore a un puntatore? Sto delirando? Sto 
scrivendo codice maccheronico?
Premetto che ho provato senza l'array (quindi dichiarando solo void 
*data) il cast funziona e riesco a lanciare anche l'eventuale funzione 
assegnata. Quindi il concetto di fondo è valido.
L'altro problema - e mi sento veramente idiota perché dovrebbe essere 
una banalità - è che non riesco a "navigare" attraverso le strutture.
Cioè, mettiamo che voglia accedere al campo name[1] della seconda struct 
di mn_main (mn_about).
Dovrei fare qualcosa del genere:
char *str;
str = ((menu_t *)mn_main.data)[1].name[1];
In pratica, dato l'array di puntatori a void "casto" a puntatore a 
menu_t, prendo il secondo campo e infine accedo a name[1] che dovrebbe 
essere un puntatore a char. Ma o non succede nulla o ottengo caratteri 
casuali, come se puntassi chissà dove...
Vabbè, se hai voglia di darci un occhio te ne sarei grato. :)
Marco / iw2nzm