Newsgroup: it.comp.lang.c++
Titolo: un paio di illuminazioni

>>>>>>>>>>>>

-come si "scrive" un numero binario?
void write_binary( int n )
{
     if ( n > 0 )
     {
        write_binary( n >> 1 ) ;
        putchar( "01"[n & 1] ) ;
     }
     else putchar( '0' ) ;
}
--
Andrea Laforgia (ICQ 87230455)
http://www.geocities.com/AndreaLaforgia
"ci sono tre tipi di persone nel mondo : quelli che
 sono bravi in matematica e quelli che non lo sono."

 si puo' comunicare un numero tipo: 1000100111101011???
#include <bitset>
#include <iostream>
#include <limits>
using namespace std;
int main() {
  int i = 34;
  cout << "Decimale: " << i << "; binario: "
       << bitset< std::numeric_limits<int>::digits >(i) << endl;
  return 0;
}
-- 
Maurizio Loreti                         
http://www.pd.infn.it/~loreti/mlo.html
Univ. of Padova, Dept. of Physics - Padova, Italy            
lor...@pd.infn.it
The instructions said to use Windows 98 or better - so I installed FreeBSD.

 void write_binary( int n )
Credo che intendesse chiedere "qual'e' la sintassi di un "literal" binario. E la
risposta e' "non c'e'". I literal interi in C++ sono decimali, ottali o
esadecimali.
Andrea Ferro
---------
Brainbench C++ Master. Scored higher than 97% of previous takers
Scores: Overall 4.46, Conceptual 5.0, Problem-Solving 5.0
More info 
http://www.brainbench.com/transcript.jsp?pid=2522556

esadecimali.
Ho letto giusto ieri un vecchio numero di CUJ in cui Pescio
presentava un template per la gestione dei literal binari.
Ridotto all'osso (in particolare senza nessun controllo
sugli errori) era qualcosa come...
   #include <iostream>
   template<int n> struct Bin
   {
     enum { value=(n%10)+2*Bin<n/10>::value };
   };
   template<> struct Bin<0>
   {
     enum { value=0 };
   };
   #define BIN(x) Bin< x >::value
   int main()
   {
     std::cout << BIN(10101111) << std::endl;
     // La conversione e' eseguita compile-time!
     return 0;
   }
Personalmente ritengo questo tipo di cose non molto
gradevoli (sono secondo me forzature dello strumento
dei template... forse un domani la cosa evolvera' in
questo senso permettendo una metaprogrammazione
agevole ma per ora mi sembra che ci si appoggi un
po' troppo a dei "trucchetti" che non ritengo fossero
voluti quando si sono pensati i template).
Comunque e' innegabile che qualche giochino
compile-time abbastanza sorprendente lo permettono.
Andrea
--
Andrea Griffini, Programmer
http://www.netservers.com/~agriff

Condivido il tuo disagio. Se pero' leggi un recente post di B.S. su
c.l.c++.m. noterai che forse non sono tutte cose non previste in
origine. E a giudicare dalle librerie Boost e da libri come "Generative
Programming" o gli articoli che A. Alexandrescu scrive su CUJ e' l'area
di ricerca piu' attiva in questo momento.
Ciao,
Nicola

di ricerca piu' attiva in questo momento.
Boh. Forse un giorno anche questa roba mi sembrera' molto molto
interessante. Per ora mi ho l'impressione che il C++ abbia
problemi piu' concreti... ad esempio io non ho capito quali
sono le regole da applicare quando i due rami dell'operatore
ternario ?: non sono dello stesso identico tipo... con il
seguente programma
  struct A {};
  struct B { operator A() { return A(); } }
  int main()
  {
    A a;
    B b;
    1 ? a : b;
    return 0;
  }
gcc compila e BCC55 no. Io ho guardato il draft e non sono
riuscito a capire chi ha ragione :(( ... soprattutto quando
penso che espressioni come "return p ? p->ptr : 0;"
dovrebbero essere sensate.
Si tratta di qualcosa di assolutamente concreto in cui i
template non centrano nulla e in cui mi sono imbattuto
scrivendo codice reale e senza cercare di fare la gestione
fatture compile-time.
Per generare codice io preferisco dei generatori esterni
scritti in PERL. Li trovo piu' flessibili (si riescono anche
ad aprire i file ;) ) e il codice del generatore e' meglio
manutenibile perche' piu' leggibile.


 dovrebbero essere sensate.
