QT : Templated Q_OBJECT class - c++

Is it possible to have a template class, which inherit from QObject (and has Q_OBJECT macro in it's declaration)?
I would like to create something like adapter for slots, which would do something, but the slot can take arbitrary number of arguments (number of arguments depends on the template argument).
I just tried doing it, and got linker errors. I guess gmake or moc is not getting called on this template class. Is there a way to do this? Maybe by explicitly instantiating templates?

It is not possible to mix template and Q_OBJECT but if you have a subset of types you can list the slots and signals like this:
class SignalsSlots : public QObject
{
Q_OBJECT
public:
explicit SignalsSlots(QObject *parent = 0) :
QObject(parent) {}
public slots:
virtual void writeAsync(int value) {}
virtual void writeAsync(float value) {}
virtual void writeAsync(double value) {}
virtual void writeAsync(bool state) {}
virtual void writeAsync(svga::SSlideSwitch::SwitchState state) {}
signals:
void readAsynkPolledChanged(int value);
void readAsynkPolledChanged(float value);
void readAsynkPolledChanged(double value);
void readAsynkPolledChanged(bool state);
void readAsynkPolledChanged(svga::SSlideSwitch::SwitchState state);
};
...
template <class T>
class Abstraction : public SignalsSlots
{...

Taking into account some restrictions: you can.
First please became familiar (if already not) https://doc.qt.io/archives/qq/qq16-dynamicqobject.html. - it will help to imlement it.
And about restrictions: you can have a template QObject class i.e. template class derived from QObject, but:
Do not tell the moc to compile it.
Q_OBJECT is just a macro and you have to replace it by it real
content which is virtual interface and something else :)
Implement QMetaObject activation (above mentioned virtual interface
and be caution with object info data, which is also come from
Q_OBJECT) and some else functionality and you will have template
QObject (even with template slots)
But as I managed to catch the one draw back - it is not possible to
simply use this class as a base for another class.
There are some other drawbacks - but I think the detail
investigation will show you them.
Hope this will helpful.

It is still not possible to mix templates and Q_OBJECT but depending on your use case you may use the new 'connect'-syntax. This allows at least the usage of template-slots.
Classical non-working approach:
class MySignalClass : public QObject {
Q_OBJECT
public:
signals:
void signal_valueChanged(int newValue);
};
template<class T>
class MySlotClass : public QObject {
Q_OBJECT
public slots:
void slot_setValue(const T& newValue){ /* Do sth. */}
};
Desired usage but not compilable:
MySignalClass a;
MySlotClass<int> b;
QObject::connect(&a, SIGNAL(signal_valueChanged(int)),
&b, SLOT(slot_setValue(int)));
Error: Template classes not supported by Q_OBJECT (For
MySlotClass).
Solution using new the 'connect'-syntax:
// Nothing changed here
class MySignalClass : public QObject {
Q_OBJECT
public:
signals:
void signal_valueChanged(int newValue);
};
// Removed Q_OBJECT and slots-keyword
template<class T>
class MySlotClass : public QObject { // Inheritance is still required
public:
void slot_setValue(const T& newValue){ /* Do sth. */}
};
Now we can instantiate desired 'MySlotClass'-objects and connect them to appropriate signal emitters.
MySignalClass a;
MySlotClass<int> b;
connect(&a, &MySignalClass::signal_valueChanged,
&b, &MySlotClass<int>::slot_setValue);
Conclusion: Using template-slots is possible. Emitting template signals is not working since a compiler error will occur due to missing Q_OBJECT.

I tried explicitly instantiating templates, and got this :
core_qta_qt_publisheradapter.hpp:96: Error: Template classes not supported by Q_OBJECT
I guess that answers my question.
EDIT
Actually, if I place whole template class definition in the header, then the qt preprocessor doesn't process it, and then I get linker errors. Therefore it must be possible to do it, if I add missing methods.
EDIT #2
This library did exactly what I wanted - to use a custom signal/slot mechanism, where the slot has not-defined signature.

Related

Using Qt5 new connect syntax with inheritance

I am trying to use the new connect syntax in some legacy code but came upon an architectural problem. So let's pretend I have a BaseReader class that looks like this :
class BaseReader : public QObject
{
Q_OBJECT
public:
BaseReader();
public slots:
virtual void read(const fstream& myStream);
}
Then let's say I have some children classes like this
class Reader1 : public BaseReader
{
Q_OBJECT
public:
BaseReader();
public slots:
virtual void read(const fstream& myStream);
}
Some of the read work is done in the base class, some in the child class. I have about 4 classes that are switched around at runtime. I have a connectReaders function that looks like this :
void connectReaders(BaseReader* currentReader)
{
connect(this, SIGNAL(mustRead(const fstream&)), currentReader, SLOT(read(const fstream&)));
}
Now it is my understanding that if I use the new syntax I will connect to the base member function no the overloaded versions. Is that correct ? Is there any way to connect these signals using the new syntax without modifying the architecture ? My example is very simplified and modifying that code would require a couple of months (including tests). It works the old way but I would like to take advantage of the new syntax. Any ideas ?
I have looked at these threads but they do not seem to offer a solution to this problem :
This is the other way around:
Using Qt signals and slots with multiple inheritance
I have read this but I am not sure I understand how the overload
section applies: https://wiki.qt.io/New_Signal_Slot_Syntax
I have qt5.4.1, Visual Studio 2013.
There's no problem when using the new 'connect' syntax with virtual slots and base class object pointers. You're specifying the object instance (e. g. currentReader), and the specific method to be called will be resolved using this object's virtual methods table.
Disclaimer: I am not familiar with Qt. The question here however, seems to be a C++ question.
When using a pointer-to-member to a virtual function in a base class, on a pointer/reference to a derived class, an override in the derived class will be called (if it exists).
class Base
{
public:
virtual void f()
{
std::cout << "Base\n";
}
};
class Derived : public Base
{
public:
virtual void f()
{
std::cout << "Derived\n";
}
};
int main()
{
void (Base::* pmf)() = &Base::f;
Derived d;
(d.*pmf)();
}
Will print "Derived", not "Base";
If a Qt signal calls your member function pointer with a derived object, the function override will therefore get called.

QMetaObject::invokeMethod: No such method when using inheritance

I have got a super class Common, which inherits from QObject. Then I have got a class Item, which inherits from Common.
Common.h
class Common : public QObject {
Q_OBJECT
public:
// some methods
};
Item.h
class Item : public Common {
Q_OBJECT
public:
// some methods
void test(QString value);
};
Item.cpp
void Item::test(QString value) {
qDebug() << value;
}
I want to use QMetaObject::invokeMethod to dynamically call a function.
So I implemented a test function in the Item class, which takes exactly one string.
Item* item = new Item();
QMetaObject::invokeMethod(item, "test", Qt::DirectConnection, Q_ARG(QString, "1234"));
This does not work. I get the following error: QMetaObject::invokeMethod: No such method Common::test(QString), which is perfectly okay and fine, because the Common class has no test function.
How can I tell QMetaObject::invokeMethod, that it should call the method from the Item class?
QMetaObject::invokeMethod can only invoke methods known to the Qt meta object system. These are slots and "invokable" functions, the latter being functions with the keyword Q_INVOKABLE before them.
So either write:
class Item : public Common {
Q_OBJECT
public slots:
// ^^^^^
void test(QString value);
};
or:
class Item : public Common {
Q_OBJECT
public:
Q_INVOKABLE void test(QString value);
//^^^^^^^^^
};

Getting around Qt's templating restrictions

I want to write a simple flexible ftp server in C++ that can be parametrized with a class to handle the user (check login and password, deliver files etc.) supplied on server initialization.
So I came up with this neat (so I thought) idea:
class FtpDelegate
{
public:
FtpDelegate() {}
virtual ~FtpDelegate() {}
virtual bool login(QString username, QString password) = 0;
// ...
};
class DummyDelegate : public FtpDelegate
{
public:
virtual bool login(QString username, QString password)
{
return true;
}
};
template<class Delegate>
class FtpServer : public QObject, Derived_from<Delegate, FtpDelegate>
{
Q_OBJECT
public:
explicit FtpServer(const QHostAddress &address = QHostAddress::Any,
quint16 port = 21,
QObject *parent = 0);
public slots:
void newConnection();
private:
QTcpServer *server;
QHostAddress address;
};
template <class Delegate>
void FtpServer<Delegate>::newConnection()
{
FtpDelegate *delegate = new Delegate();
new FtpConnection (delegate, server->nextPendingConnection(), address, this);
}
class FtpConnection : public QObject
{
Q_OBJECT
public:
explicit FtpConnection(FtpDelegate *delegate,
QTcpSocket *socket,
const QHostAddress &address,
QObject *parent = 0);
public slots:
void newDataConnection();
private:
QTcpSocket *socket;
QTcpServer *dataServer; // needed to transfer data to user
QTcpSocket *dataSocket;
};
// server initialization
FtpServer<DummyDelegate> ftpServer();
and then (you probably saw that coming) bam!
Error: Template classes not supported by Q_OBJECT
it is likely that there are other errors or misconceptions too as I am only starting to learn the C++ template mechanism (and Qt as well).
My question is: what is the best way to make it work without using ugly hacks like passing function pointers or needing to create a factory implementation for each concrete FtpDelegate's derived class. Maybe there's some clever design pattern I just can't see. Eventually I can rewrite the network mechanism to boost if it is the best option.
It is not possible to create a template Q_OBJECT class (see this and answers).
Instead of using static inheritance, you should use a run-time inheritance, and inject an object inheriting from FtpDelegate class.
It looks like the FtpServer is actually a factory creating connections. From your question, I do not see why it has to be Q_OBJECT class. So you may need to reconsider your design, and simplify that class.
what is the best way to make it work without using ugly hacks like passing function pointers or needing to create a factory implementation for each concrete FtpDelegate's derived class.
The best way may be to have a factory class, which creates instances of FtpDelegate type. But you have so many problems in the posted code, that it is not possible to tell more without knowing all gory details.

Trouble in declaring pointer before implementation of class

ConverterProxy *pthis;
ConverterProxy::ConverterProxy() {
pthis = this;
converter::callWhenUpdated(&CallBack);
}
void ConverterProxy::CallBack() {
pthis->updatedOutside();
}
ConverterProxy::~ConverterProxy() {
delete pthis;
}
Header:
class ConverterProxy
{
Q_OBJECT
public:
ConverterProxy();
~ConverterProxy();
static void CallBack();
signals:
void updatedOutside();
};
This throws an error:undefined reference to 'vtable for ConverterProxy'
When I put ConverterProxy *pthis; after implementation of constructor, that error disappears.
What is the cause of this weird situation?
I am not an expert in QT, but let me guess how to fix your problem.
There is some QT-related stuff in your code:
Q_OBJECT
I have no idea what it contains; however judging from your error it may add something about virtual functions to your class, even though you didn't have any. Try removing that stuff from your class.
Alternatively, if you really need that Q_OBJECT bit, try inheriting from QObject:
class ConverterProxy: public QObject
{
Q_OBJECT
...
};
There is some documentation, which hints that Q_OBJECT must be used together with inheritance from QObject: http://developer.blackberry.com/native/reference/cascades/moc.html
Don't delete pthis in your destructor. Your object is already being destroyed.

Connecting (in an unusual way) signals to slots

I've designed some construction like this:
template<class Ui_Class>
class Base_Dialog : virtual public QDialog, protected Ui_Class
{
protected:
QDialog* caller_;
public:
template<class Implementation>
Base_Dialog(Implementation*const & imp,QDialog *caller,QWidget* parent = nullptr);
};
template<class Ui_Class>
template<class Implementation>
Base_Dialog<Ui_Class>::Base_Dialog(Implementation*const& imp,QDialog *caller,QWidget* parent):
QDialog(parent),
caller_(caller)
{
setupUi(imp);
}
I'm using it like so:
class My_Class : public **Base_Dialog<Ui::My_Class>**
{
Q_OBJECT
public slots:
void display_me()
{/*THIS IS NOT GETTING CONNECTED*/
QMessageBox::warning(this,"Aha!","Aha!");
}
public:
explicit My_Class(QDialog* caller = nullptr,QWidget *parent = nullptr);
};
Line_Counter::Line_Counter(QDialog* caller,QWidget *parent) :
Base_Dialog(this,caller,parent)
{
//setupUi(this);//THIS WORKS BUT I'D RATHER CALL IT FROM Base_Dialog
}
this above construct is suppose to ease and simplify the way of inheriting from QDialog and from Ui class. And this works, except that when slots and signals are introduced in my class the base class for some reason doesn't see them (slots/signals). If I call setupUi in My_Class ctor everything works correctly but I'd prefer to call it in Base_Class. Is there a way to do it?
The constructor for lineCounter should be renamed to My_Class as well, am I right?
The reason for this behavior is that the meta object returned in constructor of Base_Dialog from virtual const QMetaObject * metaObject() const is meta-object for Base_Dialog, as at this stage it is not yet instance of My_Class instance. My_CLass overrides this virtual method (invisibly in Q_OBJECT macro) but only after My_Class constructor code starts to execute - means after Base_Dialog code is finished. Signals and slots internally use meta-objects while connecting, so it seems this way it will not work.
Summarizing - you cannot do it, because constructor of Base_Dialog doesn't know that My_Class instance is being created and therefore cannot have acces to any of it's contents.
Sometimes this problem can be bypassed using CRTP, but in this case I have no idea if it's aplicable. I would rather opt for doing it Qt's way - call setupUi from My_Class constructor.