Newsgroup: it.comp.lang.c++
Titolo: + return x funzione

č errato creare una funzione che abbia piů di una return?
esempio
funzione
{
se x < y allora return x
    altrimenti return y
}

 č errato creare una funzione che abbia piů di una return?
certo che no!
Anzi, č pratica comune.
--
Gerolamo

 Anzi, è pratica comune.
Anche se deprecata. Rende il codice di più difficile comprensione e
manutenzione.
Ad esempio, immagina una funzione di questo tipo:
int funzione( int _a, int _b ) {
    switch( _a ) {
        case 0:
            return _b * 2;
        case 1:
            return _b * 4;
        case 2:
            return _b * 8;
    }
    if ( _a < 4 )
        return 5;
    return _b + 1;
}
Se un domani un altro programmatore volesse modificare questa funzione, ad
esempio volesse controllare che il valore ritornato sia entro i limiti
0...128, dovrebbe controllare ben 5 punti di uscita della funzione (i tre
case, più i due return derivati dall'if), con grande duplicazione di codice
e possibilità di inserire bachi, perchè non si è riusciti a coprire tutti i
casi di uscita. In realtà, una soluzione più elegante è la seguente:
int funzione( int _a, int _b ) {
    int rc( 0 ); // Valore di ritorno.
    switch( _a ) {
        case 0:
            rc = _b * 2;
            break;
        case 1:
            rc = _b * 4;
            break;
        case 2:
            return _b * 8;
            break;
    }
    if ( _a < 4 && ! rc )
        rc = 5;
    if ( ! rc )
        rc = _b + 1;
    return rc;
}
In questo caso l'unico punto di uscita è "return rc;", di facile controllo e
test.
Ovviamente, questa è solo una soluzione, che tra l'altro prevede che il
valore zero (0) non appartenga al dominio dei valori ritornabili dalla
funzione.
Ciao!
*** Azimuth ***

 manutenzione.
Benche' personalmente preferisca anch'io avere una sola istruzione di
return per ogni funzione, la pratica di averne molteplici e' tutt'altro
che deprecata, anzi e' sostenuta a gran voce da persone di tutto
rispetto.
Ciao,
Nicola

 rispetto.
Benchè personalmente io faccia di tutto per avere tale condizione, la
pratica è deprecata nella maggior parte delle linee guida di stile per i
programmatori.
Per riferimenti, ti cito i seguenti URL:
---
University of Kansas, the Information and Telecommunication Technology
Center (ITTC)
http://www.ittc.ku.edu/~searl/BestPractices/CodingStyle.htm
[...]
    Coding Style Best Practices
    The number one goal of the Coding Style Best Practice
    is to encourage a coding style that results in code that is
    easily understood by a programmer using or modifying the code
    at a future date. The keys to understandable code are correct
    documentation, descriptive variable and function names
    and creation of small modular functions.
[...]
    Function Returns
    Only return from the end of a function's code. Multiple return locations
    within a function create code that is very prone to coding
    errors and makes maintenance difficult.
[...]
---
MKS Inc.
http://www.mks.com/support/productinfo/whitepapers/SQB_Whitepaper.pdf
    Software Quality Benchmark
[...]
    Multiple return statements may complicate the logical flow
    of a function and make it difficult to modify.
---
Department of Communication Systems Engineering - Israel
http://www.cse.bgu.ac.il/courses/Net_Prog/files/Tirgul%201.doc
[...]
    2 Multiple Returns vs One Return
    Having a single return statement at the end of a function has the
    advantage that there is a single, known point which is passed
    through at the termination of execution of the function. It forces
    you to write structured code, using if-then-else statements
    'properly'. It does mean that the code can become deeply indented,
    but if this becomes a problem, you probably should break the
    function into a number of sub-functions.
    Avoid using multiple returns if the code can be just as clearly
    written with one return.
---
CERN - European Organization for Nuclear Research
http://wwwinfo.cern.ch/asd/geant4/G4UsersDocuments/UsersGuides/SoftwareRefer
enceManual/html/CodeConvention/codeConvention.html
[...]
    Avoid multiple return points in a method where possible
    Methods with multiple return points cannot be inlined
    and some compilers (e.g. HP-CC) complain about this.
---
Macadamian inc.
http://www.macadamian.com/codingconventions.htm
[...]
        Each function must have a single entry and exit structure.
        The use of multiple return statements to return control
        from a function may only occur to handle error exits.
        The use of gotos should be avoided at all costs.
    Most programmers agree with these guidelines and they are
    good ideas in principle. In practice however, it is not rare
    to see a routine that needs more than one exit structure.
    The major risk of using returns to prematurely leave a routine
    is to leave some resources behind.
---
Construx Software Inc.
http://www.construx.com/cxone/Construction/CxStand_Code.pdf
[...]
    GEN6.25 Avoid using multiple return statements to exit a function,
except for egregious preconditions
---
Ciao.
*** Azimuth ***

 programmatori.
Quantita' non vuol dire necessariamente qualita'. La pretesa di avere
*per forza* un singolo punto di uscita da una funzione puo' portare a
codice intricato artificialmente; nessuna linea guida puo' sostituire il
buon senso.
In C++ moderno e' comunque pressoche' inevitabile avere piu' punti di
uscita da una funzione, come in qualsiasi linguaggio che supporti le
eccezioni.
Ciao,
Nicola
P.S. Andrei Alexandrescu e' un convinto assertore della generale
*preferibilita'* di avere piu' istruzioni return in una stessa funzione
(ho litigato con lui proprio su questo argomento :-). E mi pare che sul
C++ abbia da insegnare a me, te e a tutti gli autori delle linee guida
che hai indicato.

 programmatori.
[snip]
"Deprecato" non mi sembra deprecata il termine esatto. E' più corretto dire 
che è meglio avere un unico punto di uscita. Io associo al termine 
deprecato il conecetto "in futuro non sarà più supportato..."
Questa questione mi sembra fortemente legata a quella del goto; il goto è un 
costrutto da evitare il più possibile, ma a volte è la soluzione migliore ( 
o meno peggiore se volete :) ).
Analogamente il return multiplo.
Spesso mi sono trovato a scrivere codice di questo tipo:
        // inizializzazione
        while( ){
                // ...
                if( cond ) goto esci;
                for( ){
                        //...
                        if( cond ) goto esci;
                        do {
                                if( cond ) goto esci
        esci:
        // clean-up
        Per interrompere l'iterazione all'interno di un ciclo annidato più volte,       
        esistono poche soluzioni:
        1) break multiplo ( che in C/C++ non esiste )
        2) goto
        3) riarrangiare il codice in questa maniera: e ritorniamo ai return 
                multipli
int a( ){
        while( ){
                // ...
                if( cond ) return ...;
                for( ){
                        //...
                        if( cond ) return ...;
                        do {
                                if( cond ) return ...;
}
int b( ){
        // inizializzazione
        r=a( )
        // clean up
        return r;               
}

 o meno peggiore se volete :) ).
Si ai return multipli (esci se condizione di errore). *MAI* i goto!!! In 
C++, poi, se definisci nuove variabili tra un goto e la sua label si 
incaXXa a morte! 8)
-- 
Luc.

 che hai indicato.
Scusa, ma mi conosci? Io non mi sento in grado di giudicare nessuno, e
siccome sono convinto assertore della reciprocità... :)
Comunque, la bellezza delle scienze (informatica in primis) è che non si
ragiona in termini di "lei non sa chi sono io" piuttosto si cerca di
afferrare qualche brandello di verità, chiunque la scopra. :)
Ciao!
*** Azimuth ***

                 multipli
Non hai risolto! Pensa se la tua routine prende possesso di qualche risorsa
che deve restituire al sistema operativo quando ha finito... orrore... :)
Ciao!
*** Azimuth ***

 afferrare qualche brandello di verità, chiunque la scopra. :)
Se la pensi cosi' perche' non esprimi un ragionamento e lo illustri con
esempi, invece di citare una pletora di documenti dogmatici e
sconosciuti?
Ciao,
Nicola

 sconosciuti?
