C++ Librerie dinamiche

Da Andreabont's Wiki.

Compilare una libreria dinamica

Una libreria dinamica deve avere un nome preceduto dal prefisso "lib" ed avente estensione ".so".

g++ -shared -fPIC -o <libmialibreria.so> <file_sorgente.h> <file_sorgente.cpp>

Utilizzo di codice C

C++ e C utilizzano sistemi diversi per le chiamate a funzione, tra loro incompatibili. Se si vuole usare del codice C usando C++ è necessario utilizzare il seguente costrutto:

extern "C" {
   // Prototipi della libreria
}

Linking della libreria a compile-time

Può essere comodo linkare una libreria dinamica a compile-time, in modo che sia automaticamente cariata al lancio del programma, in questo caso è necessario avere a disposizione il file header della libreria. Se la libreria è stata compilata in C, controllare che il file header abbia dichiarato il costrutto extern "C".

Sorgente

#include <iostream>
#include "file_sorgente.h"

using namespace std;

int main() {
   cout << funzione_esterna() << endl;
}

Compilazione

Per compilare il programma chiamante è necessario passargli il nome della libreria senza prefisso ed estensione. L'opzione "-L." indica al compilatore di cercare la libreria nella directory corrente. L'opzione, non obbligatoria, "-Wl,-rpath ." indica al linker di cercare la libreria dinamica nella directory corrente durante l'esecuzione. Se non specificato la libreria verrà cercata nei path standard della distribuzione in uso.

g++ <sorgente_chiamante.cpp> -L. -lmialibreria [-Wl,-rpath .]

Linking della libreria a run-time

Il caricamento a run-time si aspetta un funzione in stile C, se viene compilata con C++ ricordarsi di usare il costrutto extern "C". (E' possibile in realtà chiamare direttamente funzioni C++, a condizione di indicare direttamente il nome assegnato alla funzione dal compilatore, vedi name mangling)

Sorgente

#include<iostream>
#include<dlfcn.h>

using namespace std;

int main()
{
    void *handle;
    handle = dlopen("./libmialibreria.so", RTLD_NOW);
    if (handle == NULL)
    {
        cerr << "Errore apertura: " << dlerror() << endl;
    }

    int (*quadrato)(int) = reinterpret_cast<int (*)(int)>( dlsym(handle,"quadrato") );
    if (quadrato == NULL)
    {
        cout << "Errore import: " << dlerror() << endl;
    }

    cout << (*quadrato)(2) << endl;

    return 0 ;
}

Compilazione

g++ <sorgente_chiamante.cpp> -ldl

Istanziare una classe di una libreria a run-time

Dato che per aprire una libreria condivisa a runtime si deve passare da C, per istanziare una classe dichiarata nella libreria è necessario usare un "trucco" wrappando il costruttore.

Codice della libreria

libclasstest.h

#ifndef LIBCLASSTEST_H
#define LIBCLASSTEST_H

// Classe virtuale, serve al chiamante per farsi un idea di cosa deve chiamare.
class MyClass {
  
public:
  virtual void hello()=0;
  
};

#endif

libclasstest.cpp

#include <iostream>
#include "libclasstest.h"

// Classe reale, implemento quella virtuale
class MyClassImpl : public MyClass {
  
public:
  
  MyClassImpl() {
    std::cout << "Creata MyClass!" << std::endl;
  }
  
  void hello() {
    std::cout << "Ciao Mondo!" << std::endl;
  }
  
};

// Sezione old-C, per chiamata diretta.
extern "C" {
  
  MyClass* newMyClass() {
    std::cout << "Chiamato wrapper!" << std::endl;
    return (MyClass*)(new MyClassImpl);
  }
  
}

Compilare la libreria

g++ -shared -fPIC -o libclasstest.so libclasstest.cpp

Sorgente del chiamante

#include "libclasstest.h"
#include<iostream>
#include<dlfcn.h>
 
using namespace std;
 
int main()
{
    void *handle;
    handle = dlopen("./libclasstest.so", RTLD_NOW);
    if (handle == NULL)
    {
        cerr << "Errore apertura: " << dlerror() << endl;
    }
 
    MyClass* (*creaClasse)(void) = reinterpret_cast<MyClass* (*)(void)>( dlsym(handle,"newMyClass") );
    if (creaClasse == NULL)
    {
        cout << "Errore import: " << dlerror() << endl;
    }
    
    MyClass* obj = creaClasse();
    
    obj->hello();
  
    return 0 ;
}

Compilare il chiamante

g++ chiamante.cpp -ldl