Differenze tra le versioni di "Richiamare funzioni esterne di librerie scritte in C++"

Da Gambas-it.org - Wikipedia.
Riga 7: Riga 7:
 
Ciò comporta che con ''Extern'' non è possibile utilizzare direttamente l'API di librerie esterne dinamiche .so, ma si dovrà creare un'apposita libreria dinamica .so contenente la parte di programma, scritto in C++, che comprende la routine principale ''main()'', nella quale vengono chiamate dal suo codice le funzioni esterne che si intendono utilizzare.
 
Ciò comporta che con ''Extern'' non è possibile utilizzare direttamente l'API di librerie esterne dinamiche .so, ma si dovrà creare un'apposita libreria dinamica .so contenente la parte di programma, scritto in C++, che comprende la routine principale ''main()'', nella quale vengono chiamate dal suo codice le funzioni esterne che si intendono utilizzare.
  
Inoltre, se l'applicazione Gambas è ''a riga di comando'', bisogna prestare attenzione all'identificatore espresso nella dichiarazione con ''Extern'' della funzione principale ''main()'', presente nell'apposita libreria esterna dinamica .so da noi scritta in C++. Infatti, poiché si avrebbero due identificatori sostanzialmente identici "''main''" (quello della routine principale di Gambas e quello della routine principale dell'apposita libreria esterna scritta in C++), bisognerà attribuire nella dichiarazione con ''Extern'' un nome ''fittizio'', specificando in fine riga, però, con la parola ''Exec'' il nome reale identificatore (ossia ''main'') della funzione principale:
+
Inoltre, <SPAN Style="text-decoration:underline">se l'applicazione Gambas è ''a riga di comando''</span>, bisogna prestare attenzione all'identificatore espresso nella dichiarazione con ''Extern'' della funzione principale ''main()'', presente nell'apposita libreria esterna dinamica .so da noi scritta in C++. Infatti, poiché si avrebbero due identificatori sostanzialmente identici "''main''" (quello della routine principale di Gambas e quello della routine principale dell'apposita libreria esterna scritta in C++), bisognerà attribuire nella dichiarazione con ''Extern'' un nome ''fittizio'', specificando in fine riga, però, con la parola ''Exec'' il nome reale identificatore (ossia ''main'') della funzione principale:
 
  Private Extern <FONT Color=B22222>Nome_Fittizio</font>(i As Integer, s As String) As Integer in "''/percorso/dell'apposita/libreria/esterna" <FONT Color=B22222>Exec "main"</font>
 
  Private Extern <FONT Color=B22222>Nome_Fittizio</font>(i As Integer, s As String) As Integer in "''/percorso/dell'apposita/libreria/esterna" <FONT Color=B22222>Exec "main"</font>
 
Il nome fittizio, dunque, può essere un nome qualsiasi.
 
Il nome fittizio, dunque, può essere un nome qualsiasi.

Versione delle 03:06, 12 ott 2016

Il richiamo con Extern di funzioni esterne presenti in librerie dinamiche .so, scritte in C++, non avviene in Gambas in modo identico a quello delle funzioni esterne scritte in linguaggio C.

Infatti, mentre con le librerie dinamiche .so scritte in C è possibile invocare da Gambas una qualsiasi funzione esterna avente un qualsiasi nome identificatore, con le librerie dinamiche .so scritte in C++ risulta possibile soltanto richiamare la funzione principale, quella avente nome identificatore: main( ) .

Se la libreria dinamica .so contiene comunque più funzioni, queste dovranno essere chiamate comunque attraverso la funzione principale main(), sia per il motivo sopra esposto, sia perché esse non sono direttamente disponibili all'utente come quelle delle librerie dinamiche .so scritte in C.

Ciò comporta che con Extern non è possibile utilizzare direttamente l'API di librerie esterne dinamiche .so, ma si dovrà creare un'apposita libreria dinamica .so contenente la parte di programma, scritto in C++, che comprende la routine principale main(), nella quale vengono chiamate dal suo codice le funzioni esterne che si intendono utilizzare.

Inoltre, se l'applicazione Gambas è a riga di comando, bisogna prestare attenzione all'identificatore espresso nella dichiarazione con Extern della funzione principale main(), presente nell'apposita libreria esterna dinamica .so da noi scritta in C++. Infatti, poiché si avrebbero due identificatori sostanzialmente identici "main" (quello della routine principale di Gambas e quello della routine principale dell'apposita libreria esterna scritta in C++), bisognerà attribuire nella dichiarazione con Extern un nome fittizio, specificando in fine riga, però, con la parola Exec il nome reale identificatore (ossia main) della funzione principale:

Private Extern Nome_Fittizio(i As Integer, s As String) As Integer in "/percorso/dell'apposita/libreria/esterna" Exec "main"

Il nome fittizio, dunque, può essere un nome qualsiasi.


Esempi pratici

Mostriamo di seguito tre semplici esempi pratici.

In tali esempi, ovviamente, si dovrà preventivamente provvedere a compilare i sorgenti delle previste librerie dinamiche .so esterne.


Caso in cui la libreria da noi creata ritorna un Intero

Poniamo il caso che la funzione in C++ da richiamare sia

int Calculus(int n)

che fingiamo trovarsi in una certa libreria .h, chiamata ad esempio xxx.h, ed anche in una corrispondente libreria dinamica .so, chiamata ad esempio libxxx.so .

Procediamo nel modo seguente. Creiamo un'apposita libreria dinamica .so esterna, che chiameremo ad esempio libadhoc.cpp:

#include <libxxx.h>