I ragionamenti e gli esempi mi pare di averli esposti anche nei miei post
precedenti, e in altri thread secondari rispetto a questo. I miei
ragionamenti non mi pare siano stati da te controprovati, quindi direi che
l'onere della prova, se permetti, è tuo.
Ciao.
*** Azimuth ***
[1] Non è propriamente vero, alcuni utilizzano le eccezioni stesse come
veicolo di risultati... ma, in questo caso, non entrano comunque nel merito,
dato che la manipolazione degli stessi viene fatta, di norma, non dal
livello immediatamente superiore.

 l'onere della prova, se permetti, è tuo.
Di che? del fatto che sia meglio seguire il buon senso, piuttosto che
regole espresse da altri, senza una corrispondente dimostrazione della
loro validita'?
Purtroppo (o per fortuna) non e' dimostrabile...
Ciao,
Nicola

 loro validita'?
Mi sembra che, se una teoria, fintanto che non ha almeno una controprova, è
di per sè vera. Nel tuo caso, non mi hai fornito (né tu né Alexandrescu) una
contro prova razionale ma piuttosto un insieme di strani commenti e
opinioni. Direi che mi sembra più che normale che io sia scettico riguardo
il tuo "buonsenso".
Ciao e buon lavoro!
*** Azimuth ***
[1] ETCE = Eliminate Trivial Cases Early
[2] SESE = Single Entry Single Exit
[3] Incredibilmente, anche di velocità: infatti il codice originale tentava
di tenere conto delle Trivial Cases con costrutti che sono stati spazzati
via grazie alla reingegnerizzazione del codice.

 e manutenzione.
Scusa, ma in generale non vedo perchč.
Se ho una semplice funzione tipo:
int funz()
{
    if(funz2()==1 && funz3()=="pippo")
    {
        return 12;
    }
    else
    {
        return 27;
    }
}
Non vedo perchč dovrei arzigolarmi in un codice piů complicato
o variabili temporanee solo per avere un solo return.
Che poi, di solito, sia meglio non disseminare i return nel
codice, č un altro discorso.
Ma da qui a dire che č sempre pratica deprecata ce ne corre...
--
Gerolamo

 
Vedi la critica di sopra, la gestione degli errori diviene troppo 
invasiva... Soprattutto se sono inizializzazione leggere( qualche registro 
hw ), ricorrere a troppe funzioni annidate diviene /pesante/.
Si può fare tutto con if( ) e for( ), solo che costrutti tipo goto/return 
multipli/eccezioni possono ( a patto di essere usati con criterio ) aiutare 
la leggibilità...

 Ma da qui a dire che è sempre pratica deprecata ce ne corre...
CVD.
Ciao!
*** Azimuth ***
[1] SEME = Single Entry Multiple Exit

 chi le libera?
A cosa servono i distruttori?  Il cosiddetto RAII (1) e' un idioma che
tutti i programmatori C++ dovrebbero conoscere.
(1) Resource Acquisition Is Initialisation.  E.g. Stroustrup-3
    pagg. 364 e seguenti --- anche se BS non usa questo acronimo,
    almeno nella mia edizione del 97.
-- 
Maurizio Loreti                         
http://www.pd.infn.it/~loreti/mlo.html
Dept. of Physics, Univ. of Padova, Italy          loreti at pd dot infn dot it

 hw ), ricorrere a troppe funzioni annidate diviene /pesante/.
inline is your friend. :)
Ciao!
*** Azimuth ***

 A cosa servono i distruttori?
In effetti potrebbe essere una soluzione. Ma a questo punto non si viene
meno al principio della località del codice? In altri termini se
l'allocazione si limita alla chiamata della relativa primitiva di
allocazione, ha senso scomodare il C++ con costruttori, distruttori,
exception handling, ecc...? Stiamo valutando, in altro thread, l'impatto che
avrebbe la cascata di chiamate. L'introduzione di un costruttore, per quanto
"inlined" (ma, a questo punto, si viene meno all'information hiding),
porterebbe sicuramente ad un degrado delle performances.
Altra cosa: in questo caso, la mancata allocazione che effetti dovrebbe
avere? Invocazione di una eccezione nel costruttore (ma allora diviene
invasiva per il resto dell'algoritmo, che potrebbe non essere
interrompibile)? Il controllo di un metodo "success" (ma allora ricadiamo
nel caso di controllo surrettizio)?
Ciao!
*** Azimuth ***

 porterebbe sicuramente ad un degrado delle performances.
Non capisco; il costruttore contiene esclusivamente le istruzioni che
comunque useresti per acquisire le tue risorse.  Ne' una di piu' ne'
una di meno --> NESSUN OVERHEAD.  Il vantaggio e' quello di
incapsulare quelle istruzioni in una "struttura logica" (la classe
RAII) in modo che, quando si esce di scopo COMUNQUE (normalmente,
lancio di eccezioni da parte di chicchessia, ...) le risorse vengano
in ogni caso rilasciate (dal distruttore).
E che c'entra l'information hiding?  Ad esempio, se apri un socket
senza RAII avrai delle informazioni esposte (e.g. l'intero che ti
viene ritornato da socket()); l'uso della RAII impedisce ad esempio la
scrittura diretta su quella variabile --- e quindi non puo' che
MIGLIORARE l'information hiding.


 il tuo "buonsenso".
Al contrario, in ambito scientifico una teoria e' vera solo se le stesse
conclusioni sono verificate sperimentalmente in almeno due ambiti
indipendenti.
Ciao,
Nicola

 indipendenti.
Nei hai centinaia.... :)
Ciao.
*** Azimuth ***

 invasiva implicitamente. :)
Se io ho scritto il codice è chiaro che il mio codice è invasivo !!!!
Questa frase si può adattare  al tuo stile visto dal mio punto di vista . 
Scusa ma non aggiungi niente.

   }
if( <*sono* riuscito ad allocare n-1-esima risorsa>) {
  if( <*sono* riuscito ad allocare n-esima risorsa>) {
    <fai qualcosa>
  }
  else {
    <gestisco errore n>
    fail = true;
  }
  <dealloca risorsa n>
else {
  <gestisco errore n-1>
  fail = true;
}
<dealloca risorsa n-1>
if ( fail ) {
  <eventuale gestione globale dell'errore>
}
In C++ probabilmente conviene utilizzare la tecnica RAII.
Ciao,
Nicola

 fiducia a priori.
Si, anche se alcuni esperti si guadagnano sul campo la loro
autorevolezza.
Ciao,
Nicola

 autorevolezza.
Sicuramente. Se permetti, però, questo non cambia la mia frase di una
virgola. La loro autorevolezza guadagnata sul campo non li autorizza a
sparare cazzate a priori. Chiunque deve avere la libertà di verificare. Noi
siamo qui per questo. :)
Ciao
*** Azimuth ***

 istruzione "throw" costituisce un punto di uscita aggiuntivo.
tra l'altro i throw-statement non sono l'unico potenziale problema: 
(quasi) qualsiasi chiamata di funzione puo` potenzialmente rappresentare 
un punto di uscita, sempre a causa di eventuali eccezioni; questo puo` 
IMHO essere un problema serio se il programmatore pensa di poter tenere 
sotto controllo affidandosi al fatto che "c'e` un solo return". Ad 
esempio:
int foo()
{
        int* ptr = new int;
        int result;
        <calcolo del risultato>
        delete ptr;
        return result;
}
ovviamente in presenza di eccezioni non gestite di rischia un memory 
leak. L'impressione e` che non sia semplice costringere una funzione ad 
avere un *unico* punto di uscita, se non complicando artificiosamente il 
codice. Non vedo quindi l'utilita` dello sforzo teso a unificare *parte* 
dei punti di uscita da una funzione, pur sapendo che ne restano comunque 
altri.
ciao Anto
-- 
Antonio Cuni, 20 anni, studente
Brainbench C++ Master: 4.73, higher than 99% of all previous test takers.
C Language: 3.79, higher than 92% of all previous test takers.
Guida C/C++ di Programmazione.it: 
http://cpp.programmazione.it

 }
