Zack Urlocker
Responsabile in capo del prodotto Delphi
presso Borland International
Rilasciato il 3 gennaio 1996
Traduzione di G. Bracci - Delphi Team Roma - del 5 marzo 1996.
NOTA DEL TRADUTTORE: come compromesso tra leggibilità in italiano, precisione della traduzione, chiarezza del contesto, alcune espressioni tecniche o in gergo professionale sono state lasciate in lingua originale.
Borland Delphi e Delphi Client/Server hanno definito un nuovo standard negli strumenti R.A.D. (Rapid Application Development) ad elevate prestazioni. Grazie alle sue caratteristiche peculiari, uniche tra gli strumenti R.A.D., - compilatore (non interprete) ottimizzato, editor visuale e testuale a due vie (è possibile editare quanto inserito visualmente), reale scalabilità delle applicazioni database - Delphi ha vinto decine di premi e riconoscimenti in tutto il mondo divenendo il linguaggio a più rapida diffusione. Inoltre ha raggiunto un elevatissimo grado di supporto terze-parti con componenti e utilities add-on, oltre 30 libri, riviste mensili, newletters, ed un sempre maggior numero di corsi di formazione tenuti da un numero crescente di consulenti terze-parti.
Ora Borland ha sviluppato la seconda generazione 32-bit di Delphi, col nome di Delphi 2.0. Questa nuova versione 32-bit incorpora molte nuove tecnologie incrementando ulteriormente la produttività dei programmatori e le prestazioni delle loro applicazioni. Delphi 2.0 è basato su un nuovo compilatore ottimizzato 32-bit in grado di offrire migliori prestazioni e gestione di memoria. Inoltre l'architettura del nuovo compilatore aiuta i programmatori a scrivere codice corretto grazie al migliorato controllo degli errori e ad una più efficiente gestione dei moduli. Delphi 2.0 poi si avvantaggia delle più recenti tecnologie OLE includendo OCX e OLE automation. Delphi pertanto supporta tutte le caratteristiche di Windows 95 e Windows NT.
Il presente documento spiega caratteristiche e benefici del nuovo compilatore nativo ottimizzato 32-bit. In Delphi 2.0 sono presenti molte innovazioni per usufruire dei benefici delle piattaforme Windows 95 e NT. L'interfaccia utente e le API di Windows 95 sono pienamente supportatw da Delphi 2.0 con l'inclusione di nuovi specifici componenti, la gestione di threads multipli, unicode, MAPI... Delphi ha ottenuto il logo Windows 95 e rende semplice l'ottenimento del logo stesso da parte degli sviluppatori per le loro applicazioni.
Gli obbiettivi che ci siamo prefissi con Delphi 2.0 sono:
Per costi, configurazioni, ed altre caratteristiche commerciali ->
Tra i Clienti Delphi possiamo citare: Alcatel, American Stores, Arthur Anderson, AT&T, BMW, BP Shipping, Bank of America, BBC Television, British Telecom, City of Los Angeles, Compaq, Conoco, Coopers & Lybrand, DHL, Dover Elevators, EDS, Ernst & Young, Fiat, First National Bank of Chicago, Glaxo, KPMG, Mercury Communications, Netscape, Sarah Lee Knitting, Standard & Poors, SwissBank SG Warburg, Union Bank, US Marine Corps, e molte altre ancora: per le referenze italiane V. . Delphi and Delphi Client/Server sono usati per un vasto assortimento di applicazioni che richiedono sviluppo rapido ed alte prestazioni. Alcuni esempi:
E' possibile richiedere documenti che descrivono queste e molte altre applicazioni con maggior dettaglio.
Sommario: Delphi 2.0 include un nuovo compilatore ottimizzato che ha le seguenti caratteristiche:
La nuova versione 32-bit di Delphi è costruita su un compilatore 32-bit ottimizzato completamente nuovo. Borland ha trasferito la sua esperienza ultradecennale come leader nei compilatori in questa versione. Il compilatore condivide la stessa tecnologia utilizzata nel pluripremiato compilatore C++ di Borland. Ciò significa che non solo il codice Delphi si avvantaggia ulteriormente rispetto ai sistemi che interpretano p-code, ma anche che è molto più semplice condividere codice tra Delphi e C++ poiché entrambi possono generare il formato .OBJ per i files oggetto. In aggiunta, poich´ Delphi è un compilatore nativo, non esistono DLL runtime da consegnare insieme alle applicazioni.
Tutti i tests sono stati eseguiti su un Gateway 2000 V66 (i486DX2/66MHz) con 16 MB di RAM. I benchmarks 16-bit hanno girato su Windows 3.1; per quelli 32-bit è stata usata una pre-release di Delphi 2.0.
Maggiore è il numero, migliore il risultato
loops / PowerBuilder Visual Basic Delphi 16 Delphi sec 3.0 3.0 bit 2.0 Sieve 0.22 11.95 52.77 179.37 Whetstone 0.04 1.41 4.70 15.53 File 0.05 0.42 0.74 2.89 write File 0.05 0.33 1.75 5.28 read
I risultati mostrano come il codice ottenuto con Delphi 2.0 giri offra mediamente prestazioni del 300%-400% superiori di Delphi 1.0. Ciò significa che Delphi continua ad allargare il gap con gli interpreti di p-code risultando oltre 10-20 volte più veloce. Per esempio, i risultati del test "Sieve" sono 15 volte superiori a VB 3.0 e 815 volte di PowerBuilder 3.0. Inoltre, grazie al nuovo linker ottimizzato, gli eseguibili sono più piccoli del 20%-25% ed ancora una volta non richiedono DLL run-time.
Il nuovo compilatore 32-bit ottiene questi incrementi prestazionali utilizzando molte nuove tecniche di ottimizzazione del codice. In passato, ottimizzare codice richiedeva spesso molti tentativi attraverso l'uso di complesse direttive al compilatore. Inoltre, spesso ciò comportava l'uso di più passaggi di compilazione rallentando e scoraggiando processi R.A.D. Delphi 2.0 usa molte nuove tecniche di ottimizzazione in modo automatico senza bisogno di interventi da parte del programmatore. Di più, il compilatore resta il più veloce compilatore al mondo raggiungendo oltre 350.000 righe di codice al minuto su un pentium. Come risultato, il programmatore gode sempre dei benefici del R.A.D. ottenendo altissime prestazioni.
Tra le ottimizzazioni del nuovo compilatore 32-bit possiamo citare l'ottimizzazione dei registri, l'eliminazione dell'overhead dello stack nelle chiamate, l'eliminazione di "common subexpression", "loop induction variable" le quali producono prestazioni più elevate con la maggior parte del codice. Tutte le ottimizzazioni compiute dal compilatore 32-bit sono garantite e non alterano in alcun modo il flusso del codice. Le ottimizzazioni sono comunque disabilitabili, ad esempio per scopi comparativi. Le ottimizzazioni inoltre includono l'opzione; di generare codice "a prova di bug del pentium" garantendo così il corretto funzionamento su pentium di applicazioni che usano divisioni in floating point.
Variabili usate frequentemente sono piazzate automaticamente nei registri dell CPU riducendo quindi il numero di cicli per accedervi. Il risultato è un codice più veloce e compatto poiché si risparmia il tempo e le istruzioni di caricamento delle variabili dalla memoria alla CPU. L'ottimizzazione è automatica; non c'è bisogno di alcuna specifica nella definizione della variabile. Inoltre il compilatore esegue autoamticamente delle analisi sul tempo di vita delle variabili assicurando un uso efficiente dei registri. Se ad esempio due variabili all'interno di una routine vengono usate frequentemente man in parti diverse di codice (una prima e una dopo), il compilatore assegna lo stesso registro per entrambe.
Ogni volta che risulta possibile i pramaetri sono passati alle funzioni usando i registri anziché lo stack. Questo non solo elimina gli accessi di memoria come descritto poc'anzi, ma elimina anche la necessità di gestire lo stack in cui le variabili vengono temporaneamente depositate durante la chiamata. vengono così eliminate le istruzioni per creare/distruggere lo spazio nello stack e deporre le variabili, quindi il codice risulta molto più efficiente.
Quando il compilatore deve tradurre complesse espressioni matematiche, elimina ogni subespressione comune il cui calcolo deve essere ripetuto più volte. il programmatore può quindi scrivere codice chiaro e leggibile, sapendo che il compilatore poi ottimizzerà opportunamente.
Il compilatore automaticamente usa "loop induction variables" come metodo per velocizzare l'accesso a matrici o stringhe. Se una variabile è ustata solo per indicizzare un array, p.es. in un ciclo for, il compilatore "induce" la variabile, elimnando operazioni di moltiplicazione sostituendole con un puntatore che viene incrementato per accedere agli elementi della matrice. Inoltre se la dimensione dell'elemento è 1, 2, 4 o 8 bytes, viene usato l' "Intel scale indexing" per aumentare uteriormente le prestazioni.
Il seguente codice illustra l'uso di variabili nei regiutri e "loop induction". l'espressione Sums[X-1] è ridotta a una "induction variable"- un registro che è incrementato in parallelo alla variabile di ciclo X, piuttosto che ricalcolata ad ogni iterazione. program example1;
uses Windows;
var
Sums : array [0..500] of Integer;
X: Integer;
begin
Sums[0] := 0;
for X := 1 to High(Sums) do
Sums[X] := Sums[X-1] + X;
writeln(Sums[High(Sums)]);
end.
Il codice che il nuovo compilatore 32-bit genera per questo programma, come mostrato da Turbo Debugger:
ex1.9: Sums[0] := 0;
xor eax,eax
mov [ex1.Sums],eax
ex1.10: for X := 1 to High(Sums) do
mov edx,00000001 ; EDX = X, the loop control variable
mov eax,0040242C ; EAX = the base memory address of Sums
ex1.11: Sums[X] := Sums[X-1] + X;
mov ecx,[eax] ; Get the value stored at Sums[X-1]
add ecx,edx ; Add X to that value
mov [eax+04],ecx ; Store the sum in Sums[X]
inc edx ; increment the loop control variable
add eax,00000004 ; Increment the induction variable
cmp edx,000001F5 ; Test for loop end
jne ex1.11 (0040185D)
ex1.12: writeln(Sums[High(Sums)]);
mov edx,[EX1.00402BFC]
mov eax,00402238
call @Write0Long
call @WriteLn
call @_IOTest
ex1.13: end.
Il nuovo linker 32-bit usato da Delphi durante il processo di compilazione include nuove ottimizzazioni per operare in modo più efficiente. Il nuovo linker è dal 20% al 50% più veloce grazie al nuovo schema di caching. In altri termini, i linking successivi al primo di forms o units non cambiati sono eseguiti direttamente in memoria piuttosto che ustilizzando il disco rigido. Inoltre gli eseguibili prodotti sono del 20-25% più senza il bisogno di DLL run-time.
Il linker inoltre usa un più efficace controllo delle versioni sulle units eliminando compilazioni non necessarie, diminuendo il tempo complessivo di compilazione ed una maggior congruità delle librerie. Per esempio, supponiamo che una funzione cambi in una libreria usata da quattro forms differenti. In passato, tutte e quattro le forms dovevano essere ricompilate pociché il codice immagazzinato su disco (il file .DCU) era una fotografia del prodotto di compilazione; bastava il cambio di un solo simbolo per scatenare il processo di compilazione per le units interessate. Ora il linker aggiorneraà solo le units che effettivamente usano le funzioni necessitano di ricompilazione. Questo rende tra l'altro molto più semplice la distribuzione di librerire terze parti che non sono costrette a ricompilare le loro librerie solo perché viene rilasciata una nuova versione di Delphi.
Inoltre Delphi 2.0 supporta files oggetto in formato .OBJ rendendo più semplice la condivisione di codice con C /C++ oltre alla capacità di creare e condividere DLL. Poiché molte librerie di funzioni C sono reperibili in formato .OBJ, aumenta di molto il numero di librerie che possono essere usate con Delphi.
Il nuovo compilatore 32-bit gira in uno spazio di indirizzamento piatto a 32-bit eliminando quindi tutte le limitazioni connesse all'architettura segmentata a 16 bits di Windows 3.1. I programmatori possono così utilizzare tutta la memoria fisica presente sul sistema senza l'uso di chiamate dirette alle API. Per esempio è ora possibile dichiarare matrici, stringhe, records, ed altre strutture dati grandi a piacere, vincolate solo dai limiti imposti dal sistema operativo. Si possono così ottenere stringhe di 2 gigabytes.
Il nuovo compliatore nativo 32-bit introduce nuovi tipi di dato per sfruttare le capacità di indirizzamento piatto a 32-bit, con in più la felssibilità di una facile migrazione dal codice 16-bit.
Questi nuovi tipi di dato comprendono:
Il compilatore Delphi 2.0 aggiunge inoltre le seguenti innovazioni:
Sommario: Delphi 2.0 incorpora il supporto completo per l'utilizzo di OLE in Windows 95 ed NT come p.es:
La tecnologia OLE di Microsoft offre molte potenzialità per creare applicazioni più modulari ed integrate. Con Delphi 2.0 si è voluto aderire completamente agli standard Microsoft per garantire agli sviluppatori che usano Delphi la possibilità di creare ogni tipo di programma senza limitiazioni. Delphi 2.0 va oltre la semplice adsione agli standard Microsoft; il nostro obbiettivo è stato di rendere l'uso della tecnologia OLE molto più semplice rendendola completamente orientata agli oggetti. Come risultato, la tecnologia OLE è completamente integrata in Delphi 2.0 assicurando che la compatibilità alle nuove tecnologie (come Network OLE) è assicurata.
Il supporto OLE in Delphi 2.0 include la capacità di installare ed usare facilmente gli OCX, nonché di crearli e creare OLE servers altrettanto facilmente. Per massima flessibilità Delphi 2.0 può creare servers sia in-process che out-process. Poiché supporta OLE automation controllers e servers, Delphi 2.0 è completamente compatibile con la nascitura Network OLE così come la tecnologia "remote automation" di VB 4.0, ma in modo molto più performante di questo. Inoltre, poiché Delphi 2.0 è un compilatore nativo, gli sviluppatori esperti possono sviluppare OCX, anche se non così semplicemente come i componenti Delphi.
Delphi utilizza un nuovo tipo, variant, per ottenere completa integrazione con OLE automation. Delphi 2.0 consente di scrivere applicazioni OLE sia controller che server, con la stessa facilità. I controller OLE sono quelli usualmente più diffusi tra gli sviluppatori ed intergratori di sistemi. Per esempio, un'applicazione Delphi può essere utilizzata per controllare altre applicazioni OLE, quali Word, Excel, Paradox, Quattro Pro...
Il tipo variant consente di dichiarare variabili il cui tipo è determinato a run-time, adeguandosi così alla flessibilità dell'automazione OLE. In altri termini, è possibile usare una singola variabile per connettersi a differenti tipi di OLE a runtime. Delphi 2.0 inoltre introduce la capacità di usare parametri nominali (non posizionali) durante le chiamate a servers OLE. Per le funzioni complesse, che spesso hanno decine di parametri, lo sviluppatore può fornire solo quelli pertinenti lasciando al server di utilizzare i valori di default per tutti gli altri.
Il programma che segue deriva da un'applicazione di esempio in Delphi 2.0 che effettua una query, quindi inserisce il risultato in un documento Word. Nota che l'automazione OLE richiede solo quattro linee di codice, oltre alla dichiarazione della variabile variant MSWord.
{ Questo esempio usa OLE automation per inserire il risultato di una query in MS-Word}
procedure TForm1.InsertBtnClick(Sender: TObject);
var
MSWord: Variant;
S: string;
L: Integer;
begin
{ Esegue la connessione al server OLE MS Word e lancia la query}
MSWord := CreateOleObject('Word.Basic');
with Query1 do
begin
Close;
Params[0].Text := Edit1.Text;
Open;
try
First;
L := 0;
while not EOF do
{ Store the query result set in string S }
begin
S := S + Query1Company.AsString + ',' +
Query1OrderNo.AsString + ',' + Query1SaleDate.AsString + #13;
Inc(L);
Next;
end;
{ Use OLE automation to insert S into the Word document }
MSWord.Insert(S);
MSWord.LineUp(L, 1);
MSWord.TextToTable(ConvertFrom := 2, NumColumns := 3);
finally
Close;
end;
end;
end;
Delphi 2.0 consente di creare servers OLE. Questi possono essere servers sia in-process che out-of-process (o locali). E' possibie esporre funzioni o metodi della tua applicazione, che possono essere invocati da altre applicazioni quali Microsoft Word, Excel, Visual Basic, C++, Paradox and Delphi 2.0. Dato che Delphi 2.0 può generare sia controllers che servers OLE, offre un vantaggio unico nelle prestazionim il cui vantaggio emergerà sempre più con Network OLE. Con Delphi 2.0 si otterrano applicazioni frazionate sviluppate col più veloce codice compilato sia sul lato client che server.
Per creare un server OLE si può usare l'Expert "Automation Object". L'Expert definisce automaticamente un nuovo oggetto derivato da TAutoObject e produce tutte le registrazioni OLE come il program ID, class ID e le opzioni di istanziazione.
Quindi si possono definire proprietà e metodi per l'automazione semplicemente aggiungendoli alla sezione automated section dell'oggetto. La visibiltà di un identificatore dichiarato in una sezione automated è la stessa di un identificatore public. E' possibile esporre proprietà, paramateri e valori di ritorno di funzione dei seguenti tipi: Smallint, Integer, Single, Double, WordBool, Boolean, Currency, TDateTime, String and Variant.
L'esempio riportato mostra come usare la sezione automated per consentire ad un controller OLE l'utilizzo di un memo editor. Da questo è possibile creare sia un in-process automation server (es. una DLL) o un out-of-process OLE automation server (ovvero un .EXE).
{ Mostra l'uso di OLE automation server con Delphi 2.0 }
unit MemoAuto;
interface
uses
OleAuto;
type
{ TMemoApp defines the automation server object and its services}
TMemoApp = class(TAutoObject)
private
function GetMemo(Index: Integer): Variant;
function GetMemoCount: Integer;
automated
{ OLE enable the following properties and functions }
procedure CascadeWindows;
function NewMemo: Variant;
function OpenMemo(const FileName: string): Variant;
procedure TileWindows;
property MemoCount: Integer read GetMemoCount;
property Memos[Index: Integer]: Variant read GetMemo;
end;
implementation
...
{ The registration info is created by the Automation Object Expert.}
procedure RegisterMemoApp;
const
AutoClassInfo: TAutoClassInfo = (
AutoClass: TMemoApp;
ProgID: 'MemoEdit.Application';
ClassID: '{F7FF4880-200D-11CF-BD2F-0020AF0E5B81}';
Description: 'Memo Editor Application';
Instancing: acSingleInstance);
begin
Automation.RegisterClass(AutoClassInfo);
end;
initialization
RegisterMemoApp;
end.
Poiché Delphi 2.0 è un ambiente completamente object-oriented, l'integrazione dei controlli OLE è immediata. E' possibile installare OCX terze parti nello stesso modo dei normali componenti Delphi. Delphi 2.0 fornisce accesso completo al "OLE system registry" consentendo il caricamento di OCX e la loro registrazione in una semplice dialog.
Quando si installa un OCX, Delphi crea automaticamente un object wrapper che consnte un approccio completamente object-oriented. Delphi 2.0 è l'unico R.A.D. che consente di usare gli OCX in modo completamente object-oriented. Questo rende possibile agli sviluppatori di effettuare facilmente il subclassing di qualunque componente, in modo tale che possano esser facilmente personalizzati attraverso l'ereditarietà.
Segue un estratto dal codice che è generato automaticamente e compilato "dietro le scene" quando viene installato un OCX.
{ An object wrapper is automatically generated by Delphi when you install any OCX }
TGraph = class(TOleControl)
private
FOnHotHit: TGraphHotHit;
function Get_Color(Index: Smallint): Smallint; stdcall;
procedure Set_Color(Index: Smallint; Value: Smallint); stdcall;
function Get_Data(Index: Smallint): Single; stdcall;
procedure Set_Data(Index: Smallint; Value: Single); stdcall;
...
public
property Color[Index: Smallint]: Smallint read Get_Color write Set_Color;
property Data[Index: Smallint]: Single read Get_Data write Set_Data;
...
published
property TabOrder;
property OnClick;
property OnDblClick;
...
end;
Una volta installato nel pannello dei componenti di Delphi, un OLE può essere usato come qualsiasi altro componente Delphi. Tutte le proprietà ed eventi dell'OCX sono completamente accessibili dall'Object Inspector.
Poiché Delphi 2.0 è un compilatore nativo, sfrutta tutte le caratteristiche offerte da Windows 95 and NT. Tra queste possiamo citare:
E' possibile creare facilmente applicazioni multi-threaded selezionando l'oggetto New Thread dal Delphi Object Repository. Questo genererà una unit con un oggetto che deriva da TThread semplificando la creazione di applicazioni multi-threaded. E' comunque sempre possibile accedere direttamente alle API di gestione dei Threads. Ad esempio è possibile chiamare l'API CreateThread con una funzione Object Pascal function come parametro. Allo stesso modo è possibile modificare la priorità del Thread chiamando l'API SetThreadPriority.
La libreria VCL di Delphi include il supporto per creare applicazioni multi-threaded sicure. Ad esempio il metodo Synchronize della classe TThread class aasicura che la manipolazione di componenti VCL viene svolta in modo sicuro in un thread senza alcuna possibilità di conflitto con altri threads. Delphi 2.0 inoltre introduce una nuova parola riservata ThreadVar che consente di dichiarare variabili locali al Thread, dando completo controllo sulle variabili usate in threads differenti.
Di seguito viene mostrato un codice che illustra come è possibile usare la classe TThread per creare un programma di ordinamento multi-threaded. Vengono creati tre discendenti della classe TSortThread, ognuno dei quali definisce il proprio algoritmo di sort.
{Example of multi-threading in Delphi 2.0 }
TSortThread = class(TThread)
private
FBox: TPaintBox;
FSortArray: PSortArray;
FSize: Integer;
FA, FB, FI, FJ: Integer;
procedure DoVisualSwap;
protected
procedure Execute; override;
procedure VisualSwap(A, B, I, J: Integer);
procedure Sort(var A: array of Integer); virtual; abstract;
public
constructor Create(Box: TPaintBox; var SortArray: array of Integer);
end;
TBubbleSort = class(TSortThread)
protected
procedure Sort(var A: array of Integer); override;
end;
...
implementation
{ TSortThread }
constructor TSortThread.Create(Box: TPaintBox; var SortArray: array of Integer);
begin
inherited Create(False);
FBox := Box;
FSortArray := @SortArray;
FSize := High(SortArray) - Low(SortArray) + 1;
end;
{ Since DoVisualSwap uses a VCL component (i.e., the TPaintBox) it should never be called directly by this thread. DoVisualSwap should be called by passing it to the Synchronize method which causes DoVisualSwap to be executed by the main VCL thread, avoiding multi-thread conflicts.}
procedure TSortThread.DoVisualSwap;
begin
with FBox do
begin
Canvas.Pen.Color := clBtnFace;
PaintLine(Canvas, FI, FA);
PaintLine(Canvas, FJ, FB);
Canvas.Pen.Color := clRed;
PaintLine(Canvas, FI, FB);
PaintLine(Canvas, FJ, FA);
end;
end;
{ VisusalSwap is a wrapper on DoVisualSwap making it easier to use. The
parameters are copied to instance variables so they are accessable
by the main VCL thread when it executes DoVisualSwap }
procedure TSortThread.VisualSwap(A, B, I, J: Integer);
begin
FA := A;
FB := B;
FI := I;
FJ := J;
Synchronize(DoVisualSwap);
end;
{ The Execute method is called when the thread starts }
procedure TSortThread.Execute;
begin
Sort(Slice(FSortArray^, FSize));
end;
...
end;
Sommario: L'architettura del nuovo compilatore di Delphi 2.0 rende più facile la stesura di programmi corretti tramite:
Uno dei vantaggi trascurati dei compilatori più frequentemente trascurati è la capacità di un controllo completo del programma prima del lancio. I compilatori possono spesso individuare errori logici prodotti da codifica errata o ambigua, che un interprete non può individuare. Poiché l'Object Pascal è un linguaggio fortemente tipizzato, previene il programmatore da molti errori comuni derivati dall'uso errato dei vari tipi. Il nuovo compilatore ha un'architettura "multi-error" non si ferma cioè al primo errore ma prosegue sinché possibile per determinare tutti i problemi. Ciò rende molto più semplice controllare grandi programmi.
In Delphi 2.0, abbiamo migliorato i messaggi di errore e diagnostici del compilatore riuscendo così a fornire consigli ed avvertimenti quando il codice potrebbe essere errato.
Ciò aiuta ad eliminare molti errori comuni quali:
Il compilatore offre inoltre una migliore diagnostica sugli errori di sintassi rendendo più facile la programmazione anche ai novizi del Object Pascal. Invece di riportare un generico "Error in statement" il nuovo compilatore da una indicazione del problema molto più chiara. Ad esempio
Delphi ha sempre usato ilconcetto di compilazione separata dei moduli, chiamati units, al fine di facilitare lo sviluppo ed il testing di grandi applicazioni. Infatti è così possibile costringere ad una più rigida disciplina di interfacciamento tra le units piuttosto che inserire un gran numero di variabili globali, che sono potenzialmente dannose oltre che difficilmente mantenibili. Comunque, novizi di Delphi hanno riscontrato difficoltà nell'aggiornare manualmente le sezioni uses. In Delphi 2.0, ciò è stato semplificato introducendo un nuovo comando - File Uses ed il link delle forms.
inoltre l'ambiente di sviluppo è più intelligente e capisce quando una uses deve essere automaticamente aggiornata. Ad esempio, se Form2 viene riferita da Unit dentro Form1, viene proposto di aggiungere Unit2 alla uses di Unit a tempo di compilazione.
Attraverso questa tecnica è ora possibile accedere nell'ambiente visuale a oggetti, proprietà, e codice presenti in forms differenti da quella in uso. Perciò in Delphi 2.0 è possibile unire questi componenti a design-time senza bisogno di scrivere codice rendendo più facile la creazione di moduli riutilizzabili che incapsulano dati e regole applicative separati dai componenti dell'interfacia utente
Sommario: Delphi 2.0 estende il supporto OOP offrendo la "Visual Form Inheritance".
Una delle caratteristiche più apprezzate di Delphi è il completo supporto alla programmazione object-oriented. Grazie al completo supporto a incapsulamento, polimorfismo ed ereditarietà presente in Object Pascal, Delphi offre, unico, la capcità agli sviluppatori di creade i loro popri oggetti, sia sub-classando componenti ed altri oggetti preesisteti in Delphi, che creandone di completamente nuovi. E poiché Delphi è scritto in Delphi, non esiste distinzione tra i tipi di oggeto creati da Borland e quelli scritti dal programmatore. Infatti, molte terze parti hanno creato un grande e sempre crescente mercato per i componenti.
Delphi 2.0 takes accoclie i concetti fondamentali dell'OOP e li estende all'ambiente visuale al fine di rendere la derivazione più da usare ed accessibile E' ora possibile derivare visualmente da forms durante la fase visuale, senza scrivere codice, e verificarne immediatamente gli effetti. Per esempio in molte grandi aziende si desidera creare un aspetto "standard" per le maschere, che verrà poi usato come la base per tutte le forms. Attraverso l'uso di " Visual Form Inheritance", si può essere certi che ogni cambiamento operato sulla form "standard" si ripercutoerà su tutte le forms da essa derivate.
Visual Form Inheritance consente di ereditare tutto il codice, gli oggetti e le proprietà per quanti livelli si vuole senza penalizzazioni nella prestazioni a runtime. Altri sistemi che cercano di implementare l'ereditarietà subiscono gravi penalizzazioni prestazionali che rendondola inutilizzabile nel modno delle applicazioni reali.
L'applicazione di esempio \DEMOS\DB\GDSDEMO\GDSDEMO.DPR contiene un esempio di Visual Form Inheritance. L'esempio mostra due forms che vengono presentate all'utente: GridViewForm e RecViewForm che visualizzano rispettivamente una vista a Griglia e una a record di un database. Quest due forms condividono molti elementi e, infatti, derivano dallo stesso genitore, StdDataForm. StdDataForm definisce le tavole Customer e Orders tables, un DataSource, il codice per applicare un filtro sulla tavola degli Ordinie il codice per trovare il precedente/successivo ordine che soddisfa i criteri di ricerca. Poiché la maggior parte delle funzionalità sono definite in StdDataForm, solo una piccola parte di codice deve essere aggiunta nelle due forms derivate.
Grazie alla Visual Form Inheritance, ogni cambiamento effettuato su StdDataForm, sia scrivendo codice che visualmente usando l'Object Inspector o il Form Designer, si ripercuote autoamticamente ai discendenti, in questo caso GridViewForm e RecViewForm. Per esempio, muovendo il bottone Find Next button o cambiandone il codice in StdDataForm, i cambiamenti saranno immediatamente applicati anche alle forms derivate. Naturalmente è sempre possibile ricoprire le proprietà visuali o il codice nelle forms derivate.
La Visual Form Inheritance di Delphi determina cosa è ereditato e cosa ridefinito con una granularità a livello di property. Ciò significa che anche se si cambia ad esempio il Font del bottone Find Next button nella GridViewForm, questa form continuerà ad ereditare altre proprietà auli ad esempio la scritta del bottone, le dimensioni ed il codice associato all'evento OnClick.
Se poi si vogliono ripristinare le impostazioni del componente genitore, basta usare il pulsante destro del mouse sul componente stesso nella form ereditata e selezionare la voce di menù Revert to Inherited.
Se si desidera ricoprire il comportamento di un componente in una form derivata basta usare l'Object Inspector, così come con ogni altro componente per attaccare codice al gestore dell'evento desiderato. Per esempio, per ricoprire il codice associato all'evento OnClick, del bottone Find Next button della form GridViewForm, basta fare un doppio click sul bottone Find Next o sulla proprietà OnClick nell'Object Inspector. Il metodo che verrà così generato includerà una chiamata al metodo genitore come descritto di seguito.
procedure TGridViewForm.NextBtnClick(Sender: TObject);
begin
inherited;
{ Add new code here }
end;
Nota: Per una migliore manutenibilità del codice è sempre conveniente chiamare la routine ereditata. Se non esiste, la chiamata verrà ignorata.
Per creare una nuova form ereditata, si può usare l'opzione File New per accedere all'Object Repository. Si può quindi selezionare una form qualunque nella pagina dei Progetti (es. GdsDemo), delle Forms, dei Dialogs o dei Data Modules. Nota che per forms nella pagina Progetti in uso l'unica opzione consentita è Inherit, mentre per le pagine Forms, Dialogs o Data Modules sono abilitate anche Copy e Reference.
Quando di deriva una form, sia dal progetto in uso che dall'Object Repository, la classe genitore è caricata in memoria. Si può verificare leggendo la definizione della form che è derivata da una classe diversa da TForm. Ad esempio, di seguito è mostrata la definizione di GridViewForm:
type
TGridViewForm = class(TStdDataForm)
DBGrid1: TDBGrid;
procedure NextBtnEndDrag(Sender, Target: TObject; X, Y: Integer);
procedure NextBtnClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
Notare come l'unico compnente dichiarato è DBGrid, gli altri essendo dichiarati in TStdDataForm, come mostrato di seguito:
TStdDataForm = class(TGDSStdForm)
StdCtrlPanel: TPanel;
FilterOnRadioGroup: TRadioGroup;
Orders: TTable;
Cust: TTable;
OrdersSource: TDataSource;
GroupBox1: TGroupBox;
FilterOnLabel: TLabel;
FilterCriteria: TEdit;
FilterCheckBox: TCheckBox;
NextBtn: TButton;
PriorBtn: TButton;
...
procedure FilterOnRadioGroupClick(Sender: TObject);
procedure OrdersCalcFields(DataSet: TDataSet);
procedure FilterCheckBoxClick(Sender: TObject);
procedure PriorBtnClick(Sender: TObject);
procedure NextBtnClick(Sender: TObject);
procedure FilterCriteriaExit(Sender: TObject);
procedure FilterCriteriaKeyPress(Sender: TObject; var Key: Char);
protected
FLastAmount: Double;
FLastDate: TDateTime;
function CalcAmountDue: Double;
procedure ConvertFilterCriteria;
end;
Allo stesso modo, usando il pulsante destro del mouse sulla GridViewForm nel form designer and selezionando View as Text, è possibile vedere esattamente quali proprietà sono cambiate rispetto al genitore. l'esempio mostra la rappresentazione testuale della GridViewForm in cui si sono operate delle modifche al bottone Find Next (cambiati Font e locazione):
inherited GridViewForm: TGridViewForm
Caption = 'Grid View'
inherited StdCtrlPanel: TPanel
inherited NextBtn: TButton
Left = 223
Top = 13
Font.Height = -13
Font.Style = [fsBold]
ParentFont = False
end
end
object DBGrid1: TDBGrid [2]
Left = 0
Top = 161
Width = 460
Height = 159
Align = alClient
DataSource = OrdersSource
TabOrder = 2
TitleFont.Color = clBlack
TitleFont.Height = -11
TitleFont.Name = 'MS Sans Serif'
TitleFont.Style = []
end
end
La Visual Form Inheritance è realizzata verificando le impostazioni delle proprietà componente per componente dal progenitore a tutti i discendenti e confrontando le differenze. Chi scrive componenti dovrebbero esporre solo le proprietà necessarie cosicché l'analisi delle differenze effettuata dalla Visual Form possa essere massimamente efficace. Alcuni componenti usano una grande granularità nel determinare quali proprietà sono cambiate nei discendenti. Ad es. il controllo Database Grid control usa la proprietà Collection per registrare gli attributi delle colonne. Perció cambiando proprietà quali Collection in una form derivata, comporta che l'intera Collection sia considerata sovrascritta. Perciò cambiando un attributo a livello di colonna in un discendente, altri cambiamenti operati sugli attributi di colonna non si propagheranno alla form derivata (si lavora sulla stessa proprietà). Questo non compromette ovviamente la propagazione di cambiamenti in altre proprietà.
Il meccanismo di Visual Form Inheritance induce su sistemi lenti piccoli rallentamenti nel ridisegno di forms derivate da molti livelli. Questo eventuale calo di prestazioni si verifica solamente a design time, quando Delphi deve calcolare le differenze tra genitore e derivati. In fase di compilazione, queste informaziuni divengono statiche e non c'è quindi impatto sulle prestazioni. Sia la form genitore che la derivata a molti livelli si comportano identicamente a runtime.
Delphi compila immediatamente generando codice ottimizzato, al contrario dei sistemi 4GL che talvolta generano codice C che deve essere a sua volta compilato con un compilatore C separato. Sebbene questo possa sembare un buon modo per eliminare le gravi carenze prestazionali dei sistemi 4GL, Delphi ha dimostrato che esiste un modo più efficiente e affidabile per sviluppare applicazioni generando direttamente il codice ottimizzato. i vantaggi di questo approccio sono evidenti:
Storicamente si è dimostrato che i sistemi P-code e generatori di codice si sono fermati di fronte al gap delle misure.
Similarmente, molte delle prime implementazioni del C++ sono stae create come mere traslazioni da C++ a C. Ma anche qui la strada è stata presto abbandonata a causa delle difficoltà nell'effettuare il debugging e alla lentezza del processo di compilazione a due stadi.
L'obbiettivo con delphi 2.0 è stato di offrire totale compatibilità col codice 16-bit. Poche modifiche, o nessuna, sono necessarie oltre alla ricompilazione. Nella maggior parte dei casi, gli sviluppatori possono semplicemente caricare il codice nel nuovo ambiente, compilare il codice 32-bit ed eventualmente aggiungere i nuovi aspetto del 32-bit. in qualche caso può essere necessaria la conversione di codice, ma solo quando sono state fatte assunzioni che dipendono dalla piattaforma del Sistema: tipicamente la migrazione dei tipi di dato da 16 a 32 bit. Delphi gestisce autoamticamente i cambiamenti dei tipi nei messaggi Windows (message cracking), evitando quindi cambiamenti nella gestione dei messaggi che sono stati modificati o ridimensionati a 32 bit. Il Codice che dipende dalla rappresentazione fisica e che quindi necessita di aggiornamento include:
In Delphi 2.0, il tipo Integer diviene a 32 bits, rispetto ai 16 di Delphi 1.x esiste comunque SmallInt per compatibilità
Il nuovo compilatore 32-bit ha inoltre la capcità di "unit aliasing" che consente di usare un nome simbolico differente (o alias) per una unit. Questa è una tecnica vantaggiosa quando si desidera cambiare profondamente l'implementazione di una unit per utilizzare i vantaggi del mondo 32-bit. Ad esempio, in Delphi 1.x esistevano due units separate: WinTypes e WinProcs. Queste units sono state combinate in una singola unit chiamata Windows che contiene tutti in nuovi tipi, funzioni, messaggi di Windows 32-bit. Per compatibilità sono definiti gli alias che puntano a questa unit evitando così cambiamenti nella sezione "uses". perciò la riga:
Uses WinTypes, WinProcs;
è cambiata per essere capita come: Uses Windows;
Con Delphi 2.0 viene distribuito anche Delphi 1.0 /16-bit) per le applicazioni che non devono utilizzare la piattaforma 32-bit. Tuttavia se vengono usate caratteristiche del mondo 32-bit, le applicazioni mon possono essere compilate col 16-bit, a meno di modifiche.
Delphi 2.0 è un prodotto completamente nuovo costruito dalle fondamenta del compilatore ottimizzato per trarre il massimo vantaggio offerto dal mondo 32-bit di Windows 95 e NT. Sono presenti molte nuove ottimizzazioni del compilatore, del linker, e nuovi tipi di dato a 32-bit, cosicché il gap tra Delphi e gli interpreti p-code si allarga ulteriormente a favore di Delphi. Inoltre la nuova architettura del compilatore rende più semplice creare codice corretto ed inoltre offre l'accesso a caratteristiche fondamentali quali il multi-threading, OLE automation e OLE controls (OCX).
Delphi 2.0 inoltre include molti nuovi componenti per usufruire dei nuovi elementi dell'interfaccia Windows 95, nuovi componenti per la gestione dei database, la versione 32-bit del Borland Database Engine, così come molti nuove utilities che facilitano lo sviluppo client/server ed un set più esteso delle Open Tools API per consentire una maggior integrazione con i pacchetti terze-parti quali controllo di versione e CASE tools.
Su questi argomenti verranno pubblicate altre relazioni..
© 1996 Giovanni Bracci, mc2447@mclink.it
Ultima revisione: 5 mar 1996.