Newsgroup: it.comp.lang.c++
Titolo: E se...

Vi va di fare un gioco?
Supponete di essere Bjarne Stroustrup nel 1985, che sta scrivendo il 
C++. Sapendo quello che sapete ora sul C++ (punti forti, punti deboli, 
caratteristiche della "concorrenza" ecc.)... COSA cambiereste per creare 
un linguaggio migliore?

Massimo Soricetti ha scritto:

Molti degli utenti che lo usano male :)
Just joking.... :D

 ha scritto nel messaggio 
news:43bf9928$0$1079$4faf...@reader1.news.tin.it...

Ciao.
Bella domanda. Comunque spero non sia necessario tornare indietro di venti 
anni, con una specie di macchina del tempo, per includere e/o modificare 
caratteristiche e strumenti del C++. Intendo dire che il C++ è in potenza 
modificabile anche ora e nuove features potrebbero essere create in vista di 
un'ipotetica revisione dello standard, purché ovviamente non sia già 
presente un qualche impedimento strutturale che pregiudichi le variazioni e 
le novità da volersi includere.
Così in prima battuta penserei a due cosine prese dallo standard del 
linguagio Ada (standard 1995)
1) Gestione di threads e mutexes nativa e portabile su tutte le piattaforme 
dove un compilatore C++ è presente. Su questo c'é poco da dire.
2) Possibilità di non doversi necessariamente preoccupare di quanto un tipo 
è ampio nelle diverse piattaforme dove andrai a compilare il programma C++. 
Mi spiego meglio.
In Ada è possibile dire a compilatore che vuoi un tipo, ad esempio un 
integral type, per instanziare una variabile che possa contenere valori che 
tu prevedi poter oscillare ad esempio tra -100.000 e +100.000. Se 
intraprendi questa strada sarà cura del compilatore scegliere quale tipo 
nativo della macchina è più indicato a contenere tale variabile.
Purtroppo, come tutti sapete, in C++ sei tu programmatore a dovere scegliere 
se preferisci "short", "int" o "long". Ammettiamo che hai scelto "int" su 
una piattaforma dove questo tipo è conservato su 32 bits perché sai che i 
suddetti valori sono tutti registrabili senza problemi. Dopo un po' di tempo 
capita che il tuo programma viene compilato su hardware che utilizza 16 bits 
per gli "int" e qui nascono i guai... Nessun compilatore ti avverte che 
nella tua variabile non sarà più possibile gestire valori troppo bassi o 
troppo alti e la tua applicazione funziona perfettamente per anni fino a che 
una qualche computazione non inserisce ad esempio 65.536 che prontamente 
sarà interpretato come -1... E' necessario andare oltre?
Incidentalmente in Ada esistono comunque i tipi nativi, con dimensioni 
prefissate e dipendenti dall'architettura come in C++, anzi ce ne è anche 
qualcuno in più che non guasterebbe neanche in C++.
a) I "modular types", che contengono valori da zero a n-1 (n scelto dal 
programmatore) e su questi ogni operazione aritmetica produce un risultato 
uguale al risultato stesso modulo n. Esempio:
type Hour_Type is mod 24; -- Creazione del tipo modular 24 di nome 
arbitrario Hour_Type.
Late_Hour : Hour_Type := 23; -- Instanza di una variabile chiamata Late_Hour 
di tipo Hour_Type.
Late_Hour := Late_Hour + 1; -- Late_Hour, variabile del tipo Hour_Type ora 
contiene 0.
b) I "floating points" con precisione e range definibile dal programmatore. 
Esempio con precisione 15 (cifre significative) e range tra -70 e + 50:
type Temp_Type is digits 15 range -70.0..50.0;
c) I "fixed points" con steps prefissati e range. Esempi:
type Balance_Type is delta 0.01 digits 8;
Implica un range di -0.01*(10**8-1) fino a 0.01*(10**8-1), cioè 
da -999,999.99 fino a 999,999.99.
type New_Balance_Type is delta 0.01 digits 8 range 0.0 .. 999,999.99;
Come sopra ma solo valori positivi.
type A_Third_Balance_Type is delta 1_000_000 digits 4;
Implica un range di -1,000,000*(10**4-1) fino a 1,000,000*(10**4-1), cioè 
da -9,999,000,000 fino a 9,999,000,000 a passi da 1 milione.
d) Arrays indicizzati con qualsiasi, o quasi, cosa rappresentabile come 
integral:
type Day_type is (dom, lun, mar, mer, gio, ven, sab);
type Day_Temp_Type is array ( 1 .. 52, Day_Type) of Float range 0.0 .. 
100.0;
Array a due dimensioni contenente floats nel range indicato da indicizzarsi 
tramite numero della settimana (da 1 a 52, se non sbaglio) e nome del 
giorno. La indicizzazione non deve necessariamente partire da 0.
Per finire, visto che spesso il C++ è usato per applicazioni molto low-level 
non sarebbe male, sempre da e come in Ada, potere definire direttamente il 
numero di bits che contiene un tipo e magari anche l'allineamento in 
memoria. Esempio, tratto da "Ada as a Second Language", di Norman H. Cohen:
"Il DOS gestisce una "file_allocation_table" su ogni disco. Nelle prime 
versioni del S.O. questo era un array di 316 "cluster numbers", ciascuno dei 
quali era un intero senza segno di 12 bit. per risparmiare memoria sul 
disco, i 12 bits di un elemento dell'array erano seguiti immediatamente dai 
12 bits del successivo. In sintesi ogni tre bytes da 8 bits contenevano due 
elementi dell'array.
Per dichiarare un tipo capace di rappresentare tale tabella in memoria, il 
primo passo è di dichiarare il tipo degli elementi della tabella e 
specificare che ciascuno deve essere lungo 12 bits:
type Cluster_Number_Type is mod 2**12;
for Cluster_Number_Type'Size use 12;
Il passo successivo consiste nel dichiarare il tipo della tabella stessa e 
specificare che la dimensione di ciascuno dei suoi componenti non deve 
eccedere la dimensione del tipo che conterranno:
type File_Allocation_Table_Type is
    array (Cluster_Number_Type range 0 .. 315) of Cluster_Number_Type;