E' uno stile che non mi piace, ma penso che a questo punto stiamo discutendo 
di gusti; non lo trovo meno valido del mio
 

 nascosti e sia più testabile?
Anche questo e' un apriorismo. Sono d'accordo che una funzione debba
(quasi) sempre assumersi una singola responsabilita', ma cosa sia questa
responsabilita' dipende dal contesto ed e' solo in base al contesto che
si puo' definire qual'e' la dimensione "giusta" di una funzione.
Ciao,
Nicola

 altri.
Il problema fondamentale e' che a mio avviso ci troviamo di fronte ad un
"pendolo vizioso": in principio c'era il codice "a spaghetti", poi
arrivarono quelli della programmazione strutturata col motto "un singolo
ingresso, una singola uscita". Quindi di fronte a certe storture causate
dall'adesione incondizionata a questo stile, c'e' stato un ritorno al
"return multiplo". Anche con questo si e' esagerato e si ritorna
indietro...
"In medio stat virtus" dicevano un tempo. Io aggiungerei "Usa la testa".
Ciao,
Nicola


Che sia una questione di gusti e' una cosa di cui sono pienamente
convinto. Questo e' uno stile al quale bisogna abituarsi. L'aspetto
positivo e' che in genere la gestione delle risorse ha una propria
collocazione naturale.
Ciao,
Nicola

 sapresti se stai intercettanto una eccezione di 'funz2' o di 'funz3'.
Vabbè... ma:
int rc_funz2( funz2() );
string rc_funz3( funz3() );
si puo' fare anche nella mia versione e non influenza il discorso
del singolo o multiplo punto di uscita, o no?
ciao
--
Gerolamo

 si puo' definire qual'e' la dimensione "giusta" di una funzione.
La "responsabilità" mi sembra un concetto sufficientemente semplice da non
richiedere una contestualizzazione per la sua comprensione. E' possibile
comunque che tu abbia ragione nella misura in cui chi questo concetto lo
dovrebbe afferrare con sufficiente sicurezza non vi riesca, per suoi limiti
o per limiti di chi ha progettato/analizzato la problematica. Allora il tuo
discorso potrebbe andare bene. Ma questo, a mio avviso, è solo un modo per
spostare il problema della qualità del software da un'altra parte.
Ciao.
*** Azimuth ***

 scrittura diretta su quella variabile ---
Beh, potrei per esempio effettuare delle assegnazioni, delle copie o avere
degli oggetti temporanei creati dal compilatore per quella classe, tutte
cose che dovrei esplicitamente impedire tramite una dichiarazione privata
dei costrutti relativi...
Ciao!
*** Azimuth ***

 del singolo o multiplo punto di uscita, o no?
Sě, infatti il mio commento sulle eccezioni serviva come pseudo-risposta per
un altro thread. :)
Ciao!
*** Azimuth ***

 problematica SESE.
[cut]
secondo me c'e` anche un altro aspetto da non sottovalutare; considera 
questo esempio:
bool is_sorted(int v[], int n)
{
        for(int i=0; i < n-1; i++)
                if (v[i] > v[i+1]) return false;
 
        return true;
}
se vuoi utilizzare un solo return e vuoi evitare di fare calcoli inutili 
(del tipo continuare a scorrere tutto il vettore anche quando si sa gia` 
il risultato) devi trovare un modo per interrompere il ciclo: in questo 
caso e` facile, dato che basta un break. Ma non sempre e` cosi`:
const int n = 100;
bool is_zero_filled(int v[][n])
{
        for(int i=0; i < n; i++)
                for(int j=0; j<n; j++)
                        if (v[i][j] != 0) return false;
        return true;
}
come fai in questo caso?


 come fai in questo caso?
Farei cosě:
const int n = 100;
bool is_zero_filled(int v[][n]) {
    bool rc( true );
    for(int i=0; ( rc ) && ( i < n ); i++)
        for(int j=0; ( rc ) && ( j < n ); j++)
            if (v[i][j] != 0) rc = false;
    return rc;
}
Ciao!
*** Azimuth ***

}
E questo è più leggibile?
-- 
Marco Barisione (ICQ 84277092)

 E questo è più leggibile?
Beh, impaginato così, con un carattere proporzionale non è proprio il
massimo...
Comunque, è più manutenibile (funzione SESE). Magari riscriviamolo così:
const int MAX_ROWS = 100;
const int MAX_COLUMNS = 100;
bool is_zero_filled(int v[][n]) {
        // Return "true" if the matrix is filled by all zeroes...
        bool is_still_zero( true );
        // Move along all the rows of the matrix... until we found that
matrix
        // is still filled by zero AND rows are not finished...
        for( int row = 0; ( is_still_zero ) && ( row < MAX_ROWS ); ++row )
                // Move along all the columns of the matrix' row... until we
found that matrix
                // is still filled by zero AND columns are not finished...
                for(int column = 0; ( is_still_zero ) && ( column <
MAX_COLUMNS ); ++column )
                        if ( v[ row ] [column ]  )
                                is_still_zero = false;
        return is_still_zero;
}
Ecco, adesso mi sembra leggermente più leggibile...
Ciao!
*** Azimuth ***

Ecco, adesso mi sembra leggermente piů leggibile...
Io continuo a ritenere piů leggibile l'altra forma, probabilmente č
una questione di gusti ma comunque nella forma usata da Antonio si
ottiene lo stesso risultato con meno codice e penso che anche questo
fatto aumenti molto la leggibilitŕ.


 fatto aumenti molto la leggibilità.
Meno codice sicuramente non è indice di maggiore leggibilità.... lo dimostra
l'obfuscated C contest... :)
Ciao!
*** Azimuth ***

"Azimuth" ha scritto
:
: > >
: > >const int MAX_ROWS = 100;
: > >const int MAX_COLUMNS = 100;
: > >
: > >bool is_zero_filled(int v[][n]) {
: > >
: > >        // Return "true" if the matrix is filled by all zeroes...
...
: De gustibus, comunque sicuramente è più manutenibile questa. Esempio di
: modifica dove la tua "leggibilità" sacrifica la manutenibilità: supponi
che
: l'algoritmo debba essere modificato per controllare che vi siano almeno
una
: riga E una colonna diverse da zero con indici 'row' e 'column' ovviamente
: differenti. Siccome quella con il return nel loop più interno restituisce
: subito il valore, è necessario stravolgerla completamente. Con questa,
basta
: aggiungere una condizione in più all'interno del loop più largo e legare
: alla soddisfazione della condizione il valore di 'is_still_zero'.
:
Abbiamo idee diverse riguardo la manutenibilita` del codice.
Penso che come minimo dovresti modificare anche il nome della funzione.
Spero che tu non voglia chiamare "is_zero_filled" una funzione che deve
controllare che vi siano almeno una riga E una colonna diverse da zero
con indici 'row' e 'column' ovviamente differenti. E se cambi il nome
devi modificare anche il codice chiamante. Eppoi forse dovresti modificare
anche qualche commento.
Il codice ben scritto non viene modificato, ma esteso. Se hai bisogno
di una funzione che faccia una cosa diversa scrivi un'altra funzione.
Se puoi utilizzare una funzione gia` scritta, perche` svolge in parte
il nuovo compito, la chiami.
BTW quali sono le best practices a proposito delle variabili globali?
I nomi tutti maiuscoli non sono pericolosi. Se includi un header in cui
e` presente un
#define MAX_ROWS 1000
il tuo codice non compila piu`.
ciao
Massimo

 il tuo codice non compila piu`.
Non era comunque un mio codice. :)
Ciao!
*** Azimuth ***
[1] L'ho inventato io, non cercatelo nella letteratura. :))))