Quali sono le regole da applicare e' presto detto: quelle della clausola 5.16
[expr.cond]. Che poi tale clausola non sia certo una delle piu' chiare dello
standard e' poco ma sicuro!
Per come l'ho capita io il caso "1 ? a : b;" di cui sopra deve compilare e
ritornare a come r value. In caso di falsita' (cioe' 0 al posto di 1) ritorna un
r-value, sempre di tipo A, che e' quello ottenuto chiamando "b.operator A()".
In pratica dato che il due tipi di parenza sono l-value di classi,
rispettivamente, A e B e dato che non e' possibile convertire un l-value di
classe A in un l-value di classe B ne viceversa, il risultato e' un r-value. Ora
non e' possibile convertire a in un r-value di tipo B ma e' possibile convertire
b in un r-value di tipo A (con l'apposito operatore). Pertanto esiste una sola
possibile srada per ridurre i due operandi ad un unico tipo, l'espressione e'
valida e definita e ha come valore quel tipo: ovvero un r-value di classe A.


valida e definita e ha come valore quel tipo: ovvero un r-value di classe A.
quindi tu dici che il compilatore deve provare tutte le possibili
doppie combinazioni di conversione ? Se le due espressioni possono
entrambe essere convertite in un tipo comune la cosa e' ammessa ?
   struct A { };
   struct B : A { };
   struct C : A { };
   int main()
   {
     B b;
     C c;
     1 ? B : C;
     return 0;
   }
gcc con questo codice genera un messaggio che non capisco...
tri.cpp: In function `int main()':
tri.cpp:9: no match for `int ? B & : C &'
tri.cpp:9: candidates are: operator ?:(bool, C &, C &) <builtin>
tri.cpp:9:                 operator ?:(bool, B &, B &) <builtin>
tri.cpp:9:                 operator ?:(bool, C, C) <builtin>
tri.cpp:9:                 operator ?:(bool, B, B) <builtin>
mentre compila senza problemi se uso B *b e C *c (ma non compila
se invece dei pointer uso dei (const) reference).
Supponiamo un altro caso...
    struct A { };
    struct B { };
    struct C { C(const A&){} C(const B&){} };
    int main()
    {
      A a;
      B b;
      1 ? a : b;
      return 0;
    }
In questo esempio la conversione ad un tipo comune e' possibile
per via del costruttore non explicit di C: entrambe le espressioni
possono cioe' essere convertite implicitamente in C.
Questo e' codice legale C++ ? (g++ e BCC55 non lo compilano)
g++ compila se uno dei due e' un C (l'altro viene convertito)...
e' giusto ? Questo e' il comportamento ufficiale ?


 e' giusto ? Questo e' il comportamento ufficiale ?
Si certo. Se uno dei due e' un C (o meglio di tipo C&) e l'altro puo' essere
convertito (in C& o in C), la cosa e' ok.
Nota: spesso c'e' confusione (se non altro perche' e' una regola che non ho
visto scritta da nessuna parte) sul modo in cui i compilatori "rappresentano" i
tipi nei messaggi di errore. Da quello che posso notare pare che molti usino un
meccanismo diverso a seconda se vogliono indicare il tipo di un argomento di
funzione o se vogliono indicare il tipo di una espressione.
Il tipo di un argomento (o di una variabile, in se) e' quello dato dalla sua
dichiarazione (salvo riordino dei vari qualificatori). Viceversa nel tipo di una
espressione e' importante indicare in qualche modo se parliamo di un r-value o
di un l-value. Se si segue questo principio abbiamo quello che ho scritto sopra,
ovvero che un l-value e' rappresentato come reference e un l-value come tipo
senza reference. Quindi dati:
C o;
C&r;
Secondo il suddeto schema il tipo dell'oggetto "o" e' "C" mentre il tipo
dell'espressione "o" e' "C&". Nel caso di r sia il tipo dell'oggetto che quello
dell'espressione e' "C&".
Invece avendo:
const C o;
const C&r;
Secondo il suddeto schema il tipo dell'oggetto "o" e' "const C" mentre il tipo
dell'espressione "o" e' "C". Mentre il tipo dell'oggetto r e' "const C&" e il
tipo dell'espressione "r" e' "C".
Ripeto: non so se sia una regola, ma se non altro spiega il messaggio di errore
del tuo compilatore. Ovvero il compilatore ha tentato i quatto tipi di
conversione da me elencati (te li mostra come se fossero chiamate a funzione con
i parametri aventi lo stesso tipo delle espressioni ammesse). Ovviamente non ti
dice che solo uno degli argomenti puo' essere convertito. Ma la cosa e'
implicita nel fatto che i tipi usati non prevedono la conversione (salvo da
l-value a r-value) di uno dei due!
Non so se mi sono spiegato.


Non so se mi sono spiegato.
O tu non ti sei spiegato o io non ho capito.
O entrambi.
In ogni caso, per farla breve, dove sta scritto che non e'
permessa la conversione implicita di *entrambi* i rami di ?:
quando il tipo di questi e' una classe ?
Ad essere onesti leggendo il draft ho avuto la sensazione che
il discorso di vietare la doppia conversione vale solo
per i puntatori. Su questa cosa mi sembra che la definizione
del "composite-type" sia chiara: e' il tipo di *uno* dei due
se l'altro puo' essere convertito in questo.
Mah... sempre peggio
Per ora ho problemi *terribilmente* piu' concreti ... (domani
rilascio la prima versione ufficiale di un nuovo programma :DDD)
Andrea


 (mentre in C solo gli l-value possono essere const).
Lo standard si. Ma non usa una sintassi ridotta quando descrive i tipi delle
espressioni. Dice esplicitamente "e' un l-value" o "e' un r-value". Io mi
rifacevo al fatto che spesso i compilatori sembrano "cercare di intuire" dei
tipo. E nei messaggi di errore intuendoli come l-value li rappresentano con T&
altrimenti li rappresentano con T.


 manutenibile perche' piu' leggibile.
Io mi sono trovato bene con un approccio misto: generare con un
programma esterno specializzazioni esplicite di template, che sono poi
utilizzate da template veri e propri.
Ciao,
Nicola

 c vale 1 .
Non credo che l'operator? centri molto. Anche se scrivi c=a ottieni 1.
L'operator? dovrebbe ritornare un r-value di tipo Money convertendo lo 0 tramite
Money(int).
Il motivo per cui ottieni uno sta in:
   operator double() { return val / 10000; }
Val e' un long e 10000 e' un int. La divisione "promuove" 10000 a long e da per
risultato un 1L (1 come long). Che poi viene convertito in double. Prova a
scrivere:
   operator double() { return val / 10000.; }
in modo che "10000." (con il punto) sia una costante double.


    operator double() { return val / 10000.; }
In effetti ho sbagliato a riportare l'esempio. Nell'originale l'operator
double e' scritto in modo corretto, ma il troncamento avviene lo stesso.
Ciao,
Nicola

c vale 1 .
La cosa in effetti potrebbe essere ambigua... io vedo
tre possibilita':
     zero  => Money   (sei salvo)
     Money => intero  (ti frega)
     Money => double  (sei salvo)
Quale e' che il compilatore dovrebbe scegliere ?
Nota che il fatto che tu assegni il risultato
dell'espressione ad un tipo Money non puo' essere
chiamato in causa per richiedere che la conversione
sia a Money e non verso un tipo nativo.
In C++ il tipo di una espressione non dipende dal
contesto (o almeno non dovrebbe dipendere... ma
le zone buie del C++ potrebbero riservare sorprese).
Questo e' il motivo per cui ad esempio non e'
ammesso overloading di funzioni in cui a cambiare
sia solo il valore di ritorno.
Io credo che sarebbe bello aspettarsi un warning
o un errore di risoluzione ambigua in questo caso.



Continuo a pensare che l'operator? c'entri poco. Come hai definito l'operator= ?
Ci sono altri costruttori (per esempio copy constructor)? Come si comporta se
esegui l'assegnamento direttamente (senza operator?)?


 o un errore di risoluzione ambigua in questo caso.
Mi sa che per codice ill-formed sia richiesto un errore.


 c vale 1 .
ok, seguite un attimo il mio ragionamento, perchè a me guardando il
tuo codice sembra ovvio che il risultato sia 1, e a questo punto penso
che il mio ragionamento sia sbagliato, se voi vi stupite che il
risultato sia questo.
Money a = 1.2
viene chiamato il ctor Money( double) e val assume il valore di 10'000
alla fine della zuppa! ok?
Money b = a > 0 ? a : 0;
cioè
Money b = ( a > 0 ? a : 0);
a viene convertito a int, quindi il confronto diventa
Money b = ( 1 > 0 ? a : 0);
ovvero
Money b = a;
c'è un copy ctor definito? no, quindi avviene la copia saggia dei
membri, quindi b.val è uguale a 10'000. Infine
double c = b;
viene quindi chiamato operator double()
che mi divide 10'000 per 10'000 e restituisce 1.
Sbaglio qualcosa?

 alla fine della zuppa! ok?
Mica tanto, mi sa che assumerà 12000...
Massimo

 esegui l'assegnamento direttamente (senza operator?)?
La classe che ho mostrato come esempio e' scritta sulla falsariga della
classe Currency delle librerie Borland e si comporta in modo "sensato"
in tutti gli altri casi. Il problema e' proprio nell'operatore :? che
per far combaciare il tipo del secondo e del terzo argomento li converte
entrambe a int.
Ciao,
Nicola

 Mi sa che per codice ill-formed sia richiesto un errore.
In realta' lo standard richiede solo che venga emesso un diagnostico:
non impone mai che la compilazione venga interrotta.
Ciao,
Nicola

 a viene convertito a int, quindi il confronto diventa
Il punto e' che non si vede perche' a debba essere convertito in int
piuttosto che 0 in Money (non dico che questa seconda conversione sia
preferibile, solo che paiono equivalenti).
Ciao,
Nicola

 wrote in message
news:3A9A1576...@divalsim.it...

Si. Me ne sono reso conto. Da come interpreto io lo standard pero' quel codice
e' ill-formed e non dovrebbe compilare (vedi il mio precedente post). Quindi
piu' che l'operator? (in senso standard) la colpa e' dell'implementazione (non
conforme) del suddetto operator nel tuo compilatore. IMO


 non impone mai che la compilazione venga interrotta.
Con errore intendevo un diagnostico e la mancata generazione del codice oggetto,
non un'interruzione della fase di compilazione. Intendo invece con warning un
diagnostico di avviso che non inficia la compilazione in quanto il comportamento
e' comunque definito (dallo standard o dall'implementazione). Non sono certo di
cosa lo standard chieda per codice ill-formed. Ovvero se l'implementazione e'
libera o meno di trattare ill-formed come implementation-defined e quindi
cavarsela con un warning.


preferibile, solo che paiono equivalenti).
Infatti ad esclusione di Money=>double (che richiederebbe
una conversione sia del secondo che del terzo operando di
?: ) le altre due conversioni sembrano sullo stesso piano.
Il compilatore dovrebbe segnalare l'amiguita' (come e'
richiesto anche in altri casi ambigui).
Se non sbaglio con la vecchia descrizione (del draft)
invece anche Money=>double sarebbe stata fra le papabili
ma avrebbe comunque perso rispetto a Money=>int nella
ricerca del best match (mentre le due Money=>int e
int=>Money sarebbero comunque state sullo stesso piano).


Io ho comunque imparato che il secondo e terzo parametro di ?: devono
avere tipo identico :-)
Mi domando come sarebbe programmare in un linguaggio dove tutti gli
statement sono anche espressioni, come non mi ricordo piu' quale Algol.
In pseudo C++ si avrebbe
int a = if ( b > 0 ) c else d;
Secondo me non sarebbe male. Se poi fosse imposto che tutti i flussi di
esecuzione attraverso un insieme di istruzioni dessero valori dello
stesso tipo si sarebbe costretti ad una logica di programmazione
particolarmente pulita.
Ciao,
Nicola

particolarmente pulita.
Ma non proprio 'naturale'...
Luc.

 Ma non proprio 'naturale'...
Solo perche' non ci si e' abituati. Se ci pensi, questo tipo di
approccio risolverebbe la maggior parte delle situazioni in cui la
programmazione strutturata porta a codice ingarbugliato, evitando
scappatoie come break e continue (per non parlare di goto).
Ciao,
Nicola

Solo perche' non ci si e' abituati.
int a = c when (b>0) otherwise d;
... 8)

 int a = c when (b>0) otherwise d;
Ma il punto non e' sostituire l'operatore ?: , quanto introdurre una
tecnica di programmazione differente, dove *qualsiasi* istruzione e'
anche un'espressione.
In fondo l'operatore :? e' un sostituto dell'if cosi' come l'operatore ,
lo e' per la sequenza di espressioni: il C++ e' gia' a meta' strada
dunque...
Ciao,
Nicola

 facile sbattere il muso inavvertitamente.
Gia'.
Ciao,
Nicola

 terzo operando di operator? siano dello stesso tipo!!!
ah ecco, adesso capisco!
cmq ancora non riesco a realizzare il motivo per cui alla fine c
dovrebbe risultare in un numero diverso da 1! a è maggiore di 0? sì,
quindi il ramo buono è quello a sinistra, quindi alla fine c vale 1.
Dov'è l'intrigo?

 Dov'è l'intrigo?
Riprendo il mio esempio per comodita':
class Money { 
public:
  Money(int v) { val = v * 10000; }
  Money(double v) { val = v * 10000; }
  operator int() { return val / 10000; }
  operator double() { return val / 10000.0; }
  ...
private:
  long val;
};
Money a = 1.2;
Money b = a > 0 ? a : 0;
double c = b;
Se si applica la conversione di a a intero tramite operator int, c vale
1 . Se invece si applica la conversione di 0 a Money tramite il
costruttore opportuno, c vale 1.2, che e' quello che a me
originariamente serviva.
Ciao,
Nicola

 wrote in message
news:3A9E1557...@divalsim.it...

Esatto. E standard alla mano IMHO la conversione e' ambigua e quindi il codice
e' ill-formed. Quindi nel caso specifico consiglio, sia per "disambinguare" che
per portabilita' (ammesso che esista gia' C++ "portabile") di riscrivere cosi':
Money b = a > 0 ? a : Money(0);