for File_Allocation_Table_Type'Component_Size use 12;
Il passo finale consiste nell'indicare che non ci deve essere nessuno spazio 
tra ciascuno dei componenti da 12 bits, anche se ciò causa che alcuni bytes 
da 8 bits contengono parti di due elementi da 12 bits differenti:
pragma Pack (File_Allocation_Table_Type);".
Non procediamo oltre nella spiegazione delle precedenti istruzioni che 
forse, per essere ben comprese, necessitano di ulteriori chiarimenti. Il mio 
unico intento era di mostrarle solo al fine di fare capire quanto ci si 
possa spingere low-level con Ada, conntrariamente a quanto qualcuno avrebbe 
potuto pensare a causa delle dichiarazioni di tipi che ho presentato più 
sopra. Dichiarazioni che non menzionando in alcun modo direttamente la 
dimensione in bits e bytes degli stessi potevano fare pensare ad un 
linguaggio non adatto ad applicazioni che necessitano di manipolare "raw 
memory".
Saluti.
fabio de francesco

nativo della macchina è più indicato a contenere tale variabile.
Questo accade anche in Pascal.
Si può, ad esempio, scrivere:
type 
  UnsignedByteRange = 0..255;
  SignedByteRange = -128..127;
  Bit = 0..1;
  BitIndex = 0..7;
  Voto = 18..30;
ecc...

 un linguaggio migliore?
Forzare in qualche modo la reale portabilita' tra le varie piattaforme.
Ciao!
Se vuoi un anno di prosperità, fai crescere il grano.
Se vuoi cent'anni di prosperità, fai crescere le persone.


Io avrei inserito nello standard gia' allora le seguenti caratteristiche:
* Le eccezioni.
* I template di classe.
* La classe "string".
Erano concetti ben noti e potevano essere inseriti subito o pochi anni 
dopo. Purtroppo la loro assenza ha fatto penare molti nonche' ha fatto 
adottare molte implementazioni non standard per tali costrutti.
Inoltre, forse non nel 1985, ma comunque prima del 1998:
* Avrei chiamato "array" quello che adesso si chiama "vector".
* Avrei chiamato "vector" quello che adesso si chiama "valarray".
* Invece di tutto cio' che e' contenuto nell'header <functional>, avrei 
definito una funzionalita' lambda, cioe' la possibilita' di definire 
funzioni senza nome nel punto in cui si deve inserire un oggetto chiamabile.
* Avrei definito un modo, per cui adesso si propone la parola chiave 
"auto", per definire automaticamente il tipo di una variabile o di una 
costante in base all'inizializzatore.
-- 
     Carlo Milanesi
 
