Callback tra newLisp e Lazarus Pascal/Free Pascal/Delphi (prima parte)

29 07 2008

google_translate.gif Translate To English!

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:

  1. Trovare un modo per poter chiamare, da newLisp, una funzione/procedura (o metodo di una classe) realizzato in Free Pascal / Lazarus (callback).
  2. 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:

  1. 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).
  2. 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:

  1. Creare la procedura (callback) che dovrà essere richiamata da newLisp
  2. Aprire la DLL newLisp
  3. Passare a newLisp l’indirizzo della nostra procedura
  4. 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!)) 8) 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"
La stringa “ld” indica di formattare il numero 12345 come un signed 32-bit long number (numero a 32 bit con segno).
La funzione dump estrae delle informazioni specifiche riguardo una cella newLisp. Per i dettagli sui dati ritornati potete consultare il manuale.
In pratica il codice newLisp non fà altro che…
  1. Allocare un nuovo simbolo in memoria (helloPascal!),
  2. “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).
  3. Fà in modo che il simbolo helloPascal! punti alla procedura Pascal, e non alla cella originale (la quale verrà “riciclata” automaticamente dal garbage collector).
  4. Il newLisp successivamente, chiamando la funzione helloPascal!, non farà più riferimento al simbolo originale, ma alla procedura pascal! ;-)
Per scrivere codice più ordinato, usato il “tag” <PASCAL_CALLBACK> per indicare dove verrà scritto l’indirizzo della procedura pascal. Se volete, potete salvare il codice precedente in un file (chiamandolo, per esempio, init.lsp).
Per sostituire il tag precedente con il vero indirizzo della procedura pascal, potete procedere come segue:
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!





Delphi/Lazarus Free Pascal atto secondo!

22 07 2008

google_translate.gif Translate To English!

Sto preparando un articolo per spiegare come realizzare funzioni callback tra Delphi/Lazarus Free Pascal e newLisp (la DLL).

Pubblicherò l’articolo nei prossimi giorni, restate “sintonizzati”!!!





Sondaggio: editor e newLisp

1 07 2008

google_translate.gif Translate To English!

Questa volta non pubblicherò novità o informazioni varie, ma vorrei una opinione da parte di tutti (questo è un mini-sondaggio).

Anche se esistono diversi files per abilitare l’highlight della sintassi newLisp in diversi editors, non esiste però un editor in grado di accettare macro fatte in newLisp (un pò come fà, per esempio, emacs, con il suo eLisp).

Che ne pensate di realizzare un editor simile? Avrebbe molte delle funzionalità dei più blasonati editors, sarebbe cross-platform, e potrebbe dare la possibilità di scrivere macro in newLisp.

Mi piacerebbe leggere le vostre opinioni!