"Azimuth" ha scritto
:
: Ovvio. Pero', se devo modificare proprio QUELLA funzione, e non
: scriverne un'altra, magari perche' (nome a parte) e' un algoritmo
: un pelino piu' complesso di questo, e allora il discorso ritorna.
:
: Quello che voglio dire (e qui vado in contrasto con quanto detto da
: Nicola Musatti) e' che non e' possibile pensare di adottare due
: condotte distinte a seconda della complessita', altrimenti si
: creano delle zone "cuscinetto" dove puo' prosperare del codice
: scritto male. :)
:
:
A me pare invece che uno dei punti di forza del C++ e` che ti permette
di aggiungere un return in mezzo ad una funzione senza pregiudicare la
robustezza del codice.
Se uno scrive codice exception-safe, prevedendo quindi che possano
esistere esiti eccezionali, in pratica si garantisce la possibilita`
di aggiungere un return in mezzo al codice, con minima probabilita`
di memory leak, dead-lock etc. Sara` il distruttore a rilasciare
le risorse allocate e sara` il compilatore a garantire che tutti i
percorsi di esecuzione passino per le routines di rilascio.
Quando hai inglobato tutte le chiamate critiche (new, stream, mutex etc.)
in oggettini dotati di distruttore appropriato non e` certo la presenza di
piu` return che qualifica il codice come scritto male, illeggibile o
immantenibile.
Se e` piu` semplice e chiaro avere piu` punti d'uscita, ben vengano
purche` il resto sia scritto come si deve.
ciao
Massimo

 }
Ecco.
Questo č proprio uno dei casi in cui non sarei fondamentalista.
Infatti ritengo la versione di Cuni piů chiara (e per questo
mantenibile) della tua.
Ritengo inoltre improbabile che una funzione del genere
debba subire delle modifiche come quelle che supponi tu
nel seguito del thread, quindi la farei decisamente con due return
(o, per usare la tua terminologia, SEME :-) )
--
Gerolamo

 Mai usarne. 
consiglio numero 1 di tutti i manuali di stile: non prendere mai nulla 
come oro colato e come regola da seguire sempre e comunque. Usare 
soprattutto il buon senso per capire quando fare una cosa e quando non 
farla.
Ogni volta che si parla di stile e leggo le paroline "mai", "sempre", "in 
ogni caso" mi iniziano a sorgere dubbi.


 Farei cosě:
[cut]
direi che abbiamo opinioni differenti sulla leggibilita` del codice.


 purche` il resto sia scritto come si deve.
Alla fine, meglio che tutto sia scritto come si deve. :)
Ciao,
*** Azimuth ***
[1] Codice che sia il piu' possibile aderente ai requisiti stabiliti in 
fase di analisi e di progettazione.
[2] Seppure con i distinguo emersi in altro thread.
[3] Per esempio, i distruttori possono essere chiamati in ordine diverso a 
seconda se si percorre o no il braccio del return. Inoltre, modifiche alle 
risorse derivate da codice eseguito DOPO il return puo' portare a 
interferenze sui risultati se il return viene o no eseguito, il che 
determina necessariamente una analisi piu' approfondita di quella che 
viene suggerita da un approccio SESE.

 ogni caso" mi iniziano a sorgere dubbi.
Beh, adesso non ne facciamo uno psicodramma... ;-)
*** Azimuth ***

 (o, per usare la tua terminologia, SEME :-) )
Perfetto. Pero', IMHO, cambiamo l'orizzonte della problematica. A questo 
punto, credo che sia inutile persino uno stile... :-) 
*** Azimuth ***

 direi che abbiamo opinioni differenti sulla leggibilita` del codice.
Direi che hai ragione. Se poi l'informatica avra' fortuna, prevarra' 
l'opinone latrice di maggior benefici.
*** Azimuth ***

 ogni caso" mi iniziano a sorgere dubbi.
Un dubbio nel dubbio... :D
--
Luigi
--
void f (OS x) {while (x.getbugs() > 0); destroy(earth);};
f (windows); // Meno male!

 Ovviamente, tutte queste caratteristiche contemporaneamente... :)
dunque, il primo parametro (hInstance) della funzione:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR
lpCommandLine, int cmdShow);
questo č il primo esempio che mi viene in mente.

 questo è il primo esempio che mi viene in mente.
Partiamo dal presupposto che conosco poco le sovrastrutture di Windows, 
e dato che siamo in un NG sul C++ direi che mi devo tenere sul generico.
Comunque, una soluzione piu' elegante credo che sia quella di creare 
una classe "ad hoc" (che manterra' copia di questi parametri) ed un 
wrapper per WinMain, con approccio Singleton, di modo che alla partenza 
vengano passati i parametri ad un oggetto che in realta' permarra' 
statico e read-only durante l'esecuzione del programma (e tra l'altro 
verra' creato proprio nel momento opportuno). L'oggetto potra' a quel 
punto esser messo fisicamente dovunque si voglia  (consiglio modulo 
"ad hoc") e sara' referenziato secondo le regole del namespacing con 
classi. Qualcosa del tipo:
	...
	...( GLOBALS::hInstance(), ...
	...
Comunque, la manutenibilita' e' garantita dal fatto che lasci che il 
programmatore stabilisca quali parametri saranno modificabili o 
accessibili oppure no (questo non e' possibile con una variabile
globale), oltre che poter modificare un solo punto nel codice 
laddove sia necessaria fornire una logica associata all'accesso 
al parametro medesimo (idem).
La leggibilita' deriva dal fatto che specifici a chi appartiene
hInstance, ovvero ai parametri globali dell'ambiente, il che
permette di avere degli "alias" locali (del tipo "hInstance" per
indicare la copia locale dell'handle dell'instance di GLOBALS).
 
La robustezza deriva dal fatto che la variabile globale non 
sara' accessibile direttamente ma solo attraverso chiamate 
statiche che restituiranno una copia oppure un riferimento non 
modificabile (const) al parametro stesso. 
Infine l'efficienza l'ottieni con un accorto uso dell'inlining.
Ciao!
*** Azimuth ***


a parte per la leggibilità e l'efficienza[*], sono d'accordo su tutti
i punti, ma è proprio necessaria un'infrastruttura del genere per
tenere traccia di quella che in fondo è solo una (di numero)
variabile? vale la pena di tutte queste complicazioni solo per avere,
in fondo, una funzione che ti resituisce un valore con l'aggiunta del
const? guarda:
< main.h>
HINSTANCE const getInstance();
</main.h>
<main.cpp>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)    {
    gl_instance = hInstance;
    // ... programma ...
}
HINSTANCE const getInstance()    {    return gl_instance; }
</main.cpp>
praticamente lo stesso risultato.
[*]:
qualunque programmatore di Windows conosce quella variabile, quindi
scrivendo
//    copy of WinMain's hInstance parameter
HINSTANCE gl_instance;
la leggibilità è garantita. Questo è molto più leggibile di una serie
di classi, pattern Singleton e namespace vari. Per quel che riguarda
l'efficienza non è discutibile che leggere la variabile gl_instance
sia più veloce che non inizializzare un singleton, chiamare la
getInstance e successivamente la getHInstance()...

"Azimuth" ha scritto
: >: Quello che voglio dire (e qui vado in contrasto con quanto detto da
: >: Nicola Musatti) e' che non e' possibile pensare di adottare due
: >: condotte distinte a seconda della complessita', altrimenti si
: >: creano delle zone "cuscinetto" dove puo' prosperare del codice
: >: scritto male. :)
: > A me pare invece che uno dei punti di forza del C++ e` che ti permette
: > di aggiungere un return in mezzo ad una funzione senza pregiudicare la
: > robustezza del codice.
:
: Qui direi che sia da chiarire che esistono due aspetti (o forse tre) che
: vanno affrontati nella scrittura di codice "ottimo"[1]:
:
: * Manutenibilita'
: * Efficienza
: * Robustezza
:
: Ora, mentre per il terzo punto possiamo considerare conclusa la questione
: con l'adozione del RIIA[2], direi che i primi due non si possono
: realizzare con l'adozione del "return in mezzo ad una funzione".
:
Non vedo perche` mai l'efficienza debba essere, a priori, penalizzata da
piu` exit point. Hai riveduto una semplice funzione che al piu` faceva
(N+1)**2 + N+1 + N**2 test con una che al piu` ne fa 2*(N+1)**2 + 2*(N+1)
+ N**2. (Se non ho sbagliato i conti:-)
Semmai e` la tua versione, forzatamente SESE, che e` penalizzata.
Nota che si trattava di una funzione che non modifica alcuna risorsa
dopo il return, e che non richiede analisi approfondite. Era una funzione
naturalmente SE2E.
Se vuoi, questo te lo concedo, la tua versione e` piu` manutenibile perche`
offre piu` opportunita` di essere modificata, potendosi rimuovere un maggior
numero di parti superflue:)
ciao
Massimo

 numero di parti superflue:)
