Differenze tra le versioni di "Extern: richiamare funzioni esterne a Gambas"

Da Gambas-it.org - Wikipedia.
Riga 95: Riga 95:
 
Qualora si debba utilizzare una funzione esterna avente il nome identico ad una funzione propria di Gambas, è necessario formalmente usare un nome diverso indicando, però, il vero nome della funzione esterna preceduto dal comando ''Exec''.
 
Qualora si debba utilizzare una funzione esterna avente il nome identico ad una funzione propria di Gambas, è necessario formalmente usare un nome diverso indicando, però, il vero nome della funzione esterna preceduto dal comando ''Exec''.
  
Nel seguente esempio pratico dobbiamo dichiarare ed usare la funzione ''Kill'' presente nella libreria esterna ''libc:6'':
+
Nel seguente esempio pratico dobbiamo dichiarare ed usare la funzione ''kill'' presente nella libreria esterna ''libc:6'':
 
  int kill(pid_t pid, int sig);
 
  int kill(pid_t pid, int sig);
 
Il nome della funzione esterna ''kill'', però, è - come si nota - identico alla funzione ''Kill'' propria di Gambas. Pertanto, assegneremo alla funzione esterna un nome qualsiasi, purché diverso da quello di ogni altra funzione di Gambas, ma richiameremo il suo vero nome all'atto della dichiarazione della funzione esterna, facendolo precedere dal comando ''Exec'':
 
Il nome della funzione esterna ''kill'', però, è - come si nota - identico alla funzione ''Kill'' propria di Gambas. Pertanto, assegneremo alla funzione esterna un nome qualsiasi, purché diverso da quello di ogni altra funzione di Gambas, ma richiameremo il suo vero nome all'atto della dichiarazione della funzione esterna, facendolo precedere dal comando ''Exec'':

Versione delle 13:33, 26 ago 2015

Introduzione

Gambas consente di utilizzare potenzialità e capacità di sistemi esterni mediante il richiamo di loro funzioni. A volte infatti può capitare nella programmazione la necessità di utilizzare potenzialità che Gambas da solo non può offrire |1|. L'istruzione che Gambas pone a disposizione del programmatore per richiamare tali funzioni esterne è: Extern.

Per il richiamo di funzioni esterne è necessario conoscere in particolare tre elementi:

  • il quid, ossia il cosa fare, cosa ottenere;
  • la funzione esterna che ci consente di realizzare quel quid;
  • la libreria nella quale è contenuta la funzione da richiamare.

Se quel che si vuole ottenere è possibile solamente attraverso l'uso di una funzione esterna a Gambas, bisognerà individuare la specifica funzione che consente di realizzare il nostro obiettivo. La funzione esterna, utile allo scopo del programma, dovrà essere richiamata dal nostro programma Gambas. Poiché tale funzione esterna è scritta in C, bisognerà interpretare il significato e tradurla in forma comprensibile da Gambas. Inoltre, poiché essa è appunto esterna a Gambas, bisognerà conoscere il luogo ove essa si trova. Il luogo che contiene la funzione esterna è la "Libreria"; ed anzi una specifica libreria, la quale, pertanto, andrà dichiarata in anticipo rispetto alla funzione. Le librerie, contenenti funzioni esterne a Gambas, richiamabili da Gambas, sono quelle con estensione .so, ossia le "Librerie dinamiche, condivise" |2|. E' opportuno, quando possibile, indicare anche il numero della versione della Libreria. Inoltre, se la libreria si trova in una cartella diversa da quella dedicata alle librerie (/user/lib/), sarà necessario specificare anche il percorso.

Possiamo, dunque, dire che è un po' come se Extern annunciasse: « Farò in modo che il programma utilizzi questa funzione "xxxx(yyy)", la quale non appartiene alle risorse di Gambas, ma si trova in questa libreria: zzzz.so (oppure già precedentemente dichiarata). ».


Identificazione e dichiarazione della Libreria contenente la Funzione esterna

La Libreria, contenente la funzione esterna da richiamare, può essere dichiarata separatamente, e prima della dichiarazione Extern, mediante la parola Library:

Library "libreria_esterna:num_vers"

Extern......

La Libreria, però, può anche essere dichiarata all'interno dell'istruzione di Extern mediante la parola In:

Extern ................. In "libreria_esterna:num_vers"

Se il numero della versione della Libreria da dichiarare è complesso, esso andrà scritto così come riportato nel file. La parte .so. va sostituita con il carattere dei due punti ( : ).
Facciamo l'esempio della Libreria libsane.so.1.0.23. Essa sarà scritta così:

Library "libsane:1.0.23"

Così ugualmente se la dichiareremo all'interno dell'istruzione di Extern. Ad esempio, per chiamare la funzione sane_init() scriveremo:

Private Extern sane_init() In "libsane:1.0.23"

Per evitare che la versione della Libreria richiamata non coincida con la versione effettivamente presente in un sistema operativo, si può porre anche solo il primo numero della versione. In tal caso sarà caricata comunque la versione più recente che comincia per il numero indicato.

Quindi, invece di scrivere per esempio:

Library "libsane:1.0.23"

si scriverà semplicemente:

Library "libsane:1"

Al fine, poi, di non incorrere in un errore nel richiamare la corretta versione della Libreria, è possibile evitare ogni riferimento al numero di versione, indicando così soltanto il puro nome della Libreria medesima:

Library "libsane"


Dichiarazione della Funzione esterna mediante Extern

La Funzione esterna, che intendiamo utilizzare, deve essere dichiarata mediante l'istruzione Extern. Questa dichiarazione va effettuata esternamente alle sub-procedure (routine).

La dichiarazione deve contenere: 1) il nome della funzione esterna da utilizzare; 2) la specificazione degli argomenti della funzione esterna (espressi ovviamente secondo le modalità del linguaggio Gambas); 3) l'eventuale valore ritornato dalla funzione esterna.

La dichiarazione di una Funzione esterna può essere "Publica" o "Privata".

Una Funzione esterna dichiarata in C in questo modo:

int nome_funzione(int valore1, char valore2)

in Gambas verrà dichiarata con Extern nella seguente maniera:

Private Extern nome_funzione(valore1 As Integer, valore2 As Byte) As Integer


Ordine delle dichiarazioni nel codice del programma

La funzione vera e propria, da richiamare per il suo utilizzo, sarà posta all'interno di una routine. Dunque, nel caso di dichiarazione separata della Libreria, la scaletta delle dichiarazioni sarà la seguente:
1) dichiarazione della Libreria contenente la funzione;
2) dichiarazione mediante Extern della funzione esterna che si andrà ad utilizzare (questa dichiarazione avverrà al di fuori e prima della routine che farà uso di quella funzione);
3) chiamata ed uso della funzione esterna.

Esempio astratto:

' Dichiariamo la Libreria contenente la funzione esterna che ci interessa:
   Library "libreria_esterna:num_vers"


' Ora dichiariamo la funzione esterna specifica che ci interessa.
' Ipotizziamo che in C sia così scritta: int funzione_esterna_da_richiamare(const char * valoreStringa, int valoreInteger).
' La funzione esterna, dunque, richiede che le siano passati due valori: uno è una Stringa, l'altro è un Integer. Essa, poi, restituirà un valore Integer.
' In Gambas la dichiameremo così:
   Private Extern funzione_esterna(valoreA As String, ValoreB As Integer) As Integer

Public Sub Button1_Click()

Dim primoValore As String
Dim secondoValore As Integer
Dim rit_funz As Integer

' Invochiamo la funzione esterna che ci interessa, e le passiamo i due valori, affinché li elabori.
' Essa ritornerà un valore di tipo Intero.
  rit_funz = funzione_esterna(primoValore, secondoValore)

' Vediamo in console il valore ritornato dalla funzione esterna:
  Print rit_funz

End


Uso del nome di una funzione già utilizzato da Gambas

Qualora si debba utilizzare una funzione esterna avente il nome identico ad una funzione propria di Gambas, è necessario formalmente usare un nome diverso indicando, però, il vero nome della funzione esterna preceduto dal comando Exec.

Nel seguente esempio pratico dobbiamo dichiarare ed usare la funzione kill presente nella libreria esterna libc:6:

int kill(pid_t pid, int sig);

Il nome della funzione esterna kill, però, è - come si nota - identico alla funzione Kill propria di Gambas. Pertanto, assegneremo alla funzione esterna un nome qualsiasi, purché diverso da quello di ogni altra funzione di Gambas, ma richiameremo il suo vero nome all'atto della dichiarazione della funzione esterna, facendolo precedere dal comando Exec:

Private Extern kill_C(pid AS Integer, sig AS Integer) AS Integer In "libc:6" Exec "kill"

Analogamente se è stato utilizzato il comando Library per dichiarare la libreria esterna da utilizzare:

Library "libc:6"

Private Extern kill_C(pid AS Integer, sig AS Integer) AS Integer Exec "kill"


Uso di funzioni esterne di più Librerie

Può accadere la necessità di utilizzare le funzioni esterne di due o più Librerie. In tal caso si potranno dichiarare tranquillamente le necessarie librerie esterne all'inizio della Classe sia con la parola chiave "Library", e comunque prima della dichiarazione con Extern, sia con "In":

Library "libreria_1:num_vers"

Private Extern funzione_esterna_della_libreria_1(......)


Library "libreria_2:num_vers"

Private Extern funzione_esterna_della_libreria_2(......)


' Quindi di seguito le varie sub-routine della Classe
......
......


V'è da evidenziare, però, che, qualora le librerie esterne vengano dichiarate con la parola chiave "Library" non all'inizio della Classe, ma all'interno fra una sub-routine e l'altra, la dichiarazione di una Libreria influenzerà ogni dichiarazione con Extern successiva. Cosicché, in questo caso, bisognerà dichiarare la Libreria corrispondente prima di Extern e della routine contenente la funzione da utilizzare:

Library "libreria_1:num_vers"

Private Extern funzione_esterna_della_libreria_1(valoreA As ..., ValoreB As ...) As ...

Public Sub prima_routine_esempio(Aval As ..., Bval As ....)
  ......' qui l'uso della funzione esterna della libreria_1
End


Library "libreria_2:num_vers"

Private Extern funzione_esterna_della_libreria_2(valoreC As ..., ValoreD As ...) As ...</font>

Public Sub seconda_routine_esempio(Cval As ..., Dval As ...)
  ......' qui l'uso della funzione esterna della libreria_2
End

Qualora nell'esempio precedente fosse necessario richiamare, per utilizzare, una funzione esterna presente nella 1^ Libreria, si dovrà dichiarare nuovamente la 1^ Libreria.


Esempi pratici

Mostriamo appresso un esempio pratico utilizzando una funzione esterna di ALSA |3|.

Facendo ancora riferimento ai tre punti esposti all'inizio, avremo:

1) Il quid: vogliamo far sì che il nostro applicativo si connetta con un client di Alsa;

2) per ottenere questo risultato sappiamo che è necessario richiamare una specifica, particolare funzione esterna di ALSA:

int snd_seq_connect_to(snd_seq_t *seq, int my_port, int dest_client, int dest_port)

Questa funzione va così interpretata:
- int : vuol dire che ritorna un valore integer;
- snd_seq_connect_to : è la funzione in sé;
- (snd_seq_t * seq, int my_port, int dest_client, int dest_port) : sono i parametri della funzione. In questo caso: un Pointer (definito dal simbolo *) e tre valori Integer (definiti da ciascun int);

In Gambas questa funzione esterna sarà, dunque, così tradotta:

snd_seq_connect_to(primoValore As Pointer, secondoValore As Integer, terzoValore As Integer, quartoValore As Integer) As Integer

quest'ultimo "As Integer" (esterno ai parametri specifici della funzione esterna) è ovviamente giustificato dal fatto che detta funzione restituisce un valore integer;

3) il luogo ove questa funzione è contenuta: questa funzione è presente nella Libreria contenente le funzioni di ALSA: libasound.so.2 .

Il tutto, allora, sarebbe così da impostarsi:

' Dichiarazione della Libreria contenente la funzione esterna che si intende utilizzare:
Library "libasound:2"

....

' Extern dichiara la funzione esterna che si intende richiamare ed utilizzare (lasciamo per comodità qui i nomi: primoValore, etc, precedentemente usati):
Private Extern snd_seq_connect_to(primoValore As Pointer, secondoValore As Integer, terzoValore As Integer, quartoValore As Integer) As Integer

' In questo esempio la routine riceve da altra routine i valori da passare alla funzione esterna:
Public Sub nomequalsiasi(primoVal As Pointer, secondoVal As Integer, terzoVal As Integer, quartoVal As Integer)

  Dim err As Integer

