it-swarm.com.de

Dynamische gemeinsam genutzte C++ - Bibliothek unter Linux

Dies ist eine Nachbearbeitung von Dynamic Shared Library mit g ++ .

Ich versuche, eine gemeinsame Klassenbibliothek in C++ unter Linux zu erstellen. Ich kann die Bibliothek zum Kompilieren bringen, und ich kann einige der (nicht klassenbezogenen) Funktionen mithilfe der Tutorials aufrufen, die ich hier und hier gefunden habe. Meine Probleme beginnen, wenn ich versuche, die in der Bibliothek definierten Klassen zu verwenden. Das zweite Tutorial, mit dem ich verlinkt habe, zeigt, wie die Symbole zum Erstellen von Objekten der in der Bibliothek definierten Klassen geladen werden. Es endet jedoch mit die Verwendung der Objekte, um jegliche Arbeit zu erledigen.

Kennt jemand ein umfassenderes Tutorial zum Erstellen von gemeinsam genutzten C++ - Klassenbibliotheken, das auch zeigt, wie diese Klassen in einer separaten ausführbaren Datei use verwendet werden? Ein sehr einfaches Tutorial, das die Objekterstellung und -nutzung zeigt (einfache Getter und Setter wären in Ordnung), und das Löschen wäre fantastisch. Ein Link oder ein Verweis auf Open Source-Code, der die Verwendung einer gemeinsam genutzten Klassenbibliothek veranschaulicht, wäre gleichermaßen gut.


Obwohl die Antworten von codelogic und nimrodm funktionieren, wollte ich nur hinzufügen, dass ich eine Kopie von Beginn der Linux-Programmierung da diese Frage gestellt habe, und im ersten Kapitel C-Code enthält und gute Erklärungen zum Erstellen und Verwenden von statischen und gemeinsam genutzten Bibliotheken. Diese Beispiele sind über die Google Buchsuche in einer älteren Ausgabe dieses Buches verfügbar.

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 );
}

Kompilieren Sie unter Mac OS X mit:

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

Kompilieren Sie unter Linux mit:

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

Wenn dies für ein Plug-In-System wäre, würden Sie MyClass als Basisklasse verwenden und alle erforderlichen Funktionen als virtuell definieren. Der Plugin-Autor würde dann von MyClass abgeleitet, die virtuellen überschreiben und create_object und destroy_object implementieren. Ihre Hauptanwendung müsste in keiner Weise geändert werden.

137
codelogic

Das folgende Beispiel zeigt ein Beispiel für eine gemeinsam genutzte Klassenbibliothek. [H, cpp] und ein main.cpp-Modul, das die Bibliothek verwendet. Es ist ein sehr einfaches Beispiel und das Makefile könnte viel besser gemacht werden. Aber es funktioniert und kann dir helfen:

shared.h definiert die Klasse:

class myclass {
   int myx;

  public:

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

shared.cpp definiert die getx/setx-Funktionen:

#include "shared.h"

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

main.cpp verwendet die Klasse,

#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;
}

und das Makefile, das libshared.so generiert und main mit der gemeinsam genutzten Bibliothek verknüpft:

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

Um 'main' auszuführen und mit libshared.so zu verlinken, müssen Sie wahrscheinlich den Ladepfad angeben (oder ihn in/usr/local/lib oder ähnliches ablegen).

Folgendes gibt das aktuelle Verzeichnis als Suchpfad für Bibliotheken an und führt main (Bash-Syntax) aus:

export LD_LIBRARY_PATH=.
./main

Um zu sehen, dass das Programm mit libshared.so verknüpft ist, können Sie ldd ausprobieren:

LD_LIBRARY_PATH=. ldd main

Drucke auf meiner Maschine:

  ~/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

Grundsätzlich sollten Sie die Header-Datei der Klasse in den Code einfügen, in dem Sie die Klasse in der gemeinsam genutzten Bibliothek verwenden möchten. Wenn Sie eine Verknüpfung herstellen, verwenden Sie das Kennzeichen '-l' , um Ihren Code mit der gemeinsam genutzten Bibliothek zu verknüpfen. Natürlich muss das .so dort sein, wo das Betriebssystem es finden kann. Siehe 3.5. Installieren und Verwenden einer gemeinsam genutzten Bibliothek

Die Verwendung von dlsym ist, wenn Sie zur Kompilierzeit nicht wissen, welche Bibliothek Sie verwenden möchten. Das hört sich hier nicht so an. Möglicherweise ist die Verwirrung, dass Windows die dynamisch geladenen Bibliotheken aufruft, unabhängig davon, ob Sie die Verknüpfung beim Kompilieren oder zur Laufzeit (mit analogen Methoden) ausführen. Wenn ja, dann können Sie sich dlsym als Entsprechung von LoadLibrary vorstellen.

Wenn Sie die Bibliotheken wirklich dynamisch laden müssen (d. H. Sie sind Plug-Ins), sollten this FAQ helfen.

8
Matt Lewis

Zusätzlich zu den vorherigen Antworten möchte ich das Bewusstsein dafür schärfen, dass Sie das RAII (Resource Acquisition Is Initialization) - Idiom verwenden sollten , um die Zerstörung der Handler zu vermeiden.

Hier ist ein vollständiges Arbeitsbeispiel:

Schnittstellendeklaration: Interface.hpp:

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

using Base_creator_t = Base *(*)();

Inhalt der gemeinsam genutzten Bibliothek:

#include "Interface.hpp"

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

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

Dynamischer Shared Library-Handler: 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);
        }
    }
};

Kundencode:

#include "Derived_factory.hpp"

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

Hinweis:

  • Ich habe alles in Header-Dateien für die Prägnanz gespeichert. Im wirklichen Leben sollten Sie Ihren Code natürlich in .hpp- und .cpp-Dateien aufteilen.
  • Um es zu vereinfachen, habe ich den Fall ignoriert, in dem Sie eine new/delete Überladung behandeln wollen.

Zwei klare Artikel für mehr Details:

0