PDA

View Full Version : Multiple inheritance & Qt



dublet
3rd March 2006, 11:10
This post is because of QQ15 (http://doc.trolltech.com/qq/qq15-academic.html).

I have an existing application which has several object, all related, but not yet using a single unifying object to command a universal interface across these objects. There are 4 different main types, one table object (which inherits QTable), one graph object (which inherits QWidget) and two text objects (which both inherit QTextView).
All these object thusly have a common ancestor, namely QWidget.
Using single inheritance would be a solution, but would require the reimplementing of Qt code, which would make it harder to maintain and which should be needless in the first place.

As far as I can read from the article, the solution is to create an interface and a wrapper, as follows:



class Interface {
public:
virtual void method() = 0;
};

class Wrapper : public QWidget, public Interface {
Q_OBJECT
Interface *wrapped;
public:
Wrapper(QWidget *p) : QWidget(p) {
wrapped = cast_stuff<Interface *>(p);
}
~Wrapper();
public slots:
void method() { wrapped->method(); }
};

Then you create an object structure based on the interface:


class Document : public QWidget, public Interface {
Q_OBJECT // Is this even necessary?
Wrapper *wrapper;
public:
Document(QWidget *p = 0) : QWidget(p) { }
virtual ~Document() { }
virtual void method() { generic_implementation; }
};

class Table : public QTable, public Document {
public:
Table(QWidget *p = 0) : QTable(p), Document(0) {
}
~Table() {
}
virtual void method() { specific_implementation; }
};



This is completely based on the example declarations. Now I have a few questions.
First of all, how would you go about creating a specific object?
Is it like this:

Interface *d = new Table();
(But then you wouldn't need the wrapper?)
Or would you use the wrapper, like so:

Wrapper *w = new Wrapper(new Table());
Second, why would you include the Wrapper object in the Document, does the wrapped object need to know if it's being wrapped and if so, why?

I hope someone can help me with the problem, as Voker's example code didn't include any usage tips.

high_flyer
3rd March 2006, 14:18
There are several things I don't understand in what you wrote:
First, the article you linked to is dicussing inheriting QObject from a template calss, and the problematic it has with moc.
However you are talking about general inharitance problems that has nothing to do with moc and templates.

Now, regarding your inheritance design:
A wrapper is made, when you want to have a part of a code that always does the same work with the same API, but that the objects it operates on might change (polymorphysem).
So in your case I would say you need three such wrappers:
-A Table object interface
-A graph object interface
-A text object interface
- If there are any custom (i.e not QWidget) methods all these object share then you can also make an interface that binds all these classes in a common wrapper for these operations
NOTE: such interfcae design schemes make sence for larger applications or for code that is often used in various situations, not that much for small and specific applications)
You can use the fact they all derive from QWidget for all operations that are derived from QWidget, and in these places use a QWidget pointer (so you will be able to access all objects on these oprations regardless of which derived type the object is currently).

Using single inheritance would be a solution, but would require the reimplementing of Qt code, which would make it harder to maintain and which should be needless in the first place.
I didn't understand this in respect to the code you posted.
You will have to write your own code for each custom class, thats what deriving is all about.
What Qt code would need to REimplement?
your Interface class is not needed, your wrapper class can offer that functionality.

dublet
3rd March 2006, 15:21
There are several things I don't understand in what you wrote:
First, the article you linked to is dicussing inheriting QObject from a template calss, and the problematic it has with moc.
However you are talking about general inharitance problems that has nothing to do with moc and templates.
Sorry for not being more clear on this, as the article actually treats three different subjects. :) My question is regarding the last article, called "Multiple inheritance". ;)


Now, regarding your inheritance design:
A wrapper is made, when you want to have a part of a code that always does the same work with the same API, but that the objects it operates on might change (polymorphysem).
So in your case I would say you need three such wrappers:
-A Table object interface
-A graph object interface
-A text object interface
- If there are any custom (i.e not QWidget) methods all these object share then you can also make an interface that binds all these classes in a common wrapper for these operations

Three different wrappers? Why would they be necessary, as there only needs to be one general wrapper which provided for various of the shared functions (plus the added advantage of being able to treat and manage them as the same object)?


NOTE: such interfcae design schemes make sence for larger applications or for code that is often used in various situations, not that much for small and specific applications)

This is a rather large application. :)

You can use the fact they all derive from QWidget for all operations that are derived from QWidget, and in these places use a QWidget pointer (so you will be able to access all objects on these oprations regardless of which derived type the object is currently).That's what's happening already, but there are numerous situations where the three different object classes have to reimplement the same functions to provide certain functionality, but there is no interface to mandate the implementation of them.


I didn't understand this in respect to the code you posted.
You will have to write your own code for each custom class, thats what deriving is all about.
What Qt code would need to REimplement?
your Interface class is not needed, your wrapper class can offer that functionality.
Well, a single inheritance solution using a shared Document class would be a solution, but as such you will need to create a "has a" type of interface, which passes the function calls to the object (i.e. from Table to the QTable). This carries several problems as you need to hijack a lot of things to make it appear as though it actually is a QTable. Reimplementing may be a harsh word, as you "pass the buck", but effectively you need to provide the complete QTable interface, for starters, and a QTextEdit interface for the Text documents.

The problem stems from the face that the three major objects are in fact already base classes for more specific classes.

high_flyer
3rd March 2006, 16:02
Three different wrappers? Why would they be necessary, as there only needs to be one general wrapper which provided for various of the shared functions (plus the added advantage of being able to treat and manage them as the same object)?
Well, it was not clear to me what was common to all objects.
So I assumed each object has its own functionlity and diefferent API.
But in this post you made things more clear.
Then if what you are looking for is an inteface class that will bind only the common functionalities of the various objects, I would make one such interface and use virtual or pure virtual methods.
Where is the problem then?

That's what's happening already, but there are numerous situations where the three different object classes have to reimplement the same functions to provide certain functionality, but there is no interface to mandate the implementation of them.
I see.
This is hard to answer with out knowing exactly the specifics of your implementation or exactly what it is you want to do.

dublet
3rd March 2006, 16:33
Then if what you are looking for is an inteface class that will bind only the common functionalities of the various objects, I would make one such interface and use virtual or pure virtual methods.
Where is the problem then?

Signals and slots. :(

Rather more specifically, the MOC. The Q_OBJECT macro does some C style casting which the compiler won't accept in the multiple inheritance scenario. :( It would work perfectly if we didn't use either signals nor slots, but that would kind of remove most of the functionality of the program. ;)

high_flyer
3rd March 2006, 17:50
I don't understand why signal and slots are a problem.
I use multiple inheritance with Qt, many times in order to give external non Qt classes the ability to use signal and slots and I have no problem.
Could you give an example for a class and a compiler erros it gives you?
You have to make sure you have Q_OBJECT macro in your class decleration.

dublet
6th March 2006, 08:55
I don't understand why signal and slots are a problem.
I use multiple inheritance with Qt, many times in order to give external non Qt classes the ability to use signal and slots and I have no problem.
Could you give an example for a class and a compiler erros it gives you?
You have to make sure you have Q_OBJECT macro in your class decleration.

Ok, first we define a base class:


class Document : virtual public QWidget {
Q_OBJECT

public:
Document(QWidget *p, const char *c) : QWidget(p, c) { }
virtual ~Document() {}
public slots:
virtual void method() {QMessageBox::warning(0, "bla", "doc");}
};
Then we define a derived class:

class Table : virtual public QTable, virtual public Document {
public:
Table(QWidget *p, const char *n) : QTable(p, n), Document(NULL, NULL) {
}
virtual ~Table() {
}
virtual void method() { QMessageBox::warning(0, "bla", "table"); }
};
Then we make some test code:

Document *doc = new Table(this, "");

action = new QAction( tr("Test"), tr("&Test"), CTRL+Key_T, this );
connect( action, SIGNAL( activated() ), doc, SLOT( method() ) );
This works fine. But what if you want to add slots and signals to the Table class? Well, we add the Q_OBJECT macro, and add a public slot thingy with a specific method, like so:


class Table : virtual public QTable, virtual public Document {
Q_OBJECT
public:
Table(QWidget *p, const char *n) : QTable(p, n), Document(NULL, NULL) {
}
virtual ~Table() {
}
virtual void method() { QMessageBox::warning(0, "bla", "table"); }
public slots:
void specific_slot() { QMessageBox::warning(0, "bla", "slotty"); }
};
However, when compiling this, Visual Studio throws a hissy fit, saying

c:\projects\mi\table.h(7) : error C2594: 'type cast' : ambiguous conversions from 'class Table *const ' to 'class QObject *'This is the very line in the table.h file, which contains the Q_OBJECT macro. :(
It can't be good design if you have to include all the slots from derived classes in the base class. :|

high_flyer
7th March 2006, 10:10
I get it now.
You where talking about multiple QObject inheritance, where as I was talking about general multiple inheritance of non Qt class with a QObject class.
As far as I know this is not possible due to moc.
With out thinking too deep about it at the moment, it could be that a scheme of "between classes" might work, where in your inheritance tree you split the functionalities in to QObject and non QObject calsses, and then fuse these togeather, where in each "fusion" you get only one QObject inheritance. (Hope I was clear, but I am not sure this is doable for you, it depends on your inheritance needs and structure)

EDIT:
You could achieve the above by adding the "slots" in your non QObject classes as normal methods, and then when you drive your QObject-NonQObject class, to wrap the intended slot methods in slots.
This way Document class wont need to be a QObject and it will eliminate the QObject ambiguity

dublet
7th March 2006, 11:52
If there was a quick and easy way to do this, I would have done so already. The major issue with writing all these wrapper classes is that it would take at least 24 of them, possibly up to 34. In addition to this, 1533 connect() calls would have to be scrutinised (or adapted), and countless other blurps of code would need to be adapted through over 260.000 lines of code (excluding blank and comment lines).

Hope you can see this is not really something I want to do. There is an existing object structure, which I keep wanting to use, just improve on it by taking advantage of inheritance and polymorphism more (I'm not the original creator, this is maintenance work).

Is this an impossibility? :confused:


I get it now.
You where talking about multiple QObject inheritance, where as I was talking about general multiple inheritance of non Qt class with a QObject class.
As far as I know this is not possible due to moc.
Yes, that's what I'm talking about. The third article in QQ15 is specifically about a certain method on solving this, except it's a bit unclear about the details. :(

Chicken Blood Machine
7th March 2006, 16:40
Do you need to use 'Qt' signals and slots specifically? Maybe you can take a look at boost.org and make use of the signals library offered there?

high_flyer
7th March 2006, 20:33
Yes, that's what I'm talking about. The third article in QQ15 is specifically about a certain method on solving this, except it's a bit unclear about the details.
Now that I have read fully the text in the link you gave I see that what the trolls offer in the link you offered is the same thing I did, only they formulated it much better ;)
Notice that they are using a wrapper to wrap the methods that are intended to be used as slots from the non Qt class in slots of the wrapper.

dublet
8th March 2006, 09:12
Now that I have read fully the text in the link you gave I see that what the trolls offer in the link you offered is the same thing I did, only they formulated it much better ;)
Notice that they are using a wrapper to wrap the methods that are intended to be used as slots from the non Qt class in slots of the wrapper.

If you'll then read my first post again, you can see that's what I tried to implement in the first place.