' viene praticamente richiamata ed utilizzata la funzione esterna, e le si passano i quattro valori da essa richiesti:
    err = snd_seq_connect_to(primoVal, secondoVal, terzoVal, quartoVal)

......

End




Altro esempio pratico:

' Dichiarazione della Libreria contenente le funzioni esterne da utilizzare
' (in questo esempio la libreria delle funzioni matematiche):
Library "libm:6"

' Dichiarazione mediante Extern delle funzioni esterne da utilizzare:
Private Extern modf(param As Float, pp As Pointer) As Float
Private Extern fmod(param1 As Float, param2 As Float) As Float

Public Sub Button1_Click()

Dim pp As Pointer = Alloc(SizeOf(gb.Float))

' Uso delle funzioni esterne, e ne vediamo in console il risultato:
   Print modf(5.123456, pp)
   Print fmod(5.654321, 2)

End


Funzione esterna che fa riferimento ad una funzione interna Callback

Alcune funzioni esterne possono avere nei propri parametri riferimenti ad altre funzioni interne al codice dell'applicativo usate come callback (o gestore dell'evento) per gestire eventi asincroni. Sostanzialmente viene detto alla libreria quando far accedere un evento, e soprattutto come, che nella funzione esterna viene espresso appunto dalla funzione callback. Dunque viene detto alla libreria, una volta sola e prima di tutto, come fare a raggiungere il gestore dell'evento. Quando è il momento, la libreria invoca la funzione interna callback, passandogli eventualmente dei valori come argomenti. Tali argomenti, dunque, sono valori che vengono semplicemente passati alla funzione interna callback.
Le funzioni di callback, definite così perché vengono attivate non dal programmatore ma dal codice interno, sono ideali per i casi in cui un'attività viene eseguita ripetutamente.


Nella funzione esterna la funzione callback viene espressa semplicemente indicando il suo nome. Ad esempio potremmo avere un funzione esterna scritta in C:

int funzione_esterna_callback(int param1, funzione_callback, int param3)


In Gambas all'atto della dichiarazione della funzione esterna con Extern la funzione callback viene dichiarata, quale argomento della predetta funzione esterna, come variabile di tipo Puntatore. Essa potrà assumere un nome qualsiasi, dato che in questa fase esso non è fondamentale.
Pertanto, la funzione esterna dell'esempio precedente in Gambas sarà così espressa:

Private Extern funzione_esterna_callback(param1 As Integer, funzione_callback As Pointer, param3 As Integer) As Integer


Esempio astratto

Poniamo che la funzione espressa in C sia quella indicata appena sopra. In Gambas avremo:

Private Extern funzione_esterna_callback(param1 As Integer, funzione_callback As Pointer, param3 As Integer) As Integer

Public Sub esempio()

 Dim rit, val1, val2 As Integer

 
' Viene invocata la funzione esterna, la quale a sua volta richiama la funzione interna "callback":
  rit = funzione_esterna_callback(val1, funzione_interna_callback, val2)

End


Private Function funzione_interna_callback(argom As Integer)

' Qui il codice specifico della funzione interna "callback"
  ......

End


Note

[1] Prendiamo come esempio il rapporto fra Gambas ed il sistema sonoro ALSA. Se non si avesse la possibilità di richiamare, e quindi di utilizzare le funzioni proprie di ALSA, non sarebbe possibile interloquire con questo sistema esterno. Non sarebbe possibile gestirne le capacità, e dunque utilizzare le sue funzioni e potenzialità. L'istruzione Extern ci consente di superare questo muro, questo limite.

[2] Una libreria condivisa (Shared Library) è un file binario contenente un insieme di funzioni richiamabili o classi. Esse hanno un nome particolare, detto soname, composto dalle lettere iniziali lib, dal nome specifico della libreria, da .so, da un punto e dal numero della versione.

[3] Gli applicativi per la gestione del Midi, presenti nella pagina "Progetti degli utenti" del Forum di questo portale Gambas-it.org, abbondano di richiami alle funzioni esterne delle API di ALSA. Anche il programma RPscan, anch'esso scaricabile nella pagina "Progetti degli utenti" del Forum, presenta all'interno di una delle sue classi numerose chiamate a funzioni esterne.