2016-04-27 7 views
1

So habe ich wieder die Grenzen von QObject s, die nicht mit Vorlagen gemischt werden können (zumindest nicht direkt). Grundsätzlich habe ich eine Proxy-Modell-Klasse, die Indexierung verwendet, um die Quellpositionen zu lokalen Positionen und zurück zu übersetzen. Der Index kann auf verschiedene Arten implementiert werden, für den Moment brauche ich zwei Versionen, eine mit QHash und eine mit QVector. Die Schnittstelle des Index ist beiden gemeinsam, mit nur geringen Unterschieden bezüglich der Indexmanipulation. Mit Vorlagen wäre das einfach, ich würde die Klasse zu einer Vorlage machen und dann Spezialisierung für diese beiden Fälle verwenden.Alternativen zur virtuellen Indeximplementierung in einem Modell

jedoch das Modell ein QObject sein muss, so scheint es, stattdessen würde ich Polymorphismus wie so verwenden müssen:

class IndexInterface; 
class VectorIndex; //inherits IndexInterface 
class HashIndex; //inherits IndexInterface 

class ProxyModel : public QObject 
{ 
    Q_OBJECT 
public: 
    enum IndexType { Vector, Hash }; 

    explicit ProxyModel(IndexType indexType, QObject *parent = 0) : 
     QObject(parent), 
     index(indexType == Vector ? new VectorIndex : new HashIndex) 
    { 
    } 
    //... 

private: 
    IndexInterface *index = nullptr; 
}; 

Ich habe einige Probleme mit diesem. Erstens erfordert es eine dynamische Zuordnung des Index, die ich gerne loswerden möchte. Zweitens, wegen der Verwendung von Zeiger auf IndexInterace, um die Aufrufe an den Index zu versenden, wird keine Methode des Index jemals inline sein (ich habe über disasasemed Code nachgedacht, um dies zu bestätigen und verschiedene Optimierungen usw. vergeblich versucht).

Was wären die Alternativen zu diesem Design im Idealfall ohne dynamische Zuordnung des Index und ohne virtuelle Aufrufe an den Index?

Antwort

2

Machen Sie den Index-typspezifischen Klasse einer der Basisklassen:

template <typename Index> class IndexHandler { 
}; 

using VectorIndexHandler = IndexHandler<QVector>; 
using HashIndexHandler = IndexHandler<QHash>; 

class VectorIndexProxy : public QAbstractItemModel, VectorIndexHandler { 
    ... // should be very small 
}; 

class HashIndexProxy : public QAbstractItemModel, HashIndexHandler { 
    ... // should be very small 
}; 

Dann stattdessen den Indextyp an den Konstruktor vorbei, eine Fabrik-Funktion:

QAbstractItemModel * proxyFactory(IndexType indexType, QObject * parent = 0) { 
    switch (indexType) { 
    case Foo::Vector: 
    return new VectorIndexProxy(parent); 
    ... 
    } 
} 

Wenn Sie Stellen Sie sich eine Schnittstelle vor, die breiter oder anders als QAbstractItemModel ist. Sie müssen eine solche Basisklasse schreiben und daraus natürlich in den konkreten Implementierungen ableiten.

könnten Sie CRTP verwenden, wenn für IndexHandler benötigt direkt in die abgeleitete Klasse Methoden zu nennen, so dass es noch kleiner:

template <typename Index, typename Derived> class IndexHandler { 
    Derived * derived() { return static_cast<Derived*>(this); } 
    const Derived * derived() const; // as above 
    void foo() { 
    derived()->setObjectName("Yay"); 
    } 
}; 

class VectorIndexProxy : 
    public QAbstractItemModel, 
    public VectorIndexHandler<QVector, VectorIndexProxy> 
{ 
    ... // should be very small 
}; 

können Sie auch „fördern“ Methoden der Basisklasse Qt-Slots zu sein:

class VectorIndexProxy : ... { 
#ifdef Q_MOC_RUN 
    Q_SLOT void foo(); 
#endif 
}; 

Siehe this question über eine Basis nicht-QObject Klasse mit Signalen und Slots.

Schließlich könnten Sie die PIMPL idiom verwenden und eine konkrete Implementierung eines festen Typs haben, wie Sie es wünschen. Die Factory würde im Konstruktor aufgerufen und Sie würden verschiedene PIMPLs für verschiedene Indizes austauschen. Das ist nicht so teuer wie Sie denken, da alle Qt-Klassen bereits eine PIMPL dynamisch zuweisen, so dass Sie auf diese Zuordnung durch Ableiten Ihrer PIMPL von QObjectPrivate (#include <private/qobject_p.h>) und Übergeben der Instanz der PIMPL an die geschützte QObject(QObjectPrivate&). Dieses Muster ist in Qt allgegenwärtig, und obwohl es ein Implementierungsdetail ist, wird es zumindest in Qt 5 nicht verschwinden. Hier ist eine grobe Skizze:

// ProxyModel.cpp 
#include <private/qobject_p.h> 

class ProxyModelPrivate : public QObjectPrivate { 
    // Note: you don't need a q-pointer, QObjectData already provides it 
    // for you! CAVEAT: q-pointer is not valid until the QObject-derived-class's 
    // constructor has returned. This would be the case even if you passed 
    // the q-pointer explicitly, of course. 
    ... 
}; // base class 

class VectorProxyModelPrivate : public ProxyModelPrivate { ... }; 

class ProxyModel : public QObject 
{ 
    Q_OBJECT 
    Q_DECLARE_PRIVATE(ProxyModel) 
    ProxyModel * pimpl(IndexType indexType) { 
    switch (indexType) { 
    case Vector: return new VectorProxyModelPrivate(); 
    ... 
    } 
public: 
    enum IndexType { Vector, Hash }; 

    explicit ProxyModel(IndexType indexType, QObject *parent = 0) : 
     QObject(*pimpl(IndexType), parent) 
    {} 
}; 

Wenn Sie von QAbstractItemModel ableiten wurden, Ihre PIMPL von QAbstractItemModelPrivate ziehen würden, auf die gleiche Art und Weise; das funktioniert für jede QObject -herunterziehende Klasse in Qt!

+0

Stellen Sie sicher, dass das Makro 'Q_OBJECT' in den Klassendefinitionen' VectorIndexProxy' und 'HashIndexProxy' im ersten Beispiel enthalten ist. –

+0

@JonHarper Das ist was im '...' :) impliziert. Sonst ist es ein rutschiger Abhang und das nächste, was jemand anderes mir sagen wird ist, dass die gesamte, korrekte Implementierung eines Modells da hingehört :) –

+0

Guter Punkt. :-) –

Verwandte Themen