Ahahah, buona questa.
Comunque, tornando seri, la tua il mio compilatore non riesce ad
ottimizzarla. Che fare?
Ciao!
*** Azimuth ***
[1] Per esempio, il return in mezzo alla funzione inibisce il loop
unrolling.

 *** Azimuth ***

 scritto male. :)
Infatti io auspico sempre una *singola* condotta: scrivere il codice nel
modo piu' chiaro possibile. Solo che il modo piu' chiaro possibile
dipende dal contesto.
Ciao,
Nicola

 farla.
Questo e' oro colato! :-)
Ciao,
Nicola

 l'indifendibilita' della tua tesi l'hai trovato...
Io direi di finirla qui, va.
Spero solo di non dover mai mettere mano a codice scritto da te.
*** Azimuth ***

"Azimuth" ha scritto
:
: Comunque, tornando seri, la tua il mio compilatore non riesce ad
: ottimizzarla. Che fare?
:
Non era mia!!
Io in questo caso avrei proposto qualcosa di piu` semplice, piu` chiaro
ed efficiente, qualcosa (rigorosamente SEME;-) tipo:
bool is_zero_filled(int v[][n])
{
  using namespace std;
  if (find_if(&v[0][0], &v[n][0],
              bind1st(not_equal_to<int>(),0)) == &v[n][0])
    return true;
  return false;
}
E questa e` gia` superottimizzata, per definizione:)
ciao
Massimo

 }
E' evidentemente SESE ;-)
Ciao,
Nicola

"Nicola Musatti" ha scritto
: >
: > Io in questo caso avrei proposto qualcosa di piu` semplice, piu` chiaro
: > ed efficiente, qualcosa (rigorosamente SEME;-) tipo:
:
: Zappa sui piedi, amico mio :-)
:
: > bool is_zero_filled(int v[][n])
: > {
: >   using namespace std;
:
:     return find_if( &v[0][0], &v[n][0], bind1st(not_equal_to<int>(),0) )
:            == &v[n][0];
:
: > }
:
: E' evidentemente SESE ;-)
:
Eh eh, ma cosi` ti perdi il gusto di evitare le best practices, vuoi
mettere?
ciao
Massimo


Mi spiace che ti sia offeso, ma prova a pensare l'effetto che il tuo
post fa su chi lo legge. Si discute della leggibilita' di esempi di
quattro/sei righe di codice e mi vieni fuori con questo:
const int MAX_ROWS = 100;
const int MAX_COLUMNS = 100;
bool is_zero_filled(int v[][n]) {
        // Return "true" if the matrix is filled by all zeroes...
        bool is_still_zero( true );
        // Move along all the rows of the matrix... until we found
that<A capo>
matrix
        // is still filled by zero AND rows are not finished...
        for( int row = 0; ( is_still_zero ) && ( row < MAX_ROWS ); ++row
)
                // Move along all the columns of the matrix' row...
until we<A capo>
found that matrix
                // is still filled by zero AND columns are not
finished...
                for(int column = 0; ( is_still_zero ) && ( column <<A
capo>
MAX_COLUMNS ); ++column )
                        if ( v[ row ] [column ]  )
                                is_still_zero = false;
        return is_still_zero;
}
Ho segnato con <A capo> dove le tue istruzioni si interrompono nel mio
newsreader. Leggibile? Io mi sento preso in giro. Hai aggiunto una serie
di commenti quasi tutti inutili o fuorvianti, in inglese in un newsgroup
italiano. A me pare che il sospetto che tu, magari inconsapevolmente,
volessi confondere le acque sia legittimo.
Esaminiamo poi il codice che presenti:
const int MAX_ROWS = 100;
const int MAX_COLUMNS = 100;
Come ti e' gia' stato segnalato e' uso comune riservare gli
identificatori con sole lettere maiuscole a macro del preprocessore.
Questo perche' siccome la loro sostituzione non rispetta alcuno scope
non c'e' altro modo di arginare il rischio di interferenze.
bool is_zero_filled(int v[][n]) {
// Return "true" if the matrix is filled by all zeroes...
    bool is_still_zero( true );
Questo commento, posto qui, e' fuorviante, perche l'istruzione alla
quale pare associato non e' un return.
// Move along all the rows of the matrix... until we found that
// matrix is still filled by zero AND rows are not finished...
    for ( int row = 0; ( is_still_zero ) && ( row < MAX_ROWS ); ++row )
    {
Questo commento e' del tutto inutile, perche' non fa che ripetere quello
che fa l'istruzione sottostante. I commenti dovrebbero servire a
illustrare quegli aspetti che non traspaiono immediatamente dal codice.
// Move along all the columns of the matrix' row... until we found that
// matrix is still filled by zero AND columns are not finished...
Come sopra.
        for ( int column = 0; ( is_still_zero ) && ( column <
MAX_COLUMNS );
                ++column )
        {
            if ( v[ row ] [column ]  )
                is_still_zero = false;
        }
    }
    return is_still_zero;
}
Mi sono permesso di ridurre l'indentazione e aggiungere qualche graffa:
a mio avviso e' un modo molto efficace per rendere il codice leggibile.
Ciao,
Nicola
P.S. In inglese si dice "find". "Found" e' passato remoto o participio
passato. Per la precisione.

 a mio avviso e' un modo molto efficace per rendere il codice leggibile.
Meno male che il tuo avviso non e' diffuso.
*** Azimuth ***

 Meno male che il tuo avviso non e' diffuso.
Lo e', lo e', per fortuna.
Ciao,
Nicola

 Se una istruzione e' troppo lunga per stare su una sola riga, cosa fai?
Cos'è una riga? Quanto è lunga?
-- 
Luc.

 Cos'è una riga? Quanto è lunga?
Una porzione di un file sorgente compresa tra due newline. Esistono
convenzioni diverse per la lunghezza. Il tramonto delle stampanti ad
aghi a 132 colonne porta a consigliare l'adozione di convenzioni che
impongano un limite alla lunghezza delle righe del codice entro circa 80
caratteri.
c(-: (e' la cosa piu' simile ad una linguaccia che mi sia venuta in
mente :-)
Ciao,
Nicola

"Azimuth" ha scritto
:
: > const int MAX_ROWS = 100;
: > const int MAX_COLUMNS = 100;
: > Come ti e' gia' stato segnalato e' uso comune riservare gli
: > identificatori con sole lettere maiuscole a macro del preprocessore.
:
: Veramente e' uso comune NON usare le MACRO (in C++), visto che ci sono i
: const e le funzioni inline, che per giunta garantiscono che i parametri
: siano del tipo opportuno. Ma, ovviamente, tu sei per usare la testa...
:
Il problema non e` quello di usare o meno le MACRO, ma degli header che
tu includi, e di quelli che vengono inclusi indirettamente.
Sia perche` e` possibile usare header di codice C, sia perche` anche
vecchio codice C++ faceva largo uso del preprocessore, esiste un minimo
rischio di conflitto tra nomi.
Soprattutto se vuoi ragionare in termini di manutenibilita`.
Immagina che il tuo progetto compili e funzioni sulla piattaforma abc,
su cui e` disponibile la libreria xyz che usi. Supponi ora di doverlo
portare sulla piattaforma def, su cui non e` disponibile la libreria xyz,
ma la zyx, che va altrettanto bene, ma che usa il preprocessore definendo
proprio quegli stessi nomi che tu usi per le tue costanti.
Sei costretto[1] a modificare il tuo codice in ogni punto in cui hai usato
quei nomi -> il tuo codice diventa codice da modificare (=manutenibile).
Se invece ti cauteli in anticipo, evitando anche questi (poco probabili)
conflitti, il tuo codice deve essere modificato un po' meno (=un po' meno
mautenibile;-)
ciao
Massimo
[1] Invero, puoi anche modificare la libreria xyz, in modo che funzioni
    anche sulla piattaforma def (sempre codice da modificare e`).

 una certa complessita'.
Spiacente deluderti. Non supero mai tre livelli. :)
*** Azimuth ***

 caratteri.
Questo pero' non c'entra molto con la indentazione a graffe seguita dal tuo
stile.
*** Azimuth ***

 proprio quegli stessi nomi che tu usi per le tue costanti.
Vedi sopra. Comunque non uso librerie che non siano programmate con
criterio, anche se apparentemente farei prima: quello che risparmio ora lo
paghero' poi. Tanto e' vero che nelle mie applicazioni il 95% delle librerie
e' autocostruito. Funziona anche meglio, a dire il vero...
Ciao!
*** Azimuth ***

 Spiacente deluderti. Non supero mai tre livelli. :)
Non sono sicuro che ci siamo capiti. Io intendo dire che dovendo
includere nelle tue condizioni il controllo sulle variabili guardia
dovrai scrivere per lo meno
for ( i = 0; nome_variabile && altra_condizione_di_terminazione; ++i )
Se la condizione e' complessa o se la variabile di controllo ha un nome
significativo la tua istruzione si estende.
Ciao,
Nicola

 significativo la tua istruzione si estende.
Mah.
*** Azimuth ***

"Azimuth" ha scritto
:
: > Sia perche` e` possibile usare header di codice C, sia perche` anche
: > vecchio codice C++ faceva largo uso del preprocessore, esiste un minimo
: > rischio di conflitto tra nomi.
:
: Beh, allora basta fare cosi':
:
: #undef    MAX_ROWS
: const int MAX_ROWS = 100;
Ami il rischio, prendiamo un caso concreto:
#include <windows.h>
#undef MAX_PATH
const int MAX_PATH = 100;
:
: > Immagina che il tuo progetto compili e funzioni sulla piattaforma abc,
: > su cui e` disponibile la libreria xyz che usi. Supponi ora di doverlo
: > portare sulla piattaforma def, su cui non e` disponibile la libreria
xyz,
: > ma la zyx, che va altrettanto bene, ma che usa il preprocessore
definendo
: > proprio quegli stessi nomi che tu usi per le tue costanti.
:
: Vedi sopra. Comunque non uso librerie che non siano programmate con
: criterio, anche se apparentemente farei prima: quello che risparmio ora lo
: paghero' poi. Tanto e' vero che nelle mie applicazioni il 95% delle
librerie
: e' autocostruito. Funziona anche meglio, a dire il vero...
:
Non devono essere librerie esotiche, bastano quelle di sistema.
ciao
Massimo

 Non devono essere librerie esotiche, bastano quelle di sistema.
Infatti non uso neanche quelle[1]. <grin>
Ciao!
*** Azimuth ***
[1] La gestione della memoria, ad esempio, usa esclusivamente le chiamate
alle primitive del compilatore (new...delete). I sistemi di sincronizzazione
(mutex, semafori, code, ....) tutti resi astratti dalla libreria, e quel
poco che dialoga con il sistema e' ben isolato e protetto. Rischi di
conflito == 0.

 Che te ne fai se usi std::string?
In che senso?
 
-- 
Luc.

 In che senso?
Nel senso che deduco che, da qualche parte tra le piege degli include di
Windows, ci sia qualcosa del tipo:
...
int GetDirectoryCount( const char _path[ MAX_PATH ] );
...
E che quindi GetDirectoryCount si aspetti al piu' MAX_PATH caratteri. Ma se
tu utilizzi string, puoi passare
    ...
    std::string path;
    ...
    GetDirectoryCount( path.c_str() );
    ...
... e quindi MAX_PATH non ti serve piu'...
A meno che tu non intenda limitare la stringa per essere sicuro che
GetDirectoryCount( ... ) abbia esito positivo. Nel qual caso, pero', ci sono
due approcci:
* Approccio conservativo: ti confronti rispetto alle specifiche dichiarate
da tutti gli ambienti che supporti, e fai il minimo tra tutti i potenziali
    MAX_PATH, dopo di che modifichi il codice come segue:
...
// This is the longest PATH supported by underlying O.S.. Please MODIFY
accordingly.
const int OS_SPECIFIC_MAX_PATH = 260;
...
    std::string path;
    ...
    path.resize( 0, OS_SPECIFIC_MAX_PATH );
    GetDirectoryCount( path.c_str() );
    ...
* Approccio evolutivo: crei un wrapper attorno alle funzioni per l'accesso
al file system, che in base alle direttive
    del preprocessore inizializzano la costante C++ OS_SPECIFIC_MAX_PATH.
Del tipo:
...
// This is the longest PATH supported by underlying O.S.. Please ADD other
limits accordingly.
#ifdef _LINUX_
    const int OS_SPECIFIC_MAX_PATH = 1024;
#else
#ifdef _WINDOWS_
    const int OS_SPECIFIC_MAX_PATH = 260; // or MAX_PATH, if you prefer...
#endif
---
Ovviamente, suggerisco di condire il secondo approccio con un po' di
zucchero sintattico e di shekerare il tutto dentro un solo modulo isolato...
;-)
Ciao!
*** Azimuth ***

 [MEGA-CUT]
