Newsgroup: it.comp.lang.c++
Titolo: Un parere

Ho creato un'applicazione con Visual C++ utilizzando principalmente MFC.
In una finestra di dialogo ho inserito diversi pulsanti, di tipo owner draw,
e li ho definiti derivandoli da una classe creata da me, diciamo
CMyButton(che ha sua volta è di tipo CBitmapButton).
Supponiamo io debba disabilitare contemporaneamente tutti questi pulsanti.
Io ho fatto una cosa di questo tipo:
for (i = 1; i < MAX_BUTTON; i++)
    ((CMyButton*) GetDlgItem(IDC_BUTTON1+i))->EnableWindow(FALSE);
e nel file di risorse ho fatto in modo che i pulsanti avessero un ID
sequenziale.
Chiaramente, utilizzo lo stesso metodo quando devo modificare in blocco una
qualunque proprietà o attributo dei pulsanti.
Si può fare, conviene farlo, o sto facendo una di quelle cose assurde che
ogni tanto si vedono?
Grazie.
--
N.B. rimuovere KKK per rispondermi

sequenziale.
Non mi sembra una cattiva idea, solo che se in futuro dovessi aggiungere o rimuovere
qualche pulsante dovresti stare attento agli id dei pulsanti... in particolare se ne rimuovi
uno devi riarrangiare gli Id dei successivi, se ne aggiungi uno devi poter disporre del valore
di cui bisogni. Dovresti quindi scegliere un opportuno insieme di id da riservare alla cosa.
Solo una osservazione: usi un cast di vecchio stile, usa i nuovi tipi di cast. sono piu` sicuri.
Paolo
----------------------------------------------------------
  Paolo Marotta
  Computer Science, University of Pisa
  home:   
http://www.cli.di.unipi.it/~marotta
  e-mail: 
mar...@cli.di.unipi.it
 
----------------------------------------------------------

sequenziale.



Cosa intendi per nuovi tipi di cast? Io quello è l'unico cast che conosco.

Qual'è quindi l'alternativa migliore secondo te? Scrivere tante righe di
codice quanti sono i pulsanti, indirizzandomi singolarmente ad essi? O c'è
un'alternativa migliore che io non conosco?

Cosa intendi per nuovi tipi di cast? Io quello è l'unico cast che conosco.
usa gli operatori static_cast<>, dynamic_cast<>, const_cast<> e reinterpret_cast<> quando devi
eseguire dei cast di puntatori. Hanno effetti diversi e si applicano a situazioni diverse e sono
comunque in generale piu` sicuri.
In particolare usa static_cast se sei sicuro della correttezza del cast, in modo da risolvere
tutto a compile time. Usa invece dynamic_cast se la conversione puo` fallire, in modo da poterne
verificare l'esito.


controllo sulla dialog, 
Scusa la domanda, non uso il VC++. Ma gli identificatori delle risorse non
puoi definirli tu anzicche` lasciare tale compito al'editor di risorse?


...


ogni tanto si vedono?
L'esempio specifico che dai e` in effetti abbastanza assurdo: EnableWindow
e` un metodo di CWnd, la GetDlgItem ti torna appunto un CWnd*, perche`
mai impelagarsi nel cast?!  Molto meglio -- piu` pulito, piu` tranquillo, senza
nessuno svantaggio) fare solo
    GetDlgItem(IDC_BUTTON1+i)->EnableWindow(FALSE);
