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:
ausprobierenLD_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