Come promesso sono qui per illustrarvi come integrare al meglio la DLL newLisp ed il pascal, in particolare parleremo di Free Pascal e Lazarus Pascal, ma con poche modifiche potremo adattare gli esempi anche per funzionare in Delphi.
La scelta di usare FreePascal/Lazarus e non Delphi è molto semplice: i primi sono Open Source, inoltre sono multipiattaforma… proprio come newLisp!
Ma ora entriamo nel vivo dell’azione!
Analizziamo le varie possibilità
Il nostro scopo è duplice:
- Trovare un modo per poter chiamare, da newLisp, una funzione/procedura (o metodo di una classe) realizzato in Free Pascal / Lazarus (callback).
- Analizzare i vari sistemi che abbiamo a disposizione per scambiare i dati in modo efficiente tra newLisp e Free Pascal / Lazarus (FreePascal <–> newLisp). Questo punto lo vedremo nella prossima puntata!
Creare una callback
Usare newLisp come DLL è molto efficiente, ed inoltre, come abbiamo già detto in questo articolo, possiamo utilizzare un ambiente “persistente”, cioè che non perde gli script, valori delle variabili, contesti, etc… tra una chiamata e l’altra.
Il problema però è che, finora, potevamo chiamare funzioni newLisp dal pascal, ma non potevamo chiamare procedure/funzioni pascal da newLisp. Quest’ultima attività è molto importante per diversi motivi, tra cui:
- Realizzare procedure pascal MOLTO più performanti delle omonime procedure newLisp. Infatti newLisp, anche se molto veloce, è pure sempre un linguaggio interpretato (ma ci offre un’alta flessibilità!), e quindi più lento rispetto ad un linguaggio compilato (che però è molto meno flessibile, elastico).
- Chiamare da pascal una funzione newLisp, la quale, ad un certo momento, dovrà chiamare una funzione pascal che svolgerà delle attività specifiche (questa è una callback).
Per realizzare una funzione callback si deve passare a newLisp, in qualche modo, l’indirizzo della procedura pascal da chiamare. Il codice per registrare in newLisp questa procedura ce lo ha fornito Lutz Mueller (il creatore di newLisp!), mentre il resto è un “semplice” esercizio di informatica!
I passi da svolgere sono i seguenti:
- Creare la procedura (callback) che dovrà essere richiamata da newLisp
- Aprire la DLL newLisp
- Passare a newLisp l’indirizzo della nostra procedura
- Chiamare una funzione newLisp che, a sua volta, chiamerà la nostra procedura!
La procedura indicata nel passo (1), nel nostro esempio, si limiterà a mostrare un messaggio a video (showMessage), e si chiamerà helloPascal():
procedure helloPascal; stdcall;
begin
showMessage('Hello Pascal!');
end;
La procedura ha il suffisso stdcall. Esso indica a Windows (per Linux si deve usare cdecl) il modo in cui vengono passati i parametri. Poichè Windows è scritto in C e C++ è ovvio che esso segua le direttive imposte dal linguaggio C:
- I parametri passati alla procedura (nel nostro caso non ci sono) vengono quindi caricati nello stack da destra a sinistra;
- Il nome della procedura viene preceduto da un underscore “_”
- Viene fornita la dimensione, in bytes, dei parametri passati.
Per registrare la procedura suddetta, useremo useremo il seguente codice newLisp (quello scritto dal mitico Lutz):
(set 'helloPascal! __dummy__) (cpymem (pack "ld" 265) (first (dump helloPascal!)) 4) (cpymem (pack "ld" <PASCAL_CALLBACK>) (+ (first (dump helloPascal!)) 12) 4) (cpymem (pack "ld" "helloPascal!") (+ (first (dump helloPascal!))4)
Il codice non è proprio banale, ma cercherò di illustrarvi cosa fà (almeno i punti essenziali, al resto ci pensa il manuale d’uso di newLisp
).
La funzione cpymem permette di copiare aree di memoria, oppure di copiare un elemento newLisp (ad esempio una stringa) in un’area di memoria.
La funzione pack è usata “impacchetta” una o più espressioni in un formato binario specifico. Per esempio, se un indirizzo 32 bit è una sequenza binaria di 4 bytes, posso creare tale indirizzo come segue:
> (pack "ld" 12345) "900000"
- Allocare un nuovo simbolo in memoria (helloPascal!),
- “converte” il simbolo in un reference ad una funzione del tipo stdcall (il numero 265 indica una stdcall, mentre se usate Linux, dovrete sostituire tale valore con il numero 264, che indica l’uso di una cdecl).
- Fà in modo che il simbolo helloPascal! punti alla procedura Pascal, e non alla cella originale (la quale verrà “riciclata” automaticamente dal garbage collector).
- Il newLisp successivamente, chiamando la funzione helloPascal!, non farà più riferimento al simbolo originale, ma alla procedura pascal!
procedure registerNewLispProcedure;
var
initLisp: TStringList;
begin
initLisp := TStringList.create;
try
initLisp.LoadFromFile('init.lsp');
initLisp.Text := AnsiReplaceStr(initLisp.Text, '<PASCAL_CALLBACK>', IntToStr(integer(@helloPascal)));
finally
initLisp.free;
end;
newLispEvalStr(pchar(initLisp.text));
end;
Questa procedura sostituisce il tag <PASCAL_CALLBACK> con la stringa che rappresenta l’indirizzo di memoria in cui è allocata la procedura helloPascal. Vi ricordo che l’indirizzo della procedura è prelevato usando la chiocciolina ( @helloPascal ).
Siamo alla fine di questo workshop… allora non ci resta che provare il tutto!
Scriviamo questa piccola funzione newLisp:
(define (sayHelloToPascal) (helloPascal!) (println "Procedura chiamata!") )
A questo punto non dovete far altro che invocare tale funzione dal pascal:
showMessage(newLispEvalStr(pchar('(sayHelloToPascal)')));
Non appena invocherete il precedente codice vedrete lo showMessage Hello Pascal! sullo schermo.
Per questa volta abbiamo concluso. La prossima volta vedremo vari metodi per scambiare dati tra newLisp ed il pascal.
A presto!
Translate To English!
Commenti Recenti