int main(int i, char *s[]) {

   int rit;
 
/* Invochiamo la funzione esterna che ci interessa */
   rit = Calculus(i);

   return rit;

}


Il codice principale del nostro applicativo Gambas a riga di comando sarà il seguente:

Private Extern main_Cpp(rit As Integer, po As Pointer) As Integer In "/tmp/libadhoc" Exec "main"


Public Sub Main()

 Dim i As Integer

  i = main_Cpp(4, 0)
 
  Print i

End

Come abbiamo visto, non verrà direttamente dichiarata ed invocata la funzione esterna, della libreria C++, che ci interessa, ma lo sarà una funzione principale main() posta in un'apposita libreria dinamica esterna .so, da noi scritta in C++. All'interno di tale funzione main() principale si chiamerà la vera funzione esterna che ci interessa.
La routine di main(), scritta anch'essa ovviamente in C++, fa dunque da tramite necessario, come già detto, per invocare le funzioni esterne scritte in C++ .


Caso in cui viene inviata una stringa alla libreria, da noi creata, e quindi viene da essa ritornata una nuova stringa

In questo esempio, invece, poniamo il caso di avere un'unica funzione esterna, rappresentata dalla routine principale main() del codice C++, la quale riceverà e restituirà per riferimento una stringa.

Anche in questo esempio, ovviamente, tale funzione esterna viene inserita all'interno di un'apposita libreria dinamica .so (formata dunque da questa sola routine principale), che chiameremo ad esempio libadhoc.cpp:

#include <cstdlib>
#include <sstream>
#include <string.h>
 

int main(int c, char *t[]) {

   int i = 0;
   std::string a;
 
   if (t[0] == NULL) return -1;

   a = "Aggiunta alla ";

   a = a + t[0];

   *t = strcpy((char*)malloc(a.length()+1), a.c_str());

   return i;

}


Il codice principale del nostro applicativo Gambas a riga di comando sarà il seguente:

Private Extern main_Cpp(rit As Integer, po As Pointer) As Integer In "/tmp/libadhoc" Exec "main"

 
Public  Sub Main()

 Dim p As Pointer

  p = Alloc("Stringa inviata")

  i = main_Cpp(0, VarPtr(p))
  If i = -1 Then Error.Raise("Errore !")

  Print "Stringa ritornata:  "; String@(p)

End


Caso in cui si debbano utilizzare più funzioni esterne

Nel caso in cui sia necessario utilizzare più funzioni esterne di una libreria scritta in C++, si possono adottare almeno un paio di soluzioni.

1 - Creare per ciascuna funzione un'apposita libreria dinamica .so esterna, ovviamente scritta in C++,nella quale è appunto contenuta la funzione esterna che a noi interessa. In tal caso, quindi, ogni qual volta si debba utilizzare la funzione esterna, si provvederà ad invocare la funzione della libreria dinamica da noi scritta. La libreria esterna da noi creata farà semplicemente da veicolo per utilizzare la reale funzione esterna che ci interessa.
Questa soluzione ha lo svantaggio, però, di dover realizzare - come ovvio - per ciascuna funzione un'apposita libreria esterna


2 - Creare, comunque, un'apposita libreria dinamica .so esterna, scritta in C++, nella quale porre tutte le funzioni che ci interessano e che dovremo richiamare ed utilizzare.
Si provvederà, poi, all'interno di detta unica libreria dinamica a distinguere l'uso di una o di altra funzione mediante l'invio di valori stringa o numerici univoci che saranno valutati da istruzioni condizionali, le quali consentiranno o meno di accedere ad una determinata funzione tra quelle contenute.
Di questa seconda modalità mostriamo un esempio pratico, nel quale abbiamo una certa libreria esterna .so, scritta in C++, contenente un paio di funzioni:

#include <cstdlib>
#include <sstream>
#include <string.h>
 

int main(int c, char *s[]) {

  int ritorno;
  std::string t, r;

  
/* Se abbiamo passato "funzione_1", verrà invocata la "Funzione_1" */
  if (strcmp((char *)s[0], "funzione_1") == 0) {
/* In questo caso viene ritornato un Intero */
    ritorno = Funzione_1();
    return ritorno;
  }

/* Se abbiamo passato "funzione_2", verrà invocata la "Funzione_2"
   In questo caso viene ritornata per 'riferimento' una Stringa */
  if (strcmp((char *)s[0], "funzione_2") == 0) {
    t = Funzione_2();

/* Vengono unite le stringhe di tipo base */
    r =  t + " " + std::string(s[0]) + "\n" + t;
 
/* La variabile base stringa "r" viene convertita nel tipo 'char*'
   affinché possa essere restituita al codice Gambas attraverso il parametro "char** s" della routine "main()" */
    char* rit = strcpy((char*)malloc(r.length()+1), r.c_str());
    *s =  rit;
    
    return -1;
    
  }

}


int Funzione_1() {

   int a;

   a = 100;

   return a;

}


std::string Funzione_2() {

   std::string s = "testo";

   return s;

}


Il semplice codice Gambas potrà essere il seguente:

' int main(int c, char *s[])
' E' la routine principale della libreria dinamica condivisa .so da noi creata.
Private Extern main_cpp(rit As Integer, po As Pointer) As Integer In "/tmp/libadhoc" Exec "main"


 Public  Sub Main()

 Dim p As Pointer
 Dim i As Integer = -1

  p = Alloc("funzione_2")    ' Qui va il nome di una delle due funzioni che a noi interessa realmente utilizzare

  i = main_cpp(0, VarPtr(p))
   
  Print "Dato ritornato:"
  If i > -1 Then 
    Print i
  Else
    Print String@(p)
  Endif
  
  Free(p + 8)

End