Passare un`area riservata di memoria ad una funzione esterna
Considereremo due casi: il caso in cui si debba passare ad una funzione semplicemente un'area allocata di memoria, affinché quella la riempi; ed il caso in cui, dopo aver allocato un'area di memoria, si debba riempirla, ed infine così passarla ad una funzione.
Passare semplicemente un'area allocata di memoria
Qualora una funzione esterna preveda che le si passi una Struttura, o comunque un'altra area allocata di memoria, per mezzo di un puntatore a quest'area riservata, affinché questa venga riempita dalla funzione medesima magari con due o più valori, che possono essere anche di tipo diverso; e che successivamente si debba procedere alla dereferenziazione, procederemo nel modo seguente:
1) allocheremo una sufficiente quantità di memoria, che sarà puntata da una variabile di tipo puntatore;
2) passeremo tale puntatore alla funzione esterna che provvederà a riempirla;
3) procederemo alla dereferenziazione. Se il valore restituito dalla funzione esterna è unico, ossia il suo indirizzo coincide con la struttura (o classe) stessa, e dunque o coincide con la parte iniziale dell'area, allora potranno essere utilizzate le consuete funzioni di dereferenziazione (Byte@(), Int@(), etc). Se invece I valori passati dalla funzione esterna sono più di uno, essi occuperanno in modo coerente con il tipo di valore, rappresentato da ciascuno, l'area di memoria da noi riservata e puntata dalla variabile puntatore, e andranno dereferenziati mediante l'uso dei Memory-Stream.
Vediamo un esempio:
Private p As Pointer ' Dichiariamo la funzione esterna dalla quale riceveremo il puntatore alla "Struttura" esterna: Private Extern funzione_esterna(pEst As Pointer) Public Sub Button1_Click() Dim ms As Stream Dim by As Byte ' Ci riserviamo un'area sufficiente di memoria: p = Alloc(quantità_di memoria_da_allocare) ' Passiamo alla funzione esterna il puntatore all'area allocata, ' affinché essa gli assegni il valore dell'indirizzo di memoria puntato: funzione_esterna(p) ' Andiamo a dereferenziare il pointer: ms = Memory p For Read ' Andiamo a leggere e mostrare in console i dati: While Not Eof(ms) Read #pS, by Print by Wend End Public Sub Form_Close() ' In fase di chiusura del programma, si procederà anche a "de-allocare", ' cioè a liberare, la porzione di memoria precedentemente allocata: Free(p) End
Riempire un'area allocata di memoria e successivamente passarla ad una funzione
Talvolta il codice può richiedere di passare ad una funzione esterna un'area riservata di memoria debitamente riempita con tipi di valori che occupino ciascuno un ben determinato numero di byte all'interno di detta area riservata. Una volta riempita, come previsto, si passerà detta area riservata alla funzione esterna.
In Gambas 3 la procedura è complessa, nel senso che intervengono vari elementi e fasi. Nela prima fase si alloca un'area di memoria che sarà puntata da una variabile di tipo Puntatore. Per poter scrivere, o leggere, all'interno di quest'area riservata è necessario utilizzare una variabile di tipo Stream che punti a detta area riservata. Tale variabile di tipo Stream verrà generata mediante la funzione Memory Stream sulla base della già esistente variabile di tipo Puntatore, creata precedentemente all'atto dell'allocazione di un'area di memoria. Lo Stream è uno stratagemma, uno strumento di Gambas per lavorare con la memoria. Usando la variabile di tipo Stream non viene creata un'altra area di memoria, si usa comunque la stessa area dichiarata prima con la variabile di tipo puntatore. Lo Stream è soltanto un meccanismo per operare su quell'area là precedentemente allocata e puntata dalla variabile di tipo Puntatore. Quindi la variabile di tipo Stream va vista come una risorsa collaterale, che serve soltanto per scrivere o leggere i dati dentro la variabile di tipo Puntatore all'area allocata, ma che deve rimanere tutta interna a Gambas, e non deve essere passata alle funzioni esterne. Insomma, possiamo dire che solo Gambas sa che cosa sono le variabili Stream, ed alle funzioni esterne - appartenenti all'API di altri programmi e risorse - si devono passare soltanto dei Puntatori ad aree di memoria. Nel qual caso si posseggono sia l'area di memoria (allocata, esistente e preparata a dovere) sia la variabile di tipo Puntatore relativo (che deve puntare all'inizio).
Riepilogando, procederemo generalmente come segue:
1) si punterà all'area riservata di memoria con una variabile di tipo puntatore;
2) si genererà una variabile di tipo Stream dalla predetta variabile di tipo Puntatore mediante la funzione Memory Stream (For Write);
3) si riempirà l'area con valori del tipo coerente a quelli richesti;
4) si passerà, però, alla funzione comunque la variabile di tipo Puntatore (e non quella di tipo Stream !) che punta e rappresenta l'area di memoria allocata.
Come esempio pratico poniamo il caso di voler riportare in Gambas il seguente codice scritto in C, nel quale la funzione, alla quale deve essere passata l'area riservata di memoria, preveda come parametro, appunto, che venga passata una variabile di tipo puntatore che punta a quell'area riservata:
unsigned char *data; /* Viene allocata un'area di memoria */ data = (unsigned char *)malloc(1024); /* ...qui avviene il riempimento dell'area riservata con i valori prescritti */ ...... ...... ......etc /* Quindi viene passato alla funzione il puntatore all'area riempita */ funz(data);
In Gambas avremo:
' Nel C era previsto un "Puntatore", in Gambas prevediamo egualmente e coerentemente un "Puntatore" per poter passare l'area allocata riempita !
Private Extern funzioneEsterna(param As Pointer)
Public Sub Button1_Click()
Dim dati As Pointer
Dim datStr As Stream
Dim v1 As ...' tipo di valore in base a quello previsto nel codice C
Dim v2 As ...' tipo di valore in base a quello previsto nel codice C
...etc...
dati = Alloc(1024)
' Dal puntatore creiamo una variabile di tipo "Stream" in "scrittura":
datStr = Memory dati For Write
' Scriviamo nella variabile di tipo Stream:
Write #datStr, v1 As ...' tipo di valore in base a quello previsto nel codice C
Write #datStr, v2 As ...' tipo di valore in base a quello previsto nel codice C
...etc...
' Passiamo infine alla funzione esterna comunque la variabile di tipo "Puntatore",
' precedentemente creata e che punta all'area riservata di memoria, ora riempita con i previsti valori:
funzioneEsterna(dati)
End
Utilizzo di un vettore
In alternativa alla creazione di un'apposita area riservata puntata da una variabile di tipo Puntatore, è possibile come previsto dal C utilizzare proprio un vettore.
Pertanto, riprendendo il codice esemplificativo di sopra, avremo:
Private Extern funzioneEsterna(param As Byte[]) Public Sub Button1_Click() Dim dati As New Byte[1024] ' Il vettore in questo esempio è composto da 1024 elementi. Dim i as Short ' Riempiamo l'intero vettore di dati di tipo Byte (1 byte) in questo esempio in modo casuale: For i = 0 To (1024) - 1 dati[i] = CByte(Rnd(0, 255)) Next ' E passiamo il vettore alla funzione esterna: funzioneEsterna(dati) End