Ne hai di tempo libero per insegnarci ovvietà, eh...? :)
-- 
Luc.


dove lavoro io,(in questo momento sono presso un ente pubblico) questo è un
problema che si può presentare, in quanto l'ente ha macchine con sistemi
UNIX, LINUX e e SYSTEM V, inoltre sono anche di versioni diverse tra loro, a
questo, aggiungi, che ogni macchina ha un suo compilatore, che, ovviamente,
e sempre nel caso più fortunato, non ha la stessa versione degli altri, e
quindi sei costretto, praticamente, a dover ricompilare ogni volta che
installi il tuo codice su una macchina diversa, visto che si lavora
nell'ottica del riutilizzo del codice.
--
Ciao.
Maurizio
P.S.: eliminare _!NO$PAM! per rispodere in privato

 nell'ottica del riutilizzo del codice.
D'altra parte, non riesco ad immaginarmi esattamente la natura di
applicazioni che vadano ricompilate in ambienti differenti per svolgere lo
stesso lavoro. Un tipo di macchina un tipo di lavoro, e' il mio motto... Ma
magari parliamo di transizioni da una piattaforma all'altra, nel qual caso
il lavoro di adattamento e' pressocche' inevitabile, ma sicuramente non
giustifica l'adozione di macro...
Ciao!
*** Azimuth ***

 Ne hai di tempo libero per insegnarci ovvietà, eh...? :)
Mah, sai... se hai ancora di questi dubbi, tanto ovvieta' non sono... per
te, ovviamente...
Il che chiude anche il discorso... e concludiamo che le MACRO sono inutili e
siamo liberi di dare nomi alle costanti con caratteri maiuscoli.
*** Azimuth ***

 siamo liberi di dare nomi alle costanti con caratteri maiuscoli.
E siamo anche liberi di usare le macro... Oppure la libertà è a 'tocchi'?
-- 
Luc.

 E siamo anche liberi di usare le macro... Oppure la libertà è a 'tocchi'?
No, no... fai pure... come diceva Moretti, "continuiamo cosi', facciamoci
del male"...
*** Azimuth ***

 del male"...
L'unico Moretti che 'conosco' è quello della birra... 8-PPP
-- 
Luc.

 codice altrui. Quindi direi che i commenti ci stanno tutti.
"Non descrivere l'ovvio: I commenti non dovrebbero riportare informazioni 
palesemente evidenti come il fatto che i++ ha incrementato i"
(B.W. Kernighan/R. Pike, "Programmazione nella pratica", capitolo 1 - 
"Stile")
ciao Anto
-- 
Antonio Cuni, 20 anni, studente
Brainbench C++ Master: 4.73, higher than 99% of all previous test takers.
C Language: 3.79, higher than 92% of all previous test takers.
Guida C/C++ di Programmazione.it: 
http://cpp.programmazione.it

 "++linea_molto_lunga..." NON si trova sulla stessa colonna delle graffe.