http://digilander.libero.it/carlmila

 ha scritto nel messaggio 
news:dhgvr1tn83nenurnt...@4ax.com...

Già...
In ogni caso avrai potuto notare che le specifiche per la costruzione dei 
tipi includono molto più della semplice (?!) definizione di ranges. Inoltre 
la mia enumerazione dei tipi Ada non è assolutamente esaustiva ed avrebbe 
dovuto includere anche i tipi "task", "tagged", "record", "protected" e 
"privates". Alcuni di questi hanno degli equivalenti in C++, seppur con 
funzionalità e semantica limitate.
Spero che dalla mia "enumerazione di tipi" non ne sia venuto fuori solo il 
vantaggio di potere imporre dei ranges (tra l'altro controlabili sia a 
compile-time che a run-time, se si preferisce). Se così fosse ho fallito 
miseramente...
Tra l'altro Ada ha preso alcune interessanti features dal Pascal ampliandole 
notevolmente, prime tra tutte l'ordine e la leggibilità portate a livelli 
mai visti prima. Leggere un sorgente in Ada è quasi come leggere un racconto 
in Inglese, magari a scapito di qualche carattere in più da scrivere a 
tastiera ma a tutto beneficio della documentazione, futura manutenzione ed 
estensione dello stesso sorgente.
Saluti e buon divertimento con C++.
fabio de francesco

 ha scritto nel messaggio 
news:43bf9928$0$1079$4faf...@reader1.news.tin.it...

Mentre scrivevo (altro post stesso thread) sul fatto che mi piacerebbe avere 
in C++ alcune cosine (Gulp!!! Ho scritto "cosine"??? ) come threads e 
mutexes oltre che una quantità maggiore e una gestione un po' più completa e 
potente dei tipi built-in mi sono venute in mente un altro paio di cose... 
Purtroppo sempre da Ada di cui sembra io voglia fare l'apologia... E se così 
pensate avete anche ragione... :)
Esiste nel linguaggio (Ada 1995 standard) un potente insieme di funzionalità 
che si chiamano "Distributed Systems". I compilatori Ada non sono obbligati 
a fornire tali funzionalità su tutte le architetture ma il mio fedele 
GNU/GCC le implementa in Linux ed altre ancora e per me è più che 
abbastanza...
Immaginate di potere scrivere un intera applicazione senza dovervi 
preoccupare in anticipo su quanti e quali processori l'esecuzione di parti 
della stessa saranno distribuite.
Le decisioni su come un programma distribuito sarà suddiviso in parti 
cooperanti e su dove le suddette parti saranno poste in esecuzione è 
espresso da meccanismi al di fuori del codice (text) dello stesso. Un 
programmatore potrà così occuparsi solo della logica dell'applicazione, sarà 
così possibile sviluppare anche un programma "single source" i cui 
componenti potranno essere distribuiti per eseguire in una varietà di modi 
differenti per eseguire su una varietà di architetture uguali e/o 
differenti.
Ovviamente è necessario più che il solo compilatore che supporti i 
cosiddetti "Distributed Systems". Serve anche un meccanismo di comunicazione 
per lo scambio di messaggi tra i "subprograms" che magari sarà fornito dal 
sistema operativo ospite tramite una qualche libreria.
Il "Distributed System" di Ada definisce un insieme di interfacce standard 
per il sottosistema di comunicazioni e richiede che il compilatore generi le 
appropriate chiamate alle suddette interfacce.
I meccanismi dietro tali features sono alquanto complessi ed è impossibile 
esaminarli qui con cura. In sintesi il sitema sotto il quale l'applicazione 
esegue è visto come un insieme di "nodi processore" e "nodi di memoria". I 
"nodi processore" possono essere macchine distanti geograficamente e 
comunicanti tramite un network, oppure un set di macchine nella stessa 
stanza o anche un set di processori sulla stessa schda madre, o ancora un 
set di sistemi operativi che girano sullo stesso mainframe. I differenti 
"nodi di memoria" possono essere vari file systems e/o memoria principale 
(RAM). I "nodi processore" possono comunicare tra di loro sia tramite 
scambio di messaggi (come sopra spiegato) ma anche attraverso la 
condivisione di "nodi di memoria" (qualcosa tipo "shared memory").
In sintesi non bastano le parole. Bisogna averci lavorato anche solo un po' 
per capire come una roba del genere farebbe bene ad essere inserita in una 
futura versione dello standard del C++.
Buon divertimento con il C++ (e magari un giorno anche con Ada).
Saluti.
fabio de francesco

 Tra l'altro Ada ...
