GNU/Linux >> LINUX-Kenntnisse >  >> Linux

C++ Dynamic Shared Library unter Linux

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

meineklasse.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 Plugin-System wäre, würden Sie MyClass als Basisklasse verwenden und alle erforderlichen Funktionen virtuell definieren. Der Plugin-Autor würde dann von MyClass ableiten, die virtuellen Elemente überschreiben und create_object implementieren und destroy_object . Ihre Hauptanwendung muss in keiner Weise geändert werden.


Das folgende Beispiel zeigt eine gemeinsam genutzte Klassenbibliothek shared.[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 Ihnen 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' tatsächlich auszuführen und mit libshared.so zu verknüpfen, müssen Sie wahrscheinlich den Ladepfad angeben (oder ihn in /usr/local/lib oder ähnliches einfügen).

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

export LD_LIBRARY_PATH=.
./main

Um zu sehen, ob das Programm mit libshared.so verlinkt ist, können Sie ldd:

ausprobieren
LD_LIBRARY_PATH=. ldd main

Druckt auf meinem Gerät:

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

Zusätzlich zu den vorherigen Antworten möchte ich das Bewusstsein dafür schärfen, dass Sie die Redewendung RAII (Resource Acquisition Is Initialization) verwenden sollten, um sich vor der Zerstörung von Handlern zu schützen.

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

Handler für dynamische gemeinsam genutzte Bibliotheken: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:

  • Der Kürze halber habe ich alles in Header-Dateien geschrieben. Im wirklichen Leben sollten Sie Ihren Code natürlich auf .hpp aufteilen und .cpp Dateien.
  • Der Einfachheit halber habe ich den Fall ignoriert, in dem Sie mit new umgehen möchten /delete Überlastung.

Zwei klare Artikel, um weitere Details zu erhalten:

  • C++ dlopen Mini-Anleitung
  • Dynamisches Laden von gemeinsam genutzten Objekten in C++ zur Laufzeit

Linux
  1. So installieren Sie die Ncurses-Bibliothek unter Linux

  2. Einführung in gemeinsam genutzte Linux-Bibliotheken (So erstellen Sie gemeinsam genutzte Bibliotheken)

  3. Dynamische Shared Library-Kompilierung mit g++

  4. Konvertieren Sie eine statische Bibliothek in eine gemeinsam genutzte Bibliothek?

  5. So initialisieren Sie eine gemeinsam genutzte Bibliothek unter Linux

Ein Leitfaden zum Verständnis von Linux-Softwarebibliotheken in C

Erkennung der Abhängigkeit von dynamischen Bibliotheken unter Mac OS und Linux

Strippen von gemeinsam genutzten Linux-Bibliotheken

Wo lege ich Bibliotheken von Drittanbietern ab, um eine C++-Linux-Entwicklungsumgebung einzurichten?

Cmake unter Windows fügt keine gemeinsam genutzten Bibliothekspfade hinzu (funktioniert unter Linux)

Verwenden Sie eine C-Bibliothek in Swift unter Linux