Resta che comunque e' un contesto un po' scarsino per capire dove si
trova il problema. A meno che tutti i tuoi nomi di variabili non siano
prefissi con un identificativo della funzione in ci sono definiti.
 
Ciao,
Nicola

 L'unico Moretti che 'conosco' è quello della birra... 8-PPP
Beh, ogni tanto dovresti pure mollare la tastiera e fare qualcosa di
ricreativo, senno' ti verrano gli occhi a forma di monitor... 8-PPP
*** Azimuth ***

 ricreativo, senno' ti verrano gli occhi a forma di monitor... 8-PPP
Dici che dovrei passare più tempo con mia figlia (2 mesi e 1/2)...? :-)))
-- 
Luc.

 prefissi con un identificativo della funzione in ci sono definiti.
Non hai capito. Ti consiglio caldamente di rileggere il thread per vedere
dove ti sei perso.
Ciao.
*** Azimuth ***

 "vari compilatori"...
si in effetti utilizziamo il gcc. Però adesso il "capo" ha voluto
riutilizzare delle classi, che avevo costruito per un'altro progetto,
purtroppo, si cambia server che ha una versione del compilatore più vecchia,
io, attualmente uso la 2.95.3 20010315 (release), sul server "nuovo" (si fa
per dire) ho trovato la 2.95.2 19991024 (release), che mi ha dato problemi
su una mappa: map<string, map<string, string> >, che ho dovuto modificare
sostituendo string con const char *.     ...ciò ha comportato la modifica
(leggi riscrittura) di alcune classi che utilizzano quella mappa.
Alla domanda, aggiorniamo il compilatore, tralaltro siamo già alla 3...,  mi
hanno rispostoto che tanto dovevano cambiare il sistema operativo, quando ?
In futuro....


 "Stile")
Oddio... mi pare un po' estrema come giustificazione. Io non ho mica
commentato l'ovvio... ;-)
*** Azimuth ***

 non e' uno strumento valido? Ma daiii...!
Esatto.
 
Ciao,
Nicola

"Azimuth" ha scritto
:
: > Ami il rischio, prendiamo un caso concreto:
: > #include <windows.h>
: > #undef MAX_PATH
: > const int MAX_PATH = 100;
:
: Cos'e' MAX_PATH?
:
: Non sara' mica la dimensione massima di un percorso con il filesystem di
: Windows? Che te ne fai se usi std::string?
Se l'utente volesse salvare i dati in una nuova directory potrei
segnalare che deve inserire un nome non piu` lungo di ..., almeno
quando quello inserito supera MAX_PATH.
:
: > : Vedi sopra. Comunque non uso librerie che non siano programmate con
: > : criterio, anche se apparentemente farei prima: quello che risparmio
ora
: lo
: > Non devono essere librerie esotiche, bastano quelle di sistema.
:
: Infatti non uso neanche quelle[1]. <grin>
:
: Ciao!
:
: *** Azimuth ***
:
: [1] La gestione della memoria, ad esempio, usa esclusivamente le chiamate
: alle primitive del compilatore (new...delete). I sistemi di
sincronizzazione
: (mutex, semafori, code, ....) tutti resi astratti dalla libreria, e quel
e la libreria..., chi l'ha scritta..., che ha fatto? Qualche header
di codice C lo avra` pure incluso, soprattutto se per astratti intendi
indipendenti dalla piattaforma.
: poco che dialoga con il sistema e' ben isolato e protetto. Rischi di
: conflito == 0.
:
Comunque se sei consapevole di avere annullato anche quella minima
probabilita`, puoi stare tranquillo. Usa le maiuscole, risalteranno
meglio in mezzo al codice, proprio come se venissero strillate.
Mi spiace che tu sostenga pubblicamente questa tua "Worst Practice",
qualcuno alle prime armi potrebbe formarsi opinioni pericolose...
... e non tutti possono permettersi di autoricostruirsi un sistema
da zero;-)
ciao
Massimo

"Azimuth" ha scritto
:
: > > Cos'e' MAX_PATH?
: > > Non sara' mica la dimensione massima di un percorso con il filesystem
di
: > > Windows?
: > Si... vale 260 (caratteri).
:
: E a che serve?
:
: > > Che te ne fai se usi std::string?
...
:
: * Approccio evolutivo: crei un wrapper attorno alle funzioni per l'accesso
: al file system, che in base alle direttive
:     del preprocessore inizializzano la costante C++ OS_SPECIFIC_MAX_PATH.
: Del tipo:
:
: ...
: // This is the longest PATH supported by underlying O.S.. Please ADD other
: limits accordingly.
:
#undef MAX_PATH
const int MAX_PATH=100;
: #ifdef _LINUX_
:
:     const int OS_SPECIFIC_MAX_PATH = 1024;
:
: #else
:
: #ifdef _WINDOWS_
:
:     const int OS_SPECIFIC_MAX_PATH = 260; // or MAX_PATH, if you prefer...
// certo che preferisco ;-)
// a meno che tu non voglia illustarci pro e contro dell'uso dei
// _*MAGIC*_ _*NUMBERS*_ nel ventunesimo secolo ?-)
     const int OS_SPECIFIC_MAX_PATH = MAX_PATH; // oops :-((
:
: #endif
:
Se ho capito bene OS_SPECIFIC_MAX_PATH dovrebbe essere il piu` lungo
PATH supportato dal sistema operativo sottostante. Scusa una domanda,
ma che te ne fai se usi std::string?
ciao
Massimo

 In futuro....
Beh, dai... aggiornare un compilatore e' questione di poco...
Ciao!
*** Azimuth ***

 ma che te ne fai se usi std::string?
Infatti non me ne fo' nulla, io.
*** Azimuth ***

 da zero;-)
Pero' possono permettersi di migliorarlo. Stiamo qui per questo.
*** Azimuth ***

"Azimuth" ha scritto
:
: > : Non sara' mica la dimensione massima di un percorso con il filesystem
di
: > : Windows? Che te ne fai se usi std::string?
: > Se l'utente volesse salvare i dati in una nuova directory potrei
: > segnalare che deve inserire un nome non piu` lungo di ..., almeno
: > quando quello inserito supera MAX_PATH.
:
: Da quando il common dialog te lo permette? Ah, ma tu glielo chiedi a botte
: di cin e cout... :-))))
:
Un server (p.e. FTP) potrebbe trovare altre vie per chiederlo, vie in
qualche modo simili a cin e cout.
Ed anche se l'utente e` ad un tiro di mouse e devo sapere solo il nome
della directory che deve essere creata, posso usare una dialog box,
che non faccia parte delle Common Dialog.
Che so, una dialog che raccoglie tutti i settaggi di configurazione.
Oppure potrebbe trattarsi di un file di configurazione, una key nel
registro, una variabile d'ambiente, oppure perche` no ... a colpi di
cin e cout.
Comunque di esempi in cui puoi volere testare che un file name
ricevuto dall'utente non sia piu` lungo di MAX_PATH ne puoi trovare
fin che vuoi.
E se ti riferisci a GetSaveFileName a giudicare dal SDK: Windows 95
ti permette di intercettare la pressione dell'OK nel caso tu debba
fare controlli particolari.
CDN_FILEOK
The CDN_FILEOK notification message is sent by an Explorer-style
Open or Save As common dialog box when the user specifies a filename
and clicks the OK button. Your OFNHookProc hook procedure receives
this message in the form of a WM_NOTIFY message.
...
Return Values
If the hook procedure returns zero, the dialog box accepts
the specified filename and closes.
...
: > : [1] La gestione della memoria, ad esempio, usa esclusivamente le
: chiamate
: > : alle primitive del compilatore (new...delete). I sistemi di
: > sincronizzazione
: > : (mutex, semafori, code, ....) tutti resi astratti dalla libreria, e
quel
: > e la libreria..., chi l'ha scritta..., che ha fatto?
:
: L'ho scritta io. E bene.
:
: > Qualche header
: > di codice C lo avra` pure incluso,
:
: Solo nel codice di IMPLEMENTAZIONE. Non certo in quello di dichiarazione.
Ed in quello le collisioni non danno fastidio, o non usi nomi di costanti
tutti maiuscoli.
ciao
Massimo

 sostituzioni "in linea") siano da evitare.
ancora con le Leggi Assolute. Come per tutte le altre cose bisogna 
valutare di volta in volta; certamente se esiste una alternativa alle 
macro e` bene usarla, ma non sempre questa alternativa esiste; il primo 
esempio di macro "bene usate" che mi viene in mente e` in "Modern C++ 
Design" di Alexandrescu...
adesso chi glielo dice ad Andrei che il suo codice e` scritto in cattivo 
stile? ;-)
ciao Anto
-- 
Antonio Cuni, 20 anni, studente
Brainbench C++ Master: 4.73, higher than 99% of all previous test takers.
C Language: 3.79, higher than 92% of all previous test takers.
Guida C/C++ di Programmazione.it: 
http://cpp.programmazione.it

 lo stesso lavoro. 
