Ich benutze auch einige Features von C++11, deswegen GCC 4.7 (ob es auch mit
4.4-4.6 geht habe ich nicht getestet).
+Kompilieren
+-------------
+
+Im Hauptverzeichnis einfach qmake && make aufrufen. Egal ob qmake von Qt 4 oder 5.
+
+Dann einfach bin/main starten.
+
+Vorsicht: make -j ... kann schiefgehen, da ich mir nicht die Mühe gemacht habe alle
+Abhängigkeiten explizit zu formulieren.
+
+Repo-Struktur
+---------------
+
+example.pro -> Verlinkt nur den Rest, ist schuld an fehlenden Abhängigkeiten
+
+main -> GUI und Lib-Loader, thread-Beispiel
+
+base -> Basisbibliothek, davon sind die eigentlichen Libs abgeleitet
+
+dylib[12] -> die ausgelagerten dynamisch geladenenn Bibliotheken
+
Deutsch nix gutt!
--------
+------------------
Alles was weiter als nur über Deine eigenen 4 Wände verbreitet werden soll,
sollte in Englisch kommentiert sein. Ich kenne inzwischen die Panik die
Dynamische Lib
---------------
+Es ist möglich auf vielen Unixoiden Systemen und Linux ein Plugin zu bauen
+welches Symbole des Programms benutzt. In den Standardeinstellungen fügt qmake
+aber nicht die passenden Argumente ein und ich habe mir auch nicht die Mühe
+gemacht das selbst zu tun. Grund: auf vielen Systemen geht es nicht! Zum Beispiel
+unter Windows muss eine DLL zur Compile-Zeit alle benötigten Symbole auflösen können
+und der Linker wird sich weigern eine EXE auch nur in Betracht zu ziehen.
+
+Also wird normalerweise alles was von einem Plugin gebraucht wird in ein eigenes
+.so ausgelagert und sowohl Plugin als auch Programm dagegen gelinkt. Im Extremfall
+ist der Source des Programms ein Fünfzeiler (nur main) und alle Funktionalität
+liegt in einer DLL/so. (Das mache ich zum Beispiel in einem Projekt auf
+Arbeit so.)
+
+Im Beispiel ist die Funktions-DLL in base und die Plugins in dylib*.
#include "main.h"
#include "../base/thingy.h"
+#include "thready.h"
#include <QLabel>
#include <QPushButton>
MainWin::MainWin()
{
+ //initialize pointers
mthinglib=nullptr;
mthingylabel=nullptr;
mthingy=nullptr;
+ //create buttons
QVBoxLayout*vl;
setLayout(vl=new QVBoxLayout);
vl->addWidget(mthingylabel=new QLabel("..."));
connect(p,SIGNAL(clicked(bool)),this,SLOT(unloadLib()));
vl->addWidget(p=new QPushButton("Call Thingy"));
connect(p,SIGNAL(clicked(bool)),this,SLOT(callThingy()));
+ vl->addSpacing(10);
+ vl->addWidget(p=new QPushButton("Start Thread"));
+ connect(p,SIGNAL(clicked(bool)),this,SLOT(startThread()));
}
void MainWin::callThingy()
{
+ //if we have a thingy, do things and show them in the label
if(mthingy)mthingylabel->setText(mthingy->getThingy());
+ //otherwise apologize
else mthingylabel->setText("Sorry, no thingy");
}
void MainWin::unloadLib()
{
+ //delete the thingy or we might be in trouble next time we use it!
if(mthingy)delete mthingy;
mthingy=nullptr;
+ //delete/unload the library
if(mthinglib){
mthinglib->unload();
delete mthinglib;
mthinglib=new QLibrary(qApp->applicationDirPath()+ "/libdylib1.so");
if(!mthinglib->load())
qDebug()<<"load error"<<mthinglib->errorString();
+ //try to get the thingy factory
ThingyFunc f=(ThingyFunc)mthinglib->resolve("getthing");
qDebug()<<"tried to load, have?"<<mthinglib->isLoaded()<<(intptr_t)f;
+ //use the factory to create a thingy
if(f)mthingy=f();
}
unloadLib();
//load new lib
mthinglib=new QLibrary(qApp->applicationDirPath()+ "/libdylib2.so");
+ //try to get the thingy factory
if(!mthinglib->load())
qDebug()<<"load error"<<mthinglib->errorString();
ThingyFunc f=(ThingyFunc)mthinglib->resolve("getthing");
qDebug()<<"tried to load, have?"<<mthinglib->isLoaded()<<(intptr_t)f;
+ //use the factory to create a thingy
if(f)mthingy=f();
}
+void MainWin::startThread()
+{
+ //start a thread and let it update the label automatically
+ MyThread::startMe(mthingylabel,SLOT(setText(QString)));
+}
+
class QLabel;
class QLibrary;
+
+///main window class
class MainWin:public QDialog
{
Q_OBJECT
public:
+ ///instantiate and populate it
MainWin();
private slots:
+ ///loads library one
void loadLib1();
+ ///loads library two instead
void loadLib2();
+ ///unloads either library
void unloadLib();
+ ///starts a thread to do stuff in the background
+ void startThread();
+ ///call that thingy from one of those libraries
void callThingy();
private:
+ //label to show what we are doing
QLabel*mthingylabel;
+ //the library that we loaded
QLibrary*mthinglib;
+ //the thingy from that library
Thingy*mthingy;
};
QT += widgets
DESTDIR = ../bin
-SOURCES += main.cpp
-HEADERS += main.h thingy.h
+SOURCES += main.cpp thready.cpp
+HEADERS += main.h thready.h
QMAKE_CXXFLAGS += -std=gnu++11
--- /dev/null
+#include "thready.h"
+#include <QTimer>
+
+void MyThread::startMe(QObject* c, const char* s)
+{
+ //create thread and remember connection to the outside world
+ MyThread *m=new MyThread(c,s);
+ //start it
+ m->start();
+}
+
+MyThread::MyThread(QObject* callee, const char* slot): QThread(callee)
+{
+ //remember till run
+ mcallee=callee;
+ mcallslot=slot;
+}
+
+
+void MyThread::run()
+{
+ MyTask task;
+ //call gotime once per second
+ QTimer tmr;
+ connect(&tmr,SIGNAL(timeout()),&task,SLOT(gotime()));
+ tmr.start(1000);
+ //connect updateString to the outside world
+ // Qt::AutoConnection effectively: Qt::QueuedConnection
+ connect(&task,SIGNAL(updateString(QString)),mcallee,mcallslot);
+ //kill the thread in 10s
+ // uses Qt::AutoConnection -> Qt::QueuedConnection, quit() happens in the main thread, but that does not bother me -> it is bounced back to this thread
+ QTimer::singleShot(10000,this,SLOT(quit()));
+ //make sure it deletes itself
+ // Qt::AutoConnection effectively: Qt::DirectConnection - same object
+ connect(this,SIGNAL(finished()),this,SLOT(deleteLater()));
+ //now loop for a while
+ exec();
+}
+
+void MyTask::gotime()
+{
+ //simply count up and tell everyone
+ emit updateString(QString("Hello, I'm a thread and can count to %1").arg(ctr++));
+}
--- /dev/null
+#include <QThread>
+
+///Thread class, it starts, initializes and controls the thread.
+///It does not know much about what is done there.
+class MyThread:public QThread
+{
+ public:
+ ///not needed, use startMe
+ MyThread()=delete;
+
+ ///start the thread
+ ///\param callee the object that gets the updated information, also owner of the thread object
+ ///\param slot the slot to call for updates, must be SLOT(foo(QString)) or SLOT(foo())
+ static void startMe(QObject*callee,const char*slot);
+ protected:
+ ///internal: creates the thread and remembers who to call
+ MyThread(QObject* callee,const char*slot);
+ ///runs the thread
+ void run()override;
+ private:
+ //remember who to call between startMe and run
+ QObject*mcallee;
+ const char*mcallslot;
+};
+
+///the actual class that performs the task of that thread
+class MyTask:public QObject
+{
+ Q_OBJECT
+ public:
+ ///create task object
+ MyTask()=default;
+
+ private slots:
+ ///called to update the thread status
+ void gotime();
+ signals:
+ ///sent to the outside world when the thread has something to say
+ void updateString(QString);
+ private:
+ ///internal status
+ int ctr=0;
+};
\ No newline at end of file