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

Da Gambas-it.org - Wikipedia.
 
(17 versioni intermedie di uno stesso utente non sono mostrate)
Riga 1: Riga 1:
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.
+
==Caso in cui è possibile richiamare direttamente le funzioni esterne in Gambas==
 +
Nei casi di librerie, scritte in C++, più semplici, è possibile richiamare direttamente le funzioni in esse presenti.
 +
<BR>Il sorgente della libreria sarà compilato con il comando "g++", ma avendo accortezza di dare alla futura libreria esterna l'estensione ".so", proprio come se fosse una libreria scritta in 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( )'' .
+
Esempio pratico.
 
+
Poniamo il caso di avere il seguente file sorgente scritto in C++:
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.
+
#include <cstdlib>
 
+
#include <sstream>
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.
+
  #include <string.h>
 
+
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>
+
int Funzione_1() {
Il nome fittizio, dunque, può essere un nome qualsiasi.
+
 
+
  int a;
 
+
 
+
  a = 100;
===Esempi pratici===
+
   
Mostriamo di seguito tre semplici esempi pratici.
+
  return a;
 
+
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 il cui prototipo fingiamo trovarsi in una certa libreria .h, scritta appunto in C++ e chiamata ad esempio ''xxx.h'', nonché in una corrispondente libreria dinamica .so, chiamata ad esempio ''libxxx.so'' .
 
 
 
Procediamo nel modo seguente. Poiché non possiamo invocare direttamente la funzione esterna, scritta in C++ che ci interessa, creiamo una libreria dinamica .so esterna <SPAN Style="text-decoration:underline">d'appoggio</span>, che chiameremo ad esempio ''libadhoc.cpp'':
 
  #include <xxx.h>
 
 
   
 
   
 +
char s[32];
 
   
 
   
  int <FONT Color=#B22222>main_Cpp</font>(int i, char *s[]) {
+
  char * Funzione_2() {
 
   
 
   
    int rit;
+
  strcpy(s, "testo da Funzione_2");
 
 
<FONT Color=blue>/* Invochiamo la funzione esterna che ci interessa */</font>
 
    rit = Calculus(i);
 
 
   
 
   
    return rit;
+
  return s;
 
   
 
   
 
  }
 
  }
 +
Salviamo questo codice nel file sorgente ad esempio nel seguente percorso: /tmp/prova.c
 +
<BR>Successivamente il file sorgente sarà così compilato:
 +
'''g++''' -o /tmp/prova'''.so''' /tmp/prova.c -shared -fPIC
  
 +
Per utilizzare le funzioni nel codice Gambas, dobbiamo cercare il loro esatto nome, lanciando in terminale il comando
 +
:~$ nm -D '/tmp/prova.so'
 +
oppure
 +
:~$ objdump -T '/tmp/prova.so'
 +
Troveremo le due funzioni che riportato il proprio nome un po' modificato, similmente ai seguenti:
 +
_Z10Funzione_1v
 +
_Z10Funzione_2v
 +
Pertanto nel codice Gambas saranno utilizzati questi identificatori per richiamare le due funzioni presenti nel codice sorgente scritto in C++.
  
Il codice principale del nostro applicativo Gambas ''a riga di comando'' sarà il seguente:
+
Il codice Gambas sarà ad esempio il seguente:
  Private Extern <FONT Color=#B22222>main_Cpp</font>(rit As Integer, po As Pointer) As Integer In "/tmp/libadhoc" Exec "main"
+
  Library "/tmp/prova"
 
   
 
   
 +
Private Extern _Z10Funzione_1v() As Integer
 +
Private Extern _Z10Funzione_2v() As String
 
   
 
   
'''Public''' Sub Main()
 
 
   
 
   
  Dim i As Integer
+
Public Sub Main()
 
   
 
   
  i = <FONT Color=#B22222>main_Cpp</font>(4, 0)
+
  Print _Z10Funzione_1v()
    
+
  Print i
+
   Print _Z10Funzione_2v()
 
   
 
   
  '''End'''
+
  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.
