PDA

View Full Version : Qt and Templates



jsmax
22nd September 2011, 10:31
Hi.

First, sorry for my bad knowledge of C++ templates :)

I have 2 classes: Street and Manager.



class Street
{
public:
Street();
~Street();

QMap<QString, QString> values; // ex: "id" => "1", "name" => "Paris"
....
};



class Manager
{
public:
Manager();
~Manager();

QMap<QString, QString> values; // ex: "id" => "1", "name" => "John"
....
};


Now i have 2 QAbstractTableModel subclasses: one that works with streets and one for managers.



class StreetTableModel : public QAbstractTableModel
{
Q_OBJECT

public:
explicit StreetTableModel(QObject *parent = 0);
....

private:
QList<Street*> records; // Here we store the streets
....
};



class ManagerTableModel : public QAbstractTableModel
{
Q_OBJECT

public:
explicit ManagerTableModel(QObject *parent = 0);
....

private:
QList<Manager*> records; // Here we store the managers
....
};


In the future there will be a lot of objects like street and manager and i don't want to create each time a subclass of QAbstractTableModel.

Can you please show me a way to define a subclass of QAbstractTableModel using templates ? I tried some variants, but a don't get it how it must work.

I want to be able for example to create a model like:


TableModel<Street> *model = new TableModel();

Thanks a lot.

franz
22nd September 2011, 21:16
Try something like



template<typename T>
class TableModel : public QAbstractTableModel
{
Q_OBJECT

public:
explicit TableModel(QObject *parent = 0);
....

private:
typename QList<T *> records;
....
};


It's dry coded and un-tested, but I'd expect it to behave like you want:


TableModel<Street> *model = new TableModel<Street>();

Note that this might mean a lot of the code needs to be templated, which means you're going to have to put all that implementation in a header file. Be sure to document the requirements on T. Also, given the nature of the example classes you give, you can consider not using pointers in your list. This will probably make management of the records much simpler. If that's possible, use
typename QList<T> records; instead.

Santosh Reddy
23rd September 2011, 04:51
I would suggest not to use template for your requirement, and use C++ polymorphism. Here is an example, the main point is to use BasicValue class as a interface for future classes / objects


class BasicValue
{
public:
BasicValue();
virtual ~BasicValue();


QMap<QString, QString> values; // ex: "id" => "1", "name" => "Paris" (anything)
....
};


class Street : public BasicValue {};
class Manager : public BasicValue {};
class FutureItem : public BasicValue {};


class ValueTableModel : public QAbstractTableModel
{
Q_OBJECT


public:
explicit ValueTableModel(QObject *parent = 0);
....


private:
QList<BasicValue*> records;
....
};

franz
23rd September 2011, 06:03
That is a nicer solution. I gave what OP wants, you gave what OP needs.

wysota
23rd September 2011, 12:13
Some time ago I was thinking about a template based approach to the problem myself too but the downside was that you can have models with different number of columns or different types of data. Neither of the approaches presented here actually solve the problem. I think it is possiblt to do it both using templates and polymorphism. As for the latter, we go in direction of QStandardItemModel so maybe there is no point in doubling what it already provides (you can see that the solution presented in this thread looks strikingly like QStandardItemModel with BasicValue being QStandardItem and Street being a subclass of the latter).

As for the template based approach I think we'd need to provide information to the template class about ways of accessing data in the model for instance by passing a container holding the names or types of data in each column. An example instantiation could then look like this:

class RecordDescription {
public:
//...
const_iterator begin() const;
const_iterator end() const;
//...
};

TemplateBasedModel<RecordType, RecordDescription> model;
or:

TemplateBasedModel<RecordType> model(recordDescription);
with "modelDescription" being an object that can read and write data to RecordType instances.

jsmax
25th September 2011, 12:56
Try something like



template<typename T>
class TableModel : public QAbstractTableModel
{
Q_OBJECT

public:
explicit TableModel(QObject *parent = 0);
....

private:
typename QList<T *> records;
....
};


I get errors on line 11 in your code:

error: expected nested-name-specifier
error: invalid declarator before 'records'

This is my class (i get the same errors in my class):


template<typename TPL>
class JSONTableModel : public QAbstractTableModel
{
Q_OBJECT

public:
explicit JSONTableModel(QObject *parent = 0);

int rowCount ( const QModelIndex & parent = QModelIndex() ) const;
int columnCount ( const QModelIndex & parent = QModelIndex() ) const;

QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const;
bool setData ( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole );
QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
Qt::ItemFlags flags ( const QModelIndex & index ) const;

int getTotalRecordsCount();
int getFetchedRecordsCount();
QString getParam(QString);
QString getHeaderKey(int);
void setParam(QString, QString);

private:
typename QList<TPL *> records;
QMap<QString, QString> headers;
QMap<QString, QString> params;
QNetworkAccessManager *manager;
QModelIndex updatingIndex;
QString url;
int totalRecords;

signals:
void dataLoaded();
void requestStarted();

public slots:
void loadRecords();
void replyFinished(QNetworkReply*);
};

stampede
25th September 2011, 15:19
"typename" is not needed in this case, its needed when the template parameter has a nested type and you want to use it:


template<class T>
class Test{
public:
Test(){
// T::NestedClass t; // this wont compile
typename T::NestedClass t; // this is ok
}
};

jsmax
25th September 2011, 22:59
QObject classes does not support templates :(

During compilation i get this:


Error: Template classes not supported by Q_OBJECT

wysota
26th September 2011, 08:19
They do, only that you can't use Q_OBJECT macro with a template class. You need to subclass twice -- first introduce Q_OBJECT, define all the signals and slots you want and then subclass again and introduce the template.