Wrapping non-Qt code
-
I have a library on non-Qt code that I would like to add signals to so I can use it in a Qt application easily.
An issue is that my library code is templated, so I can't directly derive from QObject with my wrappers. I fixed this by making a class that derives from QObject that just defines signals/slots, and a second (templated) class that derives from both my signal class and the library code. This also works.
The problem come in because my library is "layered" ie there is a base class and several derived classes. I would like to wrap each derived class in the library separately and not duplicate code. However this leads to a sort of double diamond inheritance problem.
My simple example project is 99% there. however my Qt wrappers can't access the signals of the parent Qt wrappers (right now this is expected since they don't derive from them, as this brings on the double diamond inheritance problem)
To the TLDR version is can I make wrappers for a set of templated classes such that each derived class has a wrapper that wraps its functionality, and each wrapper also gives access to the (wrapped) parent class methods.
here is a simple example that shows what I'm trying to do. I haven't figured out how to attach zip files (maybe it isn't possible) but other than the .ui and .pro files this is pretty much the whole project:
Non-Qt classes (usually in separate header files)
template<typename T> class nonQtBase { public: nonQtBase(T init) { value = init; } virtual T funcAdd(T first, T second) { value = first + second; return value; } T getValue() { return value; } protected: T value; }; template<typename T> class nonQtDrv1 : public nonQtBase<T> { public: nonQtDrv1(T init) : nonQtBase<T>(init) { } virtual T funcSub(T first, T second) { this->value = first - second; return this->value; } }; template<typename T> class nonQtDrv2 : public nonQtDrv1<T> { public: nonQtDrv2(T init) : nonQtDrv1<T>(init) { } virtual T funcMult(T first, T second) { this->value = first * second; return this->value; } };
Qt wrappers (also usually in separate header files)
class QtBaseSignals : public QObject { Q_OBJECT public: explicit QtBaseSignals(QObject *parent=nullptr) : QObject(parent) { } signals: void sigAddResult(QString); }; template<typename T> class QtBase : public QtBaseSignals, public nonQtBase<T> { public: QtBase(T init, QObject *parent = nullptr) : QtBaseSignals(parent), nonQtBase<T>(init) { } // This function wrapps the non-Qt function to emit a signal virtual T funcAdd(T first, T second) { T result = nonQtBase<T>::funcAdd(first, second); emit sigAddResult(QString::number(result)); return result; } }; class QtDrv1Signals : public QtBaseSignals { Q_OBJECT public: explicit QtDrv1Signals(QObject *parent=nullptr) : QtBaseSignals(parent) { } signals: void sigSubResult(QString); }; template<typename T> class QtDrv1 : public QtDrv1Signals, public nonQtDrv1<T> { public: QtDrv1(T init, QObject *parent = nullptr) : QtDrv1Signals(parent), nonQtDrv1<T>(init) { } // challenge is to somehow access the wrapper for the // base function "QtBase::funcAdd" without re-implementing // it here and without breaking everything with multiple inheritance. // another wrapper. for a method in the first derived class virtual T funcSub(T first, T second) { T result = nonQtDrv1<T>::funcSub(first, second); emit sigSubResult(QString::number(result)); return result; } }; class QtDrv2Signals : public QtDrv1Signals { Q_OBJECT public: explicit QtDrv2Signals(QObject *parent=nullptr) : QtDrv1Signals(parent) { } signals: void sigMultResult(QString); }; template<typename T> class QtDrv2 : public QtDrv2Signals, public nonQtDrv2<T> { public: QtDrv2(T init, QObject *parent = nullptr) : QtDrv2Signals(parent), nonQtDrv2<T>(init) { } virtual T funcMult(T first, T second) { T result = nonQtDrv2<T>::funcMult(first, second); emit sigMultResult(QString::number(result)); return result; } };
My main window header:
#include <QMainWindow> #include "qtdrv2.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: void on_nonBaseAdd_clicked(); void on_nonD1Add_clicked(); void on_nonD1Sub_clicked(); void on_nonD2Add_clicked(); void on_nonD2Sub_clicked(); void on_nonD2Mult_clicked(); void on_qtBaseAdd_clicked(); void on_qtD1Add_clicked(); void on_qtD1Sub_clicked(); void on_qtD2Add_clicked(); void on_qtD2Sub_clicked(); void on_qtD2Mult_clicked(); private: Ui::MainWindow *ui; nonQtBase<int> nonQtBaseInt; nonQtDrv1<int> nonQtDrv1Int; nonQtDrv2<int> nonQtDrv2Int; QtBase<int> qtBaseInt; QtDrv1<int> qtDrv1Int; QtDrv2<int> qtDrv2Int; };
My main window code
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow), nonQtBaseInt(0), nonQtDrv1Int(0), nonQtDrv2Int(0), qtBaseInt(0), qtDrv1Int(0), qtDrv2Int(0) { ui->setupUi(this); // connect all the signals for the QtWrappers connect(&qtBaseInt, SIGNAL(sigAddResult(QString)), ui->qtBaseResult, SLOT(setText(QString))); connect(&qtDrv1Int, SIGNAL(sigAddResult(QString)), ui->qtD1Result, SLOT(setText(QString))); connect(&qtDrv1Int, SIGNAL(sigSubResult(QString)), ui->qtD1Result, SLOT(setText(QString))); connect(&qtDrv2Int, SIGNAL(sigAddResult(QString)), ui->qtD2Result, SLOT(setText(QString))); connect(&qtDrv2Int, SIGNAL(sigSubResult(QString)), ui->qtD2Result, SLOT(setText(QString))); connect(&qtDrv2Int, SIGNAL(sigMultResult(QString)), ui->qtD2Result, SLOT(setText(QString))); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_nonBaseAdd_clicked() { nonQtBaseInt.funcAdd(ui->nonBaseFirst->value(), ui->nonBaseSecond->value()); ui->nonBaseResult->setText(QString::number(nonQtBaseInt.getValue())); } void MainWindow::on_nonD1Add_clicked() { nonQtDrv1Int.funcAdd(ui->nonD1First->value(), ui->nonD1Second->value()); ui->nonD1Result->setText(QString::number(nonQtDrv1Int.getValue())); } void MainWindow::on_nonD1Sub_clicked() { nonQtDrv1Int.funcSub(ui->nonD1First->value(), ui->nonD1Second->value()); ui->nonD1Result->setText(QString::number(nonQtDrv1Int.getValue())); } void MainWindow::on_nonD2Add_clicked() { nonQtDrv2Int.funcAdd(ui->nonD2First->value(), ui->nonD2Second->value()); ui->nonD2Result->setText(QString::number(nonQtDrv2Int.getValue())); } void MainWindow::on_nonD2Sub_clicked() { nonQtDrv2Int.funcSub(ui->nonD2First->value(), ui->nonD2Second->value()); ui->nonD2Result->setText(QString::number(nonQtDrv2Int.getValue())); } void MainWindow::on_nonD2Mult_clicked() { nonQtDrv2Int.funcMult(ui->nonD2First->value(), ui->nonD2Second->value()); ui->nonD2Result->setText(QString::number(nonQtDrv2Int.getValue())); } void MainWindow::on_qtBaseAdd_clicked() { qtBaseInt.funcAdd(ui->qtBaseFirst->value(), ui->qtBaseSecond->value()); } void MainWindow::on_qtD1Add_clicked() { qtDrv1Int.funcAdd(ui->qtD1First->value(), ui->qtD1Second->value()); } void MainWindow::on_qtD1Sub_clicked() { qtDrv1Int.funcSub(ui->qtD1First->value(), ui->qtD1Second->value()); } void MainWindow::on_qtD2Add_clicked() { qtDrv2Int.funcAdd(ui->qtD2First->value(), ui->qtD2Second->value()); } void MainWindow::on_qtD2Sub_clicked() { qtDrv2Int.funcSub(ui->qtD2First->value(), ui->qtD2Second->value()); } void MainWindow::on_qtD2Mult_clicked() { qtDrv2Int.funcMult(ui->qtD2First->value(), ui->qtD2Second->value()); }
-
I have a library on non-Qt code that I would like to add signals to so I can use it in a Qt application easily.
An issue is that my library code is templated, so I can't directly derive from QObject with my wrappers. I fixed this by making a class that derives from QObject that just defines signals/slots, and a second (templated) class that derives from both my signal class and the library code. This also works.
The problem come in because my library is "layered" ie there is a base class and several derived classes. I would like to wrap each derived class in the library separately and not duplicate code. However this leads to a sort of double diamond inheritance problem.
My simple example project is 99% there. however my Qt wrappers can't access the signals of the parent Qt wrappers (right now this is expected since they don't derive from them, as this brings on the double diamond inheritance problem)
To the TLDR version is can I make wrappers for a set of templated classes such that each derived class has a wrapper that wraps its functionality, and each wrapper also gives access to the (wrapped) parent class methods.
here is a simple example that shows what I'm trying to do. I haven't figured out how to attach zip files (maybe it isn't possible) but other than the .ui and .pro files this is pretty much the whole project:
Non-Qt classes (usually in separate header files)
template<typename T> class nonQtBase { public: nonQtBase(T init) { value = init; } virtual T funcAdd(T first, T second) { value = first + second; return value; } T getValue() { return value; } protected: T value; }; template<typename T> class nonQtDrv1 : public nonQtBase<T> { public: nonQtDrv1(T init) : nonQtBase<T>(init) { } virtual T funcSub(T first, T second) { this->value = first - second; return this->value; } }; template<typename T> class nonQtDrv2 : public nonQtDrv1<T> { public: nonQtDrv2(T init) : nonQtDrv1<T>(init) { } virtual T funcMult(T first, T second) { this->value = first * second; return this->value; } };
Qt wrappers (also usually in separate header files)
class QtBaseSignals : public QObject { Q_OBJECT public: explicit QtBaseSignals(QObject *parent=nullptr) : QObject(parent) { } signals: void sigAddResult(QString); }; template<typename T> class QtBase : public QtBaseSignals, public nonQtBase<T> { public: QtBase(T init, QObject *parent = nullptr) : QtBaseSignals(parent), nonQtBase<T>(init) { } // This function wrapps the non-Qt function to emit a signal virtual T funcAdd(T first, T second) { T result = nonQtBase<T>::funcAdd(first, second); emit sigAddResult(QString::number(result)); return result; } }; class QtDrv1Signals : public QtBaseSignals { Q_OBJECT public: explicit QtDrv1Signals(QObject *parent=nullptr) : QtBaseSignals(parent) { } signals: void sigSubResult(QString); }; template<typename T> class QtDrv1 : public QtDrv1Signals, public nonQtDrv1<T> { public: QtDrv1(T init, QObject *parent = nullptr) : QtDrv1Signals(parent), nonQtDrv1<T>(init) { } // challenge is to somehow access the wrapper for the // base function "QtBase::funcAdd" without re-implementing // it here and without breaking everything with multiple inheritance. // another wrapper. for a method in the first derived class virtual T funcSub(T first, T second) { T result = nonQtDrv1<T>::funcSub(first, second); emit sigSubResult(QString::number(result)); return result; } }; class QtDrv2Signals : public QtDrv1Signals { Q_OBJECT public: explicit QtDrv2Signals(QObject *parent=nullptr) : QtDrv1Signals(parent) { } signals: void sigMultResult(QString); }; template<typename T> class QtDrv2 : public QtDrv2Signals, public nonQtDrv2<T> { public: QtDrv2(T init, QObject *parent = nullptr) : QtDrv2Signals(parent), nonQtDrv2<T>(init) { } virtual T funcMult(T first, T second) { T result = nonQtDrv2<T>::funcMult(first, second); emit sigMultResult(QString::number(result)); return result; } };
My main window header:
#include <QMainWindow> #include "qtdrv2.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: void on_nonBaseAdd_clicked(); void on_nonD1Add_clicked(); void on_nonD1Sub_clicked(); void on_nonD2Add_clicked(); void on_nonD2Sub_clicked(); void on_nonD2Mult_clicked(); void on_qtBaseAdd_clicked(); void on_qtD1Add_clicked(); void on_qtD1Sub_clicked(); void on_qtD2Add_clicked(); void on_qtD2Sub_clicked(); void on_qtD2Mult_clicked(); private: Ui::MainWindow *ui; nonQtBase<int> nonQtBaseInt; nonQtDrv1<int> nonQtDrv1Int; nonQtDrv2<int> nonQtDrv2Int; QtBase<int> qtBaseInt; QtDrv1<int> qtDrv1Int; QtDrv2<int> qtDrv2Int; };
My main window code
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow), nonQtBaseInt(0), nonQtDrv1Int(0), nonQtDrv2Int(0), qtBaseInt(0), qtDrv1Int(0), qtDrv2Int(0) { ui->setupUi(this); // connect all the signals for the QtWrappers connect(&qtBaseInt, SIGNAL(sigAddResult(QString)), ui->qtBaseResult, SLOT(setText(QString))); connect(&qtDrv1Int, SIGNAL(sigAddResult(QString)), ui->qtD1Result, SLOT(setText(QString))); connect(&qtDrv1Int, SIGNAL(sigSubResult(QString)), ui->qtD1Result, SLOT(setText(QString))); connect(&qtDrv2Int, SIGNAL(sigAddResult(QString)), ui->qtD2Result, SLOT(setText(QString))); connect(&qtDrv2Int, SIGNAL(sigSubResult(QString)), ui->qtD2Result, SLOT(setText(QString))); connect(&qtDrv2Int, SIGNAL(sigMultResult(QString)), ui->qtD2Result, SLOT(setText(QString))); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_nonBaseAdd_clicked() { nonQtBaseInt.funcAdd(ui->nonBaseFirst->value(), ui->nonBaseSecond->value()); ui->nonBaseResult->setText(QString::number(nonQtBaseInt.getValue())); } void MainWindow::on_nonD1Add_clicked() { nonQtDrv1Int.funcAdd(ui->nonD1First->value(), ui->nonD1Second->value()); ui->nonD1Result->setText(QString::number(nonQtDrv1Int.getValue())); } void MainWindow::on_nonD1Sub_clicked() { nonQtDrv1Int.funcSub(ui->nonD1First->value(), ui->nonD1Second->value()); ui->nonD1Result->setText(QString::number(nonQtDrv1Int.getValue())); } void MainWindow::on_nonD2Add_clicked() { nonQtDrv2Int.funcAdd(ui->nonD2First->value(), ui->nonD2Second->value()); ui->nonD2Result->setText(QString::number(nonQtDrv2Int.getValue())); } void MainWindow::on_nonD2Sub_clicked() { nonQtDrv2Int.funcSub(ui->nonD2First->value(), ui->nonD2Second->value()); ui->nonD2Result->setText(QString::number(nonQtDrv2Int.getValue())); } void MainWindow::on_nonD2Mult_clicked() { nonQtDrv2Int.funcMult(ui->nonD2First->value(), ui->nonD2Second->value()); ui->nonD2Result->setText(QString::number(nonQtDrv2Int.getValue())); } void MainWindow::on_qtBaseAdd_clicked() { qtBaseInt.funcAdd(ui->qtBaseFirst->value(), ui->qtBaseSecond->value()); } void MainWindow::on_qtD1Add_clicked() { qtDrv1Int.funcAdd(ui->qtD1First->value(), ui->qtD1Second->value()); } void MainWindow::on_qtD1Sub_clicked() { qtDrv1Int.funcSub(ui->qtD1First->value(), ui->qtD1Second->value()); } void MainWindow::on_qtD2Add_clicked() { qtDrv2Int.funcAdd(ui->qtD2First->value(), ui->qtD2Second->value()); } void MainWindow::on_qtD2Sub_clicked() { qtDrv2Int.funcSub(ui->qtD2First->value(), ui->qtD2Second->value()); } void MainWindow::on_qtD2Mult_clicked() { qtDrv2Int.funcMult(ui->qtD2First->value(), ui->qtD2Second->value()); }
@SummoningDark said in Wrapping non-Qt code:
To the TLDR version is can I make wrappers for a set of templated classes such that each derived class has a wrapper that wraps its functionality, and each wrapper also gives access to the (wrapped) parent class methods.
You can't template a
QObject
. AFAIK moc doesn't support it. -
Sorry, I should have been more clear in my summary. Another key is that the signals/slots do not depend on the templating, so they can be separated.
The following works for a single wrapped class:
1 create a non-templated class that derives from QObject and defines all the signals and slots(slots defined pure virtual and implemented in the final class)2 create the templated wrapper class that derives from both the signal/slot class from 1 and the templated library class. ( this class defines the functionality of the slots )
This can be seen in the QtBaseSignals/nonQtBase<T>/QtBase<T> in my example code, and works fine as far as I can tell.
Using virtual inheritance, I can make a derived class that has access to the methods in the first wrapper class. Even adding new slots works by making a second custom signals/slots class. The real issue is that if I create a new signal (in ex. QtDrv1Signals) and try to connect it, I get an error. It seems to be this:
Cannot convert from pointer to base class to pointer to derived class
I was hoping that someone had a clever solution. If all else fails, I can re-write the templated code.
-
You should be using composition instead of inheritance.
Instead of
template<typename T> class QtBase: public QtBaseSignals, public nonQtBase<T> { ... }
you should write
template<typename T> class QtBase : public QtBaseSignals { Q_OBJECT // this is totally necessary (don't know if it works with templates...) std::unique_ptr<nonQtBase<T>> data; ... }
This should not be much of a problem as you are wrapping all functions already in order to be able to emit signals.
The derived class would then also just inherit from the base:
template<typename T> class QtDrv1 : public QtBase { ... }
(You should drop the QtBaseSignals/QtDrv1Signals classes!) QtDrv1 will not have its own
data
member, but will use the one provided by QtBase. This is the good thing about polymorphism. However, when forwarding signals in your derived Qt classes you should always cast to the derived type, e.g. nonQtDrv1<T>, ofdata
before accessing it.