web-dev-qa-db-fra.com

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

Ceci est une suite de La compilation de la bibliothèque partagée dynamique avec g ++ .

J'essaie de créer une bibliothèque de classes partagée en C++ sous Linux. Je suis en mesure de faire compiler la bibliothèque et d’appeler certaines des fonctions (non-class) à l’aide des tutoriels que j’ai trouvés ici et ici . Mes problèmes commencent lorsque j'essaie d'utiliser les classes définies dans la bibliothèque. Le deuxième tutoriel que j'ai lié montre comment charger les symboles pour la création d'objets des classes définies dans la bibliothèque, mais s'arrête avant en utilisant ces objets pour effectuer le travail.

Est-ce que quelqu'un connaît un didacticiel plus complet sur la création de bibliothèques de classes C++ partagées qui montre également comment utiliser ces classes dans un exécutable séparé? Un tutoriel très simple qui montre la création d’objets, son utilisation (des accesseurs simples et des setters serait très bien) et sa suppression serait fantastique. Un lien ou une référence à un code open source illustrant l'utilisation d'une bibliothèque de classes partagée serait également utile.


Bien que les réponses de codelogic et nimrodm ne fonctionnent pas, je voulais juste ajouter que j’ai pris une copie de Beginning Linux Programming depuis que je pose cette question, et son premier chapitre contient un exemple de code C et de bonnes explications pour la création et l'utilisation de bibliothèques statiques et partagées. Ces exemples sont disponibles via Google Recherche de Livres dans une édition antérieure de ce livre .

147
Bill the Lizard

myclass.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

myclass.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éfinissez toutes les fonctions requises virtuelles. Le plugin auteur dériverait alors de MyClass, remplacerait les virtuals et implémenterait create_object et destroy_object. Votre application principale n'aurait besoin d'aucune modification.

137
codelogic

Vous trouverez ci-dessous un exemple de bibliothèque de classes partagée partagée. [H, cpp] et un module main.cpp utilisant cette bibliothèque. C'est un exemple très simple et le makefile pourrait être 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 lie main avec 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 lier 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 pour les 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)
49
nimrodm

Fondamentalement, vous devez inclure le fichier d'en-tête de la classe dans le code où vous souhaitez utiliser la classe dans la bibliothèque partagée. Ensuite, lorsque vous créez un lien, utilisez l'indicateur '-l' pour lier votre code à la bibliothèque partagée. Bien entendu, cela nécessite que le fichier .so soit là où le système d'exploitation peut le trouver. Voir 3.5. Installer et utiliser une bibliothèque partagée

L'utilisation de dlsym est utile lorsque vous ne savez pas au moment de la compilation quelle bibliothèque vous souhaitez utiliser. Cela ne semble pas être le cas ici. La confusion est peut-être que Windows appelle les bibliothèques chargées dynamiquement, que vous fassiez la liaison lors de la compilation ou de l'exécution (avec des méthodes analogues)? Si c'est le cas, dlsym est l'équivalent de LoadLibrary.

Si vous avez réellement besoin de charger dynamiquement les bibliothèques (c’est-à-dire des plug-ins), alors this FAQ devrait vous aider.

8
Matt Lewis

En plus des réponses précédentes, j'aimerais attirer votre attention sur le fait que vous devez utiliser l'idiome RAII (l'initialisation d'une ressource est une 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 les fichiers d'en-tête pour plus de concision. Dans la vraie vie, vous devez bien sûr scinder votre code entre les fichiers .hpp et .cpp.
  • Pour simplifier, j'ai ignoré le cas où vous souhaitez gérer une surcharge new/delete.

Deux articles clairs pour plus de détails:

0
Xavier Lamorlette