Nel loop, i parte da 1, il che significa che il controllo IDC_BUTTON1 non
viene toccato, solo i successivi; anche questa e` una stranezza mica male
(perche` i non parte da 0...?).
A parte questi due dettagli, puoi benissimo (a tuo rischio) castare il CWnd*,
tornato da GetDlgItem, a puntatore allo specifico tipo del controllo che "sai"
essere; quando ti serve veramente qualche metodo specifico di quel tipo,
un tale "downcast" non e` assurdo.
Sarebbe, naturalmente, meglio controllare, almeno quando sei a debug...:
    for (i = 0; i < MAX_BUTTON; i++) {
        CWnd* pWnd = GetDlgItem(IDC_BUTTON1+i);
        ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(CMyButton)));
        ((CMyButton*)pWnd)->MetodoParticolare();
    }
supponendo, naturalmente, il DECLARE_DYNAMIC(CMyButton)
(o _DYNCREATE, o _SERIAL, ovviamente).
Alex

Ho sbagliato a riportare il codice. Non l'avevo sottomano e sono andato a
memoria:
1) i parte da 0 e non da 1;
2) il codice era un esempio, in effetti mi serve inizializzare delle
variabili dell'oggetto CMyButton e non abilitarlo o meno. Anche se non ti
nego che non avevo mai pensato a questa cosa, nel senso che non faccio caso
al fatto che la funzione utilizzata sia di CWnd o meno. Se lo devo fare su
un oggetto CMyButton, faccio sempre un cast di quel tipo. Mi sa tanto che
peggioro le cose, vero? Per te è talmente anormale che hai focalizzato
l'attenzione sul cast, mentre io non pensavo ci fosse nulla di sbagliato in
quello. Il mio dubbio era sull'utilizzo di IDC_BUTTON+i per puntare a dei
controlli, che presuppone la sequenzialità degli ID. Qualcuno infatti, come
puoi notare alle risposte datemi, ha avuto qualche obiezione. Non ricordo
molto bene (forse mi sbaglio), me anche a è capitato qualche problema quando
lavoravo con il Visual C++ 1.5, nel senso che quando aggiungevo dei
controlli questo mi cambiava gli ID, alcune volte anche di quelli già
assegnati, come se rifacesse un assegnamento. Dovevo chiudere il progetto e
rieditare a mano il file .rc (anche allora era il .rc, vero?) Ora quando nè
aggiungo di nuovi, quelli non vengono alterati, ma parte dal successivo.
L'unica pecca è che forse dovevo lasciare degli ID disponibili nel caso
debba aggiungere pulsanti simili. E' una pratica giusta quella di basare il
codice sugli ID dei pulsanti (e similitudini)?
Grazie.

codice sugli ID dei pulsanti (e similitudini)?
Non e` necessariamente sbagliata, se ben documentata; ad esempio,
in uno degli esempi del tutorial sulle API che sto postando alla mailing
list GIDA, ho usato questa tecnica, per associare delle checkbox ai
vari stili di un edit... ho usato (praticamente) ciascun bit di stile come
ID della checkbox corrispondente, invece di costruire una mappa di
corrispondenza.
Per lo specifico compito che tu esemplifichi (trovare tutti i controlli
di un dialogo che appartengono a una certa sottoclasse di CWnd) ci
sono tecniche alternative da considerare, come EnumChildWindows
(con test di IsKindOf su ciascuna CWnd::FromHandle della HWND
ricevuta nella procedura di enumerazione -- si puo` fare generale,
passandole come lparam la const CRuntimeClass* che si cerca) --
la performance puo` essere un po' meno "da Indianapolis", certo,
ma sara` probabilmente sufficiente, e il codice ne puo` risultare
meno "fragile" a fronte di future manutenzioni.
Alex


Sul fatto che bisogna stare attenti sono d'accordo, d'altronde nel post in
cui rispondevo alla domanda ponevo l'accento sul fatto che bisognava
prevedere un range di valori da riservare allo scopo al fine di poter
eseguire delle modifiche successivamente.
E` un approccio che sicuramente permette di scrivere (almeno all'inizio) meno codice
(l'alternativa sarebbe mantenere una lista con gli ID) e probabilmente il codice risultante
e` anche piu` efficiente. Comunque considerato tutto e tenendo presente quella miniera di
contenitori che e` la libreria standard in effetti non e` che sia poi un gran vantaggio.


passandole come lparam la const CRuntimeClass* che si cerca) --
Potrei cioč scrivere una funzione parametrizzata rispetto al HWND e a
lparam, che mi restituisce per esempio un CWnd* se il controllo con handle
HWND appartiene alla classe passatagli tramite RUNTIME_CLASS(CMyButton)
(giusto?), NULL altrimenti?
Sul puntatore CWnd* dovrei poi fare un cast a (CMyButton*) se devo
utilizzare metodi o attributi specifici di CMyButton, vero?.
Grazie per le esaurienti risposte (come sempre)

Grazie per le esaurienti risposte (come sempre)
Prego, e grazie per l'apprezzamento, e per l'ottima
comprensione degli argomenti.
Alex

Cosa intendi per nuovi tipi di cast? Io quello è l'unico cast che conosco.
I "nuovi cast", attualmente raccomandati (il "cast all'antica",
ereditato dal C, e` deprecato), sono i seguenti:
    const_cast<tipodiarrivo>(espressione)
        mette e/o toglie "const" (e simili aggettivi, come "volatile")
    static_cast<tipodiarrivo>(espressione)
        copre la maggior parte delle situazioni: permette di fare
        qualsiasi trasformazione di tipo che sia implicita, o inversa
        di una implicita, eccetto per quelle che cura il const_cast
        (tipi numerici, puntatori parenti fra loro, ecc)
    reinterpret_cast<tipodiarrivo>(espressione)
        prende i BIT del valore dell'espressione, e, senza NESSUNA
        modifica, li *interpreta* come "tipo di arrivo"; l'uso meno raro
        e` fra puntatori non parenti fra loro (char* e unsigned char*
        NON sono parenti fra loro...)
    dynamic_cast<tipodiarrivo>(espressione)
        si usa solo fra puntatori o reference a tipi polimorfici; verifica
        che l'espressione sia effettivamente del tipo desiderato, se
        no torna 0 (per cast tra puntatori) o da` exception (per cast
        tra reference)
dynamic_cast offre una funzionalita` del tutto nuova rispetto
ai "vecchi cast" (e` la "run-time type identification", RTTI). Gli
altri, invece, sostanzialmente RIDUCONO, e suddividono in
modo piu` preciso, quello che i "vecchi cast" potevano fare
(inoltre, offrono una sintassi fatta per "saltare all'occhio", al
contrario delle parentesi dei vecchi cast, e tale da rendere
facile cercarle con un editor, ecc).
Un costrutto TROPPO potente, in un linguaggio, va evitato;
per questo la potenza eccessiva dei "vecchi cast" viene
"ritagliata" (e chiarificata) in tre casi distinti.  const_cast
va dunque usato in modo esclusivo per mettere/togliere
"const" e simili (solo, naturalmente, se risulta necessario
farlo esplicitamente; spesso, ad es passando un char*
a una funzione che richiede un const char*, il cast puo`
essere lasciato implicito, "che e` meglio"!).  static_cast
copre tutti quei casi in cui il "nuovo tipo" puo` richiedere
una rappresentazione binaria diversa da quello della
espressione (float<->int, puntatori apparentati, ecc);
reinterpret_cast, tutti quelli in cui vogliamo che resti
invece identica la rappresentazione binaria (ad esempio,
se ho un "token opaco" di 32 bit, presentato come un
unsigned int, e "so" che e` in realta` un char*, oppure
un float, oppure..., reinterpret_cast e` giusto).
Alex