+
==Caso in cui non è possibile richiamare direttamente le funzioni esterne in Gambas==
<BR>La routine di ''main_Cpp()'', (contenuta nella nostra libreria esterna di appoggio il cui sorgente abbiamo chiamato ''libadhoc.cpp'') scritta anch'essa ovviamente in C++, fa dunque da tramite necessario, come già detto, per invocare le funzioni esterne scritte in C++ .
+
Con librerie esterne, scritte in C++, più complesse il richiamo con ''Extern'' delle funzioni esterne non avviene in Gambas in modo identico a quello delle funzioni esterne scritte in linguaggio C.
 +
 
 +
Infatti, in tali casi risulta possibile soltanto richiamare la funzione principale, quella avente nome identificatore: "main()".
 +
 
 +
Se la libreria dinamica .so contiene più funzioni, queste dovranno essere chiamate comunque attraverso la funzione principale "main()", non essendo direttamente disponibili all'utente come quelle delle librerie condivise .so scritte in C.
 +
 
 +
Ciò comporta che con ''Extern'' non è possibile utilizzare direttamente l'API di librerie esterne condivise .so, ma si dovrà creare un'apposita libreria condivisa .so contenente la parte di programma, scritto in C++, che comprende la routine principale "main()", nella quale sono poste e vengono chiamate le funzioni esterne che si intendono realmente utilizzare.
 +
 
 +
Insomma, non verrà ''direttamente'' dichiarata ed invocata la funzione esterna, che ci interessa, della libreria C++, ma lo sarà una funzione principale "main()" di un'apposita libreria condivisa esterna .so, da noi scritta in C++. All'interno di tale funzione "main()" principale si porrà e si chiamerà la funzione esterna che realmente ci interessa chiamare.
 +
 
 +
La routine di "main()" - contenuta nella nostra libreria esterna di appoggio, scritta anch'essa ovviamente in C++ - fa dunque da tramite necessario, come già detto, per invocare le funzioni esterne di librerie scritte in C++ .
  
 
Ricapitolando si profila un rapporto secondo questo schema:
 
Ricapitolando si profila un rapporto secondo questo schema:
  Gambas <--> Libreria dinamica esterna nostra di ''appoggio'' <--> libreria dinamica esterna scritta in C++
+
  Gambas <--> Nostra libreria condivisa esterna di ''appoggio'' <--> libreria condivisa esterna scritta in C++
  
  
 +
===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 condivisa .so esterna d'appoggio, ovviamente scritta in C++, nella quale è appunto contenuta la funzione esterna che a noi interessa invocare. In tal caso, quindi, ogni qual volta si debba utilizzare la funzione esterna, si provvederà ad invocare la funzione della libreria condivisa da noi scritta. La libreria esterna, da noi creata, farà nuovamente da veicolo per utilizzare la funzione esterna che ci interessa realmente.
 +
<BR>Questa soluzione ha lo svantaggio, però, di dover realizzare - come ovvio - per ciascuna funzione un'apposita libreria esterna
  
==Utilizzare il codice sorgente scritto in C++ di un programma come fosse una libreria dinamica .so==
 
Poniamo il caso di avere il codice sorgente di un semplice programma, scritto in C++ e formato ad esempio dalla sua sola routine principale, e di volerlo utilizzare mediante sua trasformazione in una libreria dinamica esterna .so.
 
  
In tal caso la routine principale diventa la funzione esterna - unica - della libreria dinamica esterna .so che andremo a creare.
+
2 - Creare, comunque, un'apposita libreria condivisa .so esterna, scritta in C++, nella quale porre tutte le funzioni che ci interessano e che dovremo richiamare ed utilizzare.
 +
<BR>Si provvederà, poi, all'interno di detta unica libreria condivisa .so 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.
  
Nell'esempio pratico che segue la funzione riceverà e restituirà per ''riferimento'' una stringa.
+
Di questa seconda modalità mostriamo un esempio pratico, nel quale abbiamo una certa libreria esterna condivisa .so, scritta in C++, contenente un paio di funzioni:
 
  #include <cstdlib>
 
  #include <cstdlib>
 
  #include <sstream>
 
  #include <sstream>
 
  #include <string.h>
 
  #include <string.h>
 
    
 
    
 