www.gnu.org


 stile? ;-)
Gia' dobbiamo dirgli che non deve piu' scrivere funzioni con piu' return
:-)
Ciao,
Nicola

 
www.gnu.org
Non capisco questo link... GNU non mi sembra un singolo ambiente operativo
dove sono presenti piu' elaboratori con differenti architetture che svolgono
lo stesso lavoro... o forse non ho capito qualcosa...
Ti faccio un esempio piu' esplicito: prendiamo una dot com che ha nel suo
CED i sistemi di smistamento di posta elettronica. Credo che non sia fuori
dal mondo ritenere che, se la funzione di smistamento e' svolta da piu'
elaboratori (per ridondanza o per sopportazione del carico), essa debba
esser fatta da elaboratori identici (come architetture, S.O., versioni,
ecc.).
*** Azimuth ***

 stile? ;-)
Se mi dai l'e-mail glieli mando io due appuntini... ;-) (Scherzo, ma non
troppo... i "miti" non mi sono mai piaciuti)
Ciao!
*** Azimuth ***

Scusate se mi intrometto in questa discussione, ma ho la sensazione che
stiate facendo un "dialogo fra sordi".
Nel senso che, a mio parere, si scontrano due punti di vista: una teorica ed
accademica ed una pratica e lavorativa. E già sappiamo come si andrà a
finire!!!! Purtroppo .-(
Il punto vista accademico è sempre molto attento alla forma, alla teoria, a
chi a detto che cosa , dove e quando. Noi che lavoriamo, mi ci metto
anch'io, al di là della forma vogliamo che il "prodotto funzioni", e
funzioni bene, se poi il nostro modo di lavorare l'hanno detto molti
sconosciuti, o alcuni luminari, poco importa. Il prodotto funziona.
Lavorabdo, poi, sei costretto a trovare uno "standard di lavoro", che non è
sicuramente quello accademico, ma è il "tuo" standard, non sarà bello, ma
funziona e ti permette di riutilizzare le cose che hai già scritto senza
avere brutte sorprese alla fine.
Non si può reinventare tutto ogni volta, perché tizio ha detto, in qualche
convegno, che la tal cosa fatta in quel modo, è meglio. E questo, non lo
dico in senso dispregiativo, ma purtroppo non c'è il tempo per sperimentare
novità su cose già scritte da anni, o su metodologie di lavoro ormai
consolidate, anche se qualche accademico la considera deprecabile:
funzionano è questo è l'importante. Anche perché, molto spesso il codice su
cui lavori lo erediti da altri, o lo ricevi dai tuoi colleghi[1], che stanno
lavorando su un'altra parte del progetto, tempo che dovrei togliere ad altre
cose: e poi come le giustifico, al mio capo, due settimane in più sulla
realizzazione del lavoro? Sono comunque daccordo, che nel momento in cui si
scrivono delle cose nuove, si debba cercare di far confluire su queste le
migliorie consigliate da tizio, piuttosto che da caio, tenendo d'occhio,
sempre, i limiti temporali del progetto, ed il pregresso, che ti porti
sempre dietro.
Tutto questo, per dire in definitiva, che un conto è la teoria ed un conto è
la pratica.
Uau!!!!!!!. Penso che sono finalmente riuscito a teorizzare l'acqua calda
;-)
[1] tenete presente che non sempre il gruppo di lavoro è composto da
luminari, ma, solitamente, comprende un purpurì di figure, che vanno dal
laureato appena assunto, al programmatore senior con 20 anni di lavoro alle
spalle(........e fagli capire che per 20 anni ha scritto, per così dire, "da
cane" (detto in senso buono, scherzoso), che sui libri c'è scritto che....)
. Passando per i programmatori che arrivano dal fortran. o dal cobol, o da
VB, perché se no gli arriva la lettera di licenziamento a casa. A questi,
devi ancora aggiungere il capo, ed il cliente, che magari non ha mai usato
un computer.


 un computer.
Io faccio in modo che il cliente non entri nel merito. :)
Ciao!
*** Azimuth ***
[1] Considerando che sia veloce pure di debug. :)

 potendosi secondo lui stabilire regole assolute.
Scusa se mi ci ficco anchio: vivo e lavoro ogni giorno col compito di
fare manutenzione a codice schifoso e mi piacerebbe davvero tanto che
non fosse cosi', quindi sono d'accordo con te che sarebbe auspicabile
seguire delle linee guida davvero valide.
Detto questo vorrei farti notare queste cose:
1) Un unico return per funzione: e' sicuramente una buona pratica, ma se
complica il codice non e' utile.
Mi spiace ma la critica del signor Musatti e' stata costruttiva e
pertinente. Spero che non te la prenderai per questo.
2) Non usare le macro per definire delle costanti e' una buona pratica
e' vero. Ma se stai programmando su un dispositivo con limitazioni di
memoria sei costretto a farlo. Mi spiace c'e' ancora chi ha di questi
limiti (Io). La critica di Musatti e' stata costruttiva.
3) Stilisticamente definire le costanti con "solo" lettere maiuscole e'
veramente brutto proprio perche' tutto il resto del mondo e' abituato a
vederle come define. Anche qui il signor Musatti ha ragione.
5) E' giusto che i commenti non dicano quello che e' chiaro nel codice
(se il codice e' chiaro).
4) Tra il tuo stile di indentazione e quello di Musatti preferisco
quello di Musatti. Mi spiace.
Qual'e' e' il messaggio: che la verita' sta' nel mezzo (un'altra
banalita').
Tu fai bene a difendere un principio.
Il mondo e' pieno di cantinari che mandano in giro codice veramente
pessimo. Lo vivo tutti i giorni io sulla mia pelle.
Pero' secondo me ti sei un po' troppo accanito e quando qualcuno che ha
ragione si impunta in questa maniera alla fine passa dalla parte del
torto.
Saluti
Cesare

Cambia newsreader.
Č il tuo newsreader (e non č colpa di MS, lo fanno anche gli altri)
che manda a capo lě.
-- 
Marco Barisione (ICQ 84277092)

Cos'è una riga? Quanto è lunga?
È standard diffuso non superare i 78 caratteri.


E' il massimo supportato dal filesystem...
Tranne per il fatto che su NT possono essare anche più di MAX_PATH
caratteri :-(


 pertinente...
Basta, basta che se no poi mi gaso :-)
Ciao,
Nicola

 Non capisco questo link... 
dici che secondo te non sta ne` in cielo ne` in terra dover ricompilare 
un programma per farlo funzionare... eppure nel mondo del software 
libero la distribuzione dei sorgenti e` il modo principale di 
distribuire il software.


 Trascrivimi l'esempio (se ti va).
l'esempio e` un po' troppo lungo per essere scritto in un post... cmq se 
cerchi sul suo sito puoi scaricare la libreria Loki che implementa 
quanto proposto nel libro: cerca nei sorgenti relativi alle Typelist e 
vedrai cosa intendo.


 De programmarum stilae
Beh... De e il genitivo è la prima volta che lo vedo, confesso... :-)
Keko\