Citazione da: cdimauro - 27 Aprile 2014, 14:56:27Purtroppo quel libro è disponibile soltanto in versione cartacea. Dopo aver comprato il Kindle, non ho proprio voglia di prendere altra roba "scomoda", e per giunta costosa.E' un libro fantastico (la libreria di I/O è la più performante che ci sia).
Purtroppo quel libro è disponibile soltanto in versione cartacea. Dopo aver comprato il Kindle, non ho proprio voglia di prendere altra roba "scomoda", e per giunta costosa.
Comunque il capitolo più menzionato da libri di compilatori o di design è "How the language got its spots"
Citazione da: cdimauro - 27 Aprile 2014, 14:56:27Comunque ho letto velocemente sulla paginetta wiki di Modula-3 che l'operator overloading fu scartato perché ritenuta una funzionalità complicata e pericolosa?Credo che pericoloso sia riferito al multiple inheritance e che complicating sia riferito all'operator overloading
Comunque ho letto velocemente sulla paginetta wiki di Modula-3 che l'operator overloading fu scartato perché ritenuta una funzionalità complicata e pericolosa?
Praticamente il Type System non è a conoscenza del genericity, i moduli generici vengono istanziati da quake (il make tool di Modula-3) e il compilatore si ritrova solo i moduli specializzati. Questo elimina un sacco di problemi tipo un type system complesso e il code bloating/memory hogging dei template C++.
Non so cosa tu intenda che per il modello del C++ non ci sia codice duplicato, tutto l'opposto. Sarò breve, perché ho il tempo contato con il contagocce.
Limitare il genericity solo ai reference object è la soluzione adottata da ISO-Modula-2, Modula-3, Oberon-2ext, Java.
Shared object:Il codice generato è unico per tutti i casi possibili. Vengono utilizzate le classi estese più grosse (T2 e P2) e le classi base vengono allocate con un padding tutte della stessa dimensione.Per il parametro basic type viene usato il basic type più grande rappresentabile.Numero totale di codice oggetto per classe: 2, numero funzioni: 2.Vantaggi: Velocità di compilazione, easy debugging, no code bloating.Svantaggi: Il codice delle classi occupa leggermente di più, overhead sui basic type, aritmetica lenta (gli interi vengono usati all'intero più grosso di 128 bit, i float sono tutti a precisione doppia, etc..).
Quanti sono i primitive type in C++? 14? 9*2*14 = 252 funzioni in totale. Il C++ linker ovviamente eliminerà gran parte (se non quasi tutte) le istanze non utilizzate, ma sta di fatto che il compilatore per motivi di type checking andrà a generare tutto questo garbage.
Vantaggi: Storage economy nell'allocazione delle classi, no overhead sui basic type, nessuna influenza sull'aritmetica.
Se consideri l'istanziamento del generic/template sì, ma forse mi sono espresso male, mi riferisco qui all'object code delle classi, essendo 2 (indipendentemente dalle estensioni, dato che viene fatto il padding prendendo la classe con dimensione più grande) ci sono 2 object code per due classi. Invece se si vanno a considerare le estensioni sono 3+3 (6) object code, in tal modo si risparmia quando si alloca su stack.Non mi riferivo al codice che va ad istanziare il generic/template (che sarebbe in questo caso il numero di funzioni). Forse tu consideri un template class (in questo caso sì, solo una istanza, anche se sarebbe da considerare a parte), in Ada il genericity è a livello modulare quindi ho considerato solo l'istanziamento delle due funzioni (che sarebbe 1 istanziamento del generic).
No, se sono typechecked vengono istanziate tutte, poi quelle inutilizzate vengono rimosse dal linker.(Tu dirai, che ne può sapere se non conosce il C++?) Da Programming Language Pragmatics:CitazioneIn the usual case, the compiler creates a separate copy of the code for every instance. (C++ goes farther, and arranges to type-check each of these instances independently.)[...]C++ will create specialized implementations of a generic for different built-in or value types.[...]C++ is by far the most ambitious. Its templates are intended for almost any programming task that requires substantially similar but not identical copies of an abstraction.
In the usual case, the compiler creates a separate copy of the code for every instance. (C++ goes farther, and arranges to type-check each of these instances independently.)[...]C++ will create specialized implementations of a generic for different built-in or value types.[...]C++ is by far the most ambitious. Its templates are intended for almost any programming task that requires substantially similar but not identical copies of an abstraction.
Se ti interessa il language design ed il language implementation, è un must (1000 pagine circa).
Ehm, non credo sia possibile fare type checking su un template non istanziato a meno che non ci sia qualche metodo che non conosco. Il type checking richiede che tutti i tipi di un determinato blocco facciano parte della symbol table, in modo tale che dopo il lookup si hanno informazioni sugli operatori implementati da quel tipo (=, <, o >=, etc).
template<typename T>T modulo(T a, T b) { return a % b;}...int res = modulo(10, 5);
In realtà c'è una soluzione a questo problema, ovvero il constrained genericity di Eiffel, nella definizione del template puoi passare determinate interfacce di tipi, prendendo l'esempio di wikipedia in quel caso puoi passare solo tipi che implementano l'interfaccia comparable (=, !=, <,>, <=, >=).Qui puoi fare un po' di type checking senza istanziare il template prima, perché conosci un minimo di informazioni sugli operatori che implementa
Io mi chiedo perché concetti semplici e lineari devono renderli immensamente complicati in C++, i concepts sono estremamente perversi rispetto al constrained genericity. Basterebbe solo che implementassero le interfacce
Limitare il genericity solo ai reference object è la soluzione adottata da ISO-Modula-2, Modula-3, Oberon-2ext, Java.Ci sono due code pattern generation: lo shared object (di Ada) e specific object instantiation (di C++).Immaginiamo di avere un generic package o template che accetta due classi con tre livelli di extension ed un tipo basic type come parametro, vengono usate per due funzioni all'interno del package/template. Classi T, T2 (estende T), T3 (estende T2), P, P1 (estende P), P2 (estende P1).Shared object:Il codice generato è unico per tutti i casi possibili. Vengono utilizzate le classi estese più grosse (T2 e P2) e le classi base vengono allocate con un padding tutte della stessa dimensione.Per il parametro basic type viene usato il basic type più grande rappresentabile.Numero totale di codice oggetto per classe: 2, numero funzioni: 2.Vantaggi: Velocità di compilazione, easy debugging, no code bloating.Svantaggi: Il codice delle classi occupa leggermente di più, overhead sui basic type, aritmetica lenta (gli interi vengono usati all'intero più grosso di 128 bit, i float sono tutti a precisione doppia, etc..).Specific object instantiation:Viene istanziato codice per ogni classe in ogni sua estensione e per ogni basic type.Trattiamo prima il caso delle classi:Le classi vengono istanziate in ogni loro estensione, T, T1, T2, P, P1, P2. Il codice del template (le due funzioni) viene allocato per ogni coppia: T, P; T, P1; T, P2; ... T3, P3;Numero totale di codice oggetto per classe: (3+3) 6, numero funzioni: 18 (3*3 * 2).Quanti sono i primitive type in C++? 14? 9*2*14 = 252 funzioni in totale. Il C++ linker ovviamente eliminerà gran parte (se non quasi tutte) le istanze non utilizzate, ma sta di fatto che il compilatore per motivi di type checking andrà a generare tutto questo garbage.Vantaggi: Storage economy nell'allocazione delle classi, no overhead sui basic type, nessuna influenza sull'aritmetica.Svantaggi: Super code bloat, ultra slow compilation time, nightmare debugging.I template del C++ sono comunque (inutilmente) molto avanzati e complicano di tantissimo questo code pattern generation. Non sono un esperto di C++ per poter dire di più.Mi dilungherei sulle alternative/varianti ma per ora non posso, non ho nemmeno riletto per vedere se ho scritto inesattezze
auto mean( Sequence seq ){ return std::accumulate( begin(seq), end(seq), 0.0 ) / seq.size(); }
Ci sono due code pattern generation: lo shared object (di Ada) e specific object instantiation (di C++).Immaginiamo di avere un generic package o template che accetta due classi con tre livelli di extension ed un tipo basic type come parametro, vengono usate per due funzioni all'interno del package/template. Classi T, T2 (estende T), T3 (estende T2), P, P1 (estende P), P2 (estende P1).Shared object:Il codice generato è unico per tutti i casi possibili. Vengono utilizzate le classi estese più grosse (T2 e P2) e le classi base vengono allocate con un padding tutte della stessa dimensione.Per il parametro basic type viene usato il basic type più grande rappresentabile.Numero totale di codice oggetto per classe: 2, numero funzioni: 2.Vantaggi: Velocità di compilazione, easy debugging, no code bloating.Svantaggi: Il codice delle classi occupa leggermente di più, overhead sui basic type, aritmetica lenta (gli interi vengono usati all'intero più grosso di 128 bit, i float sono tutti a precisione doppia, etc..).
Citazione da: fulvio - 30 Aprile 2014, 13:15:25Principalmete che linguaggio usi?Questa me la chiedo anche io
Principalmete che linguaggio usi?
Citazione da: cdimauro - 01 Maggio 2014, 06:55:05E da Python, che ha soltanto reference object alla fine. D'altra parte è la soluzione più semplice.E va bene in tutti i casi. Ho visto povere argomentazioni contro questo tipo di genericity, come per esempio quando fanno vedere che le operazioni su intero/float wrappate in un object reference sono lente per via di quello che chiamano boxing/unboxing.I container sono fatti per conservare oggetti, non per farci dei calcoli di sopra. Un buon programmatore si copia il tipo di dato in una struttura locale (o direttamente in delle variabili) se c'è una lunga computazione da fare
E da Python, che ha soltanto reference object alla fine. D'altra parte è la soluzione più semplice.
Citazione da: cdimauro - 01 Maggio 2014, 06:55:05Gli interi dovrebbero arrivare a 64 bit al massimo.Sì, hai ragione, non so per quale motivo ricordassi che GNAT supportasse 128 bit (con l'uso di due registri e 16 byte di allocazione nelle strutture). Invece l'implementazione supporta fino a 64 bit, boh.. starò tirando le cuoia
Gli interi dovrebbero arrivare a 64 bit al massimo.
Citazione da: cdimauro - 01 Maggio 2014, 06:55:05A parte questo dettaglio, non capisco per quale motivo per il basic type si debba utilizzare il più grande rappresentabile. Se utilizzi un basic type come parametro per il generic package, che viene usato poi in tutte le classi di cui sopra, e mettiamo che tale template venga "istanziato" usando soltanto interi a 8 e 16 bit, a conti fatti il compilatore dovrebbe utilizzare l'intero più grande utilizzato, quindi quello a 16 bit, anziché la massima rappresentazione (32 o 64 bit) per l'unica copia di dati (definiti nelle classi, come parametri dei metodi, e come variabili locali) e codice che ne fa uso.In C++ una cosa del genere si potrebbe accettare (se solo non supportasse l'uso implicito dei template) ma in Ada no. In C++ un vero separate compilation non esiste.I generics in Ada vengono compilati una sola volta e viene generato un unico (shared) object code per sempre. Se prima l'object code è stato generato per il basic type (più grande istanziato) a 16 bit e poi vari mesi dopo in un modulo X è stato istanziato per 32 bit, allora l'object code del generic andrebbe rigenerato e tutti i package che fanno uso del generic package andrebbero ricompilati perché cambiano tutti gli offset. Questo va contro il principio del modularity, solo la modifica dell'interfaccia del modulo implica la ricompilazione del modulo e di tutti moduli che lo utilizzanoIl modularity è un big topic, però stringendo in Ada una cosa del genere non sarebbe accettabile :-)
A parte questo dettaglio, non capisco per quale motivo per il basic type si debba utilizzare il più grande rappresentabile. Se utilizzi un basic type come parametro per il generic package, che viene usato poi in tutte le classi di cui sopra, e mettiamo che tale template venga "istanziato" usando soltanto interi a 8 e 16 bit, a conti fatti il compilatore dovrebbe utilizzare l'intero più grande utilizzato, quindi quello a 16 bit, anziché la massima rappresentazione (32 o 64 bit) per l'unica copia di dati (definiti nelle classi, come parametri dei metodi, e come variabili locali) e codice che ne fa uso.
Citazione da: cdimauro - 01 Maggio 2014, 06:56:06Citazione da: legacy - 30 Aprile 2014, 16:02:36Citazione da: fulvio - 30 Aprile 2014, 13:15:25Principalmete che linguaggio usi?Questa me la chiedo anche io Azzardo: PHP e/o Javascript. Credo non potesse finirmi peggio
Citazione da: legacy - 30 Aprile 2014, 16:02:36Citazione da: fulvio - 30 Aprile 2014, 13:15:25Principalmete che linguaggio usi?Questa me la chiedo anche io Azzardo: PHP e/o Javascript.