int main(int c, char *t[]) {
 
 
    int i = 0;
 
    std::string a;
 
 
    
 
    
    if (t[0] == NULL) return -1;
+
int Funzione_1() {
 
   
 
   
     a = "Aggiunta alla ";
+
    int a;
 +
   
 +
     a = 100;
 
   
 
   
     a = a + t[0];
+
     return a;
 
    *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
+
std::string Funzione_2() {
 
   
 
   
  p = Alloc("Stringa inviata")
+
    std::string s = "testo dalla";
 
   
 
   
  i = main_Cpp(0, VarPtr(p))
+
    return s;
  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.
 
<BR>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.
 
<BR>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.
 
<BR>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 main(int c, char *s[]) {
Riga 138: Riga 125:
 
   }
 
   }
 
   
 
   
  <FONT Color=gray>/* Se abbiamo passato "funzione_2", verrà invocata la "Funzione_2"
+
  <FONT Color=gray>/* Se abbiamo passato "funzione_2", verrà invocata la "Funzione_2". In questo caso viene ritornata per 'riferimento' una Stringa */</font>
    In questo caso viene ritornata per 'riferimento' una Stringa */</font>
 
 
   if (strcmp((char *)s[0], "funzione_2") == 0) {
 
   if (strcmp((char *)s[0], "funzione_2") == 0) {
 
     t = Funzione_2();
 
     t = Funzione_2();
 
   
 
   
 
  <FONT Color=gray>/* Vengono unite le stringhe di tipo base */</font>
 
  <FONT Color=gray>/* Vengono unite le stringhe di tipo base */</font>
     r =  t + " " + std::string(s[0]) + "\n" + t;
+
     r =  t + " " + std::string(s[0]) + " della libreria esterna in C++";
 
    
 
    
  <FONT Color=gray>/* La variabile base stringa "r" viene convertita nel tipo 'char*'
+
  <FONT Color=gray>/* 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()" */</font>
    affinché possa essere restituita al codice Gambas attraverso il parametro "char** s" della routine "main()" */</font>
 
 
     char* rit = strcpy((char*)malloc(r.length()+1), r.c_str());
 
     char* rit = strcpy((char*)malloc(r.length()+1), r.c_str());
 
     *s =  rit;
 
     *s =  rit;
Riga 156: Riga 141:
 
   
 
   
 
  }
 
  }
 
 
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:
 
Il semplice codice Gambas potrà essere il seguente:
 
  <FONT Color=gray>' ''int main(int c, char *s[])''
 
  <FONT Color=gray>' ''int main(int c, char *s[])''
 
  ' ''E' la routine principale della libreria dinamica condivisa .so da noi creata.''</font>
 
  ' ''E' la routine principale della libreria dinamica condivisa .so da noi creata.''</font>
  Private Extern main_cpp(rit As Integer, po As Pointer) As Integer In "/tmp/libadhoc" Exec "main"
+
  Private Extern main(rit As Integer, po As Pointer) As Integer In "/tmp/libadhoc"
 
   
 
   
 
   
 
   
  '''Public'''  Sub Main()
+
Public Sub Main()
 
   
 
   
 
   Dim p As Pointer
 
   Dim p As Pointer
 
   Dim i As Integer = -1
 
   Dim i As Integer = -1
 
   
 
   
  p = Alloc("funzione_2")    <FONT Color=gray>' ''Qui va il nome di una delle due funzioni che a noi interessa realmente utilizzare''</font>
+
<FONT Color=gray>' ''Qui va il nome di una delle due funzioni della libreria esterna in C++ che a noi interessa realmente utilizzare:''</font>
 +
  p = Alloc("funzione_2")
 
   
 
   
  i = main_cpp(0, VarPtr(p))
+
  i = main(0, VarPtr(p))
   
+
  Print "Dato ritornato:"
+
  Print "Dato ritornato:"
  If i > -1 Then  
+
  If i > -1 Then  
    Print i
+
    Print i
  Else
+
  Else
    Print String@(p)
+
    Print String@(p)
  Endif
+
  Endif
 
+
  Free(p + 8)
+
  Free(p)
 
   
 
   
  '''End'''
+
  End

Versione attuale delle 05:55, 16 nov 2023

Caso in cui è possibile richiamare direttamente le funzioni esterne in Gambas

Nei casi di librerie, scritte in C++, più semplici, è possibile richiamare direttamente le funzioni in esse presenti.
Il sorgente della libreria sarà compilato con il comando "g++", ma avendo accortezza di dare alla futura libreria esterna l'estensione ".so", proprio come se fosse una libreria scritta in C.

Esempio pratico. Poniamo il caso di avere il seguente file sorgente scritto in C++:

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


int Funzione_1() {

  int a;

  a = 100;

  return a;

}


char s[32];

char * Funzione_2() {

  strcpy(s, "testo da Funzione_2");

  return s;

}

Salviamo questo codice nel file sorgente ad esempio nel seguente percorso: /tmp/prova.c
Successivamente il file sorgente sarà così compilato:

g++ -o /tmp/prova.so /tmp/prova.c -shared -fPIC

Per utilizzare le funzioni nel codice Gambas, dobbiamo cercare il loro esatto nome, lanciando in terminale il comando

:~$ nm -D '/tmp/prova.so' 

oppure

:~$ objdump -T '/tmp/prova.so'

Troveremo le due funzioni che riportato il proprio nome un po' modificato, similmente ai seguenti:

_Z10Funzione_1v
_Z10Funzione_2v

Pertanto nel codice Gambas saranno utilizzati questi identificatori per richiamare le due funzioni presenti nel codice sorgente scritto in C++.

Il codice Gambas sarà ad esempio il seguente:

Library "/tmp/prova"

Private Extern _Z10Funzione_1v() As Integer
Private Extern _Z10Funzione_2v() As String


Public Sub Main()

 Print _Z10Funzione_1v()

 Print _Z10Funzione_2v()

End


Caso in cui non è possibile richiamare direttamente le funzioni esterne in Gambas

Con librerie esterne, scritte in C++, più complesse il richiamo con Extern delle funzioni esterne non avviene in Gambas in modo identico a quello delle funzioni esterne scritte in linguaggio C.

Infatti, in tali casi risulta possibile soltanto richiamare la funzione principale, quella avente nome identificatore: "main()".

Se la libreria dinamica .so contiene più funzioni, queste dovranno essere chiamate comunque attraverso la funzione principale "main()", non essendo direttamente disponibili all'utente come quelle delle librerie condivise .so scritte in C.

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

Insomma, non verrà direttamente dichiarata ed invocata la funzione esterna, che ci interessa, della libreria C++, ma lo sarà una funzione principale "main()" di un'apposita libreria condivisa esterna .so, da noi scritta in C++. All'interno di tale funzione "main()" principale si porrà e si chiamerà la funzione esterna che realmente ci interessa chiamare.

La routine di "main()" - contenuta nella nostra libreria esterna di appoggio, scritta anch'essa ovviamente in C++ - fa dunque da tramite necessario, come già detto, per invocare le funzioni esterne di librerie scritte in C++ .

Ricapitolando si profila un rapporto secondo questo schema:

Gambas <--> Nostra libreria condivisa esterna di appoggio <--> libreria condivisa esterna scritta in C++


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 condivisa .so esterna d'appoggio, ovviamente scritta in C++, nella quale è appunto contenuta la funzione esterna che a noi interessa invocare. In tal caso, quindi, ogni qual volta si debba utilizzare la funzione esterna, si provvederà ad invocare la funzione della libreria condivisa da noi scritta. La libreria esterna, da noi creata, farà nuovamente da veicolo per utilizzare la funzione esterna che ci interessa realmente.
Questa soluzione ha lo svantaggio, però, di dover realizzare - come ovvio - per ciascuna funzione un'apposita libreria esterna


2 - Creare, comunque, un'apposita libreria condivisa .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 condivisa .so 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 condivisa .so, scritta in C++, contenente un paio di funzioni:

#include <cstdlib>
#include <sstream>
#include <string.h>
 
 
int Funzione_1() {

   int a;
   
   a = 100;

   return a;

}


std::string Funzione_2() {

   std::string s = "testo dalla";

   return s;

}


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]) + " della libreria esterna in C++";
 
/* 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;
    
  }

}

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(rit As Integer, po As Pointer) As Integer In "/tmp/libadhoc"


Public Sub Main()

 Dim p As Pointer
 Dim i As Integer = -1

' Qui va il nome di una delle due funzioni della libreria esterna in C++ che a noi interessa realmente utilizzare:
 p = Alloc("funzione_2")

 i = main(0, VarPtr(p))

 Print "Dato ritornato:"
 If i > -1 Then 
   Print i
 Else
   Print String@(p)
 Endif

 Free(p)

End