Ti sei dimenticato di dire:
"Il nome del linguaggio avrebbe dovuto essere Ada". :)


 "Il nome del linguaggio avrebbe dovuto essere Ada". :)
Come ho fatto a non pensarci prima? Si può ancora fare? :)


tipi includono molto più della semplice (?!) definizione di ranges.
Io mi riferivo a questa caratteristica particolare, ovvero la
possibilità, anche da parte del Pascal, di costruire dei tipi come
range di valori.  Non vuol dire che del tuo messaggio ho letto solo
questo.

 Vi va di fare un gioco?
Come no, siamo qui per perder tempo :-)

Secondo me il nodo principale e' la compatibilita' col C. Eliminando
questo vincolo si sarebbe potuto avere una sintassi molto piu' semplice
e si sarebbero evitati diversi punti delicati del C++. Peccato che il
linguaggio non avrebbe avuto alcuna speranza di arrivare dov'e'...
In particolare avrei preferito una sintassi delle dichiarazioni piu'
simile a quella del Pascal, e soprattutto avrei fatto di tutto per
evitare parti con notazione infissa.
In presenza dei template avrei anche ridotto drasticamente i casi di
conversione automatica, specialmente dove il valore originale puo' non
essere preservato, oppure dove il risultato dipende dalla piattaforma.
Avrei anche sovvertito uno dei principi fondamentali del C, che per
restare piu' "vicino al metallo" non fornisce alcun tipo di controllo
di sicurezza; invece avrei posto come default tutti i controlli di
range, validita' dei puntatori, ecc. possibili, salvo consentire
esplicitamente la loro rimozione per ottimizzazione da parte del
compilatore dove e' possibile dimostrare che sono duplicati e fornendo
comunque un mezzo esplicito, tipo pragma, per la loro rimozione, da
usare per porzioni di codice critiche dal punto di vista prestazionale.
Ciao,
Nicola

 memoria. 
E cosa manca ai bitfield?
Ciao,
Nicola

 Come ho fatto a non pensarci prima? Si può ancora fare? :)
Ada probabilmente no, ma "Augusta" quasi certamente si ;-)
Ciao,
Nicola

 Forzare in qualche modo la reale portabilita' tra le varie piattaforme.
E anche la fine di tutte le guerre. Forse sarebbe meglio se provassi a
specificare meglio cosa intendi.
Ciao,
Nicola

 Nicola
Saluti.
fabio de francesco

 Nicola
pragma Suppress( All_Checks );
Indovina chi lo fornisce?
fabio de francesco

 Indovina chi lo fornisce?
La sintassi non me la ricordo, ma io ho visto per la prima volta
qualcosa del genere nell'IBM Pascal per PC (intorno all'84).
Ciao,
Nicola

>
PS.: Ti ho scritto in privato. L'indirizzo che esponi al NG è operativo?

 effettivamente oggi manca.
Anche se questa classe in quanto classe prenderebbe in molti casi una
memoria superiore a quella necessaria per i numeri modulo n. Ora nella
maggior parte dei casi non sono gli 8 byte di una classe scarna a fare
la differenza (per lo meno questo prende sulla mia piattaforma) rispetto
al potenzialmente 1 byte di un modulo n (non n sufficientemente
piccolo), tuttavia potrebbe tornare comodo.
-- 
L'idiozia trascende il mezzo con cui viene espressa.

 piccolo), tuttavia potrebbe tornare comodo.
Mi sa che stai confondendo la classe con i suoi oggetti.
Lo spazio occupato dalla classe e' costituito dal codice macchina 
generato dal compilatore, mentre lo spazio occupato dai suoi oggetti sta 
nei segmenti di dati, e dipende da quanti oggetti di tale classe vengono 
istanziati. Supponendo che ogni oggetto occupi lo stesso spazio, lo 
spazio complessivo e' pari allo spazio di un oggetto moltiplicato per il 
numero di oggetti istanziati, oltre all'eventuale overhead per la 
gestione della memoria dinamica.
Su piattaforma Windows+Pentium, sia con Visual C++ che con gcc, il 
seguente programma stampa 1, cioe' l'oggetto occupa un byte.
#include <iostream>
using namespace std;
struct modulo { unsigned char c; };
int main() {
   cout << sizeof (modulo) << endl;



Non c'e' motivo perche' sia cosi', anche se e' molto probabile che una
simile classe non scenda sotto la dimensione di int.
Ciao,
Nicola

 seguente programma stampa 1, cioe' l'oggetto occupa un byte.
Tempo fa ebbi un problema "relativamente" simile. Stavo scrivendo un
risolutore di sudoku (al momento in stallo) in grado di trattare sudoku
di dimensione arbitraria (con n=m^2). In questo contesto mi avrebbe
fatto comodo avere oggetti numero "piccoli" (giusto per vedere quanto mi
sarebbe costato). Per cui mi feci sta faccenda. E in effetti un pggetto
di un'implementazione minimale di numero-sudoku (senza ancora il
meccanismo per segnare i numeri escluusi) prendeva 8 byte.
Anzi, mi sono reso conto, di più. Questo frammento di classe
class small{
    bool arr_[4];
    public:
        small(unsigned int n){
        }
};
prende 16 byte. Piattaforma PPC MacOS X gcc 4.
Comunque ho idea che il problema sia che non ottimizza i bool. In
effetti se invece di un array di 4 bool ci schiaffo un int, di byte ne
prende solo 4.
Dovrei provare con il flag sull'allineamento del gcc, ma andremmo
parecchio OT.


 
mi sembra che std::vector<bool> preveda questa ottimizzazione
-- 
"E' solo dopo aver perso tutto che siamo liberi di fare qualsiasi cosa."

 linguaggio non avrebbe avuto alcuna speranza di arrivare dov'e'...
In effetti è una cosa che, guardandoti alle spalle, ti chiedi "ma perchè 
cavolo l'han fatto compatibile col C???" Io vengo dal C infatti, e 
credendo di avere a che fare con un linguaggio che tutto sommato era 
ancora "una specie di C" mi sono messo a progammare in C++. Salvo 
scoprire, dopo e a forza di casini, che tutto quello che facevo in C lo 
si fa diversamente in C++. La sintassi "spicciola" è la stessa, ma 
quella è la parte di un linguaggio che si impara in un'oretta. Avesse 
creato un linguaggio da zero ci sarebbero stati TANTI equivoci di 
meno... almeno per me :-)

 simile classe non scenda sotto la dimensione di int.
Da un minimo test fatto ho idea che sia questione di allineamento. E che
di default mi allinea i bool, e in effetti un array di bool occupa tanto
spazio quanto uno di interi.
Cioč facendo un disegnino se mi sono mal spiegato
bool arr[4];
[X|o|o|o][X|o|o|o][X|o|o|o][X|o|o|o]
int arr[4];
[X|X|X|X][X|X|X|X][X|X|X|X][X|X|X|X]
Ed in effetti la cosa si vede... se facessi un 
(unsigned int)*(&bool_arr)
dopo avere messo bit per bit un unsigned int nell'array di bool
(ovviamente con la endianess corretta) IMHO dovrebbe ridarmi l'intero di
partenza. E invece non funge.


 simile classe non scenda sotto la dimensione di int.
Da un minimo test fatto ho idea che sia questione di allineamento. E che
di default mi allinea i bool, e in effetti un array di bool occupa tanto
spazio quanto uno di interi.
-- 


 partenza. E invece non funge.
Non molto efficiente, in termini di spazio. Mi vien da pensare che il
processore sottostante sia fortemente inefficiente nello spacchettare
array di caratteri piu' "impacchettati" e comunque mi aspetterei che
fosse possibile, via pragma o opzioni, dire al compilatore di usare una
rappresentazione piu' compatta.
Ciao,
Nicola

 rappresentazione piu' compatta.
A titolo di controprova sotto Windows 2000 il seguente programmino
#include <iostream>
int main()
{
    std::cout << sizeof(bool[8]) << '\n';
}
stampa 8 con Comeau, g++ 3.4.2 e bcc32 5.6.4 .
Ciao,
Nicola

 rappresentazione piu' compatta.
Infatti lo č. Solo non mi piace aggiungere troppi flag, e comunque non
ho nessuna garanzia che su un altra piattaforma le cose siano
diverse/peggiori etc etc


 prende solo 4.
ULTRA OT :)))
IO lo ho già fatto ... non è difficile... il casini è generare giochi di 
sudoku che portino ad una soluzione unica....
