GNU/Linux >> Tutoriels Linux >  >> Linux

Bibliothèque partagée dynamique C++ sous Linux

maclasse.h

#ifndef __MYCLASS_H__
#define __MYCLASS_H__

class MyClass
{
public:
  MyClass();

  /* use virtual otherwise linker will try to perform static linkage */
  virtual void DoSomething();

private:
  int x;
};

#endif

maclasse.cc

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

using namespace std;

extern "C" MyClass* create_object()
{
  return new MyClass;
}

extern "C" void destroy_object( MyClass* object )
{
  delete object;
}

MyClass::MyClass()
{
  x = 20;
}

void MyClass::DoSomething()
{
  cout<<x<<endl;
}

class_user.cc

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

using namespace std;

int main(int argc, char **argv)
{
  /* on Linux, use "./myclass.so" */
  void* handle = dlopen("myclass.so", RTLD_LAZY);

  MyClass* (*create)();
  void (*destroy)(MyClass*);

  create = (MyClass* (*)())dlsym(handle, "create_object");
  destroy = (void (*)(MyClass*))dlsym(handle, "destroy_object");

  MyClass* myClass = (MyClass*)create();
  myClass->DoSomething();
  destroy( myClass );
}

Sous Mac OS X, compilez avec :

g++ -dynamiclib -flat_namespace myclass.cc -o myclass.so
g++ class_user.cc -o class_user

Sous Linux, compilez avec :

g++ -fPIC -shared myclass.cc -o myclass.so
g++ class_user.cc -ldl -o class_user

S'il s'agissait d'un système de plug-in, vous utiliseriez MyClass comme classe de base et définiriez toutes les fonctions requises virtuellement. L'auteur du plugin dériverait alors de MyClass, remplacerait les virtuels et implémenterait create_object et destroy_object . Votre application principale ne devrait en aucun cas être modifiée.


L'exemple suivant montre une bibliothèque de classes partagées shared.[h,cpp] et un module main.cpp utilisant la bibliothèque. C'est un exemple très simple et le makefile pourrait être bien amélioré. Mais cela fonctionne et peut vous aider :

shared.h définit la classe :

class myclass {
   int myx;

  public:

    myclass() { myx=0; }
    void setx(int newx);
    int  getx();
};

shared.cpp définit les fonctions getx/setx :

#include "shared.h"

void myclass::setx(int newx) { myx = newx; }
int  myclass::getx() { return myx; }

main.cpp utilise la classe,

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

using namespace std;

int main(int argc, char *argv[])
{
  myclass m;

  cout << m.getx() << endl;
  m.setx(10);
  cout << m.getx() << endl;
}

et le makefile qui génère libshared.so et relie main à la bibliothèque partagée :

main: libshared.so main.o
    $(CXX) -o main  main.o -L. -lshared

libshared.so: shared.cpp
    $(CXX) -fPIC -c shared.cpp -o shared.o
    $(CXX) -shared  -Wl,-soname,libshared.so -o libshared.so shared.o

clean:
    $rm *.o *.so

Pour exécuter réellement 'main' et établir un lien avec libshared.so, vous devrez probablement spécifier le chemin de chargement (ou le mettre dans /usr/local/lib ou similaire).

Ce qui suit spécifie le répertoire actuel comme chemin de recherche des bibliothèques et exécute main (syntaxe bash) :

export LD_LIBRARY_PATH=.
./main

Pour voir que le programme est lié à libshared.so, vous pouvez essayer ldd :

LD_LIBRARY_PATH=. ldd main

Imprime sur ma machine :

  ~/prj/test/shared$ LD_LIBRARY_PATH=. ldd main
    linux-gate.so.1 =>  (0xb7f88000)
    libshared.so => ./libshared.so (0xb7f85000)
    libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0xb7e74000)
    libm.so.6 => /lib/libm.so.6 (0xb7e4e000)
    libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0xb7e41000)
    libc.so.6 => /lib/libc.so.6 (0xb7cfa000)
    /lib/ld-linux.so.2 (0xb7f89000)

En plus des réponses précédentes, j'aimerais attirer l'attention sur le fait que vous devez utiliser l'idiome RAII (Resource Acquisition Is Initialisation) pour être sûr de la destruction du gestionnaire.

Voici un exemple de travail complet :

Déclaration d'interface :Interface.hpp :

class Base {
public:
    virtual ~Base() {}
    virtual void foo() const = 0;
};

using Base_creator_t = Base *(*)();

Contenu de la bibliothèque partagée :

#include "Interface.hpp"

class Derived: public Base {
public:
    void foo() const override {}
};

extern "C" {
Base * create() {
    return new Derived;
}
}

Gestionnaire de bibliothèque partagée dynamique :Derived_factory.hpp :

#include "Interface.hpp"
#include <dlfcn.h>

class Derived_factory {
public:
    Derived_factory() {
        handler = dlopen("libderived.so", RTLD_NOW);
        if (! handler) {
            throw std::runtime_error(dlerror());
        }
        Reset_dlerror();
        creator = reinterpret_cast<Base_creator_t>(dlsym(handler, "create"));
        Check_dlerror();
    }

    std::unique_ptr<Base> create() const {
        return std::unique_ptr<Base>(creator());
    }

    ~Derived_factory() {
        if (handler) {
            dlclose(handler);
        }
    }

private:
    void * handler = nullptr;
    Base_creator_t creator = nullptr;

    static void Reset_dlerror() {
        dlerror();
    }

    static void Check_dlerror() {
        const char * dlsym_error = dlerror();
        if (dlsym_error) {
            throw std::runtime_error(dlsym_error);
        }
    }
};

Code client :

#include "Derived_factory.hpp"

{
    Derived_factory factory;
    std::unique_ptr<Base> base = factory.create();
    base->foo();
}

Remarque :

  • J'ai tout mis dans des fichiers d'en-tête pour plus de concision. Dans la vraie vie, vous devriez bien sûr diviser votre code entre .hpp et .cpp fichiers.
  • Pour simplifier, j'ai ignoré le cas où vous voulez gérer un new /delete surcharge.

Deux articles clairs pour obtenir plus de détails :

  • Mini tutoriel C++ dlopen
  • Chargement dynamique C++ d'objets partagés lors de l'exécution

Linux
  1. Un guide pour comprendre les bibliothèques de logiciels Linux en C

  2. Convertir une bibliothèque statique en bibliothèque partagée ?

  3. Comment compiler une bibliothèque dynamique pour une application JNI sous Linux ?

  4. Comment afficher la liste des fonctions qu'une bibliothèque partagée Linux exporte ?

  5. Détecter Windows ou Linux en C, C++

Comment initialiser une bibliothèque partagée sous Linux

Suppression des bibliothèques partagées Linux

Où puis-je placer des bibliothèques tierces pour configurer un environnement de développement C++ Linux ?

Comment faire le versioning d'une bibliothèque partagée sous Linux ?

Comment configurer googleTest en tant que bibliothèque partagée sous Linux

Comment faire un délai de bibliothèque partagée chargé sur Linux