PDA

View Full Version : MOC and macro expanding



kert
4th August 2015, 15:34
I know that Qt4 moc was not able to do macro expanding, and now Qt5 moc can do it, at least partly.
I have macro for simple signals adding to multiple classes, lets say, for errors and warnings reports.


#define ERROR_SENDER \
signals: \
void error(Errors::ErrorCodes errorCode);

#define WARNING_SENDER \
signals: \
void warning(Warnings::WarningCodes warningCode);

#define ERROR_AND_WARNING_SENDER \
ERROR_SENDER \
WARNING_SENDER

And, in another header:


class Compressor : public QObject
{
Q_OBJECT

ERROR_AND_WARNING_SENDER

...
}

But it doesn't work. If I expand macro myself with simple copy-paste, it works, and signals are added in moc-generated file. But if I use macro, signals are not added in moc-generated file and it results in
./debug/compressor.o: In function `ZN10Compressor8openFileERK7QString6QFlagsIN9QIODe vice12OpenModeFlagEE':
compressor.cpp:40: undefined reference to `Compressor::error(Errors::ErrorCodes)'

How to fix it?

stampede
4th August 2015, 21:31
I don't know about macros, but you can use a simple base class to do that:


class Notifier : public QObject{
Q_OBJECT
signals:
void error(Errors::ErrorCodes errorCode);
void warning(Warnings::WarningCodes warningCode);
};


class Compressor : public Notifier
{
Q_OBJECT
}



It is much cleaner and now it is easier to add new functionality to all the signal-sending objects in the hierarchy. It will work in both Qt4 and Qt5 too.
If you have a lot of common code, macros will get messy very quickly.
Another thing, does it really make sense for an object to be an "error sender" without being a "warning sender" ? If something can report an error, surely it should be able to report a warning.
Anyway, I'd go for common functionality via base class instead of macros.

kert
5th August 2015, 10:34
but you can use a simple base class to do that
That was my first idea before creating any macros. The problem is that Qt classes don't support multiple inheritance from QObject-based classes. More correctly, there can be only one QObject-based parent and it should be first one in inheritance ierarchy. Lets say I use that Notifier class you suggested, and I have two other classes:
class A : public QMainWindow
and
class B : public QDialog
I want them both use my error() and warning() signals, connected to single object who shows user all errors and warnings from the whole program. However, I can't inherit from Notifier and QMainWindow or Notifier and QDialog - they are both based on QObject already.

Another thing, does it really make sense for an object to be an "error sender" without being a "warning sender" ? If something can report an error, surely it should be able to report a warning.
Its for possible future program expanding. However, it will not work with a single macro anyway.

stampede
5th August 2015, 20:15
You can use composition instead of inheritance (so the mainWindow or dialog instance "contains" a notifier object).
If you want to stick to inheritance, then the "Notifier" class does not have to be QObject-based:


// helper class used to maintain the connections
class NotifierHelper : public QObject{
Q_OBJECT
signals:
void error(Errors::ErrorCodes errorCode);
void warning(Warnings::WarningCodes warningCode);
};

// "error reporting" base class, not based on QObject
class Notifier
public:
void error(Errors::ErrorCodes errorCode){
this->_sender.error(errorCode); // you can invoke signals directly, like method calls (in fact they are methods)
}
void addErrorReceiver(QObject * receiver, const char * signal_or_slot){
QObject::connect(&_sender, SIGNAL(error(Errors::ErrorCodes)), receiver, signal_or_slot);
}
private:
NotifierHelper _sender;
};


class Compressor : public Notifier
{
Q_OBJECT
public:
void functionWithError(){
SomeReceiver receiver;
this->addErrorReceiver(&receiver, SLOT(receiveError(ErrorCodes)));
// now the 'receiver' object will get the error signal
this->error(Errors::SomeErrorCode);
}
}

There exists quite a lot of object-oriented programming languages without the macro expansion feature. Just imagine how would you solve this problem in one of these languages.

kert
6th August 2015, 11:32
You can use composition instead of inheritance (so the mainWindow or dialog instance "contains" a notifier object).
Yes, but I don't like it. It's kind of "OOP-hack" for me, because architecturally it is inventing another class and making another object when you just want to make base class realise an interface.

If you want to stick to inheritance, then the "Notifier" class does not have to be QObject-based
It is an encapsulation of composition. Looks like I will use this, though it's not OOP-pure. Yes, macros are not pure too :D

There exists quite a lot of object-oriented programming languages without the macro expansion feature. Just imagine how would you solve this problem in one of these languages.
Using interfaces?

Going to composition-inheritance combination, thank you.