2016-04-27 1 views
1

J'ai donc de nouveau rencontré les limites de QObject s qui ne peuvent pas être mélangées avec des modèles (du moins pas directement). Fondamentalement, j'ai une classe de modèle de proxy qui utilise l'indexation pour traduire les positions de la source à des positions locales et inversement. L'index peut être implémenté de plusieurs façons, pour l'instant j'ai besoin de deux versions, l'une utilisant QHash et l'autre utilisant QVector. L'interface de l'index est commune aux deux avec seulement des différences subtiles concernant la manipulation d'index. Avec des modèles, ce serait facile, je ferais de la classe un modèle, puis j'utiliserais la spécialisation pour ces deux cas.Alternatives à l'implémentation d'index virtuel dans un modèle

Cependant, le modèle doit être un QObject donc plutôt il semble que je aurais besoin d'utiliser le polymorphisme comme ceci:

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; 
}; 

J'ai quelques problèmes avec cela. D'abord, il faut une allocation dynamique de l'indice dont je voudrais me débarrasser. Deuxièmement, en raison de l'utilisation de pointeur IndexInterace pour envoyer les appels à l'index aucune méthode de l'index ne sera jamais en ligne (j'ai examiné le code démonté pour confirmer cela et essayé diverses optimisations, etc. en vain).

Quelles seraient les alternatives à cette conception idéalement sans allocation dynamique de l'indice et sans appels virtuels à l'index?

Répondre

2

Faire les classes de classe spécifiques au type d'index une des bases:

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 
}; 

Ensuite, au lieu de passer le type d'index au constructeur, utiliser une fonction d'usine:

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

Si vous Imaginez une interface plus large ou différente de QAbstractItemModel, vous aurez besoin d'écrire une telle classe de base et en dériver dans les implémentations concrètes, bien sûr.

Vous pouvez utiliser CRTP si nécessaire pour IndexHandler d'appeler directement dans les méthodes de la classe dérivée, ce qui rend encore plus petit:

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 
}; 

Vous pouvez également « promouvoir » les méthodes de la classe de base à fentes Qt:

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

Voir this question pour avoir une classe de base non-QObject avec des signaux et des intervalles. Enfin, vous pouvez utiliser le PIMPL idiom, et avoir une implémentation concrète d'un type fixe comme vous le désirez. L'usine serait invoquée dans le constructeur et vous échangeriez différents PIMPL pour différents index. Ce n'est pas aussi cher que vous le pensez puisque toutes les classes Qt allouent déjà dynamiquement un PIMPL, donc vous pouvez revenir sur cette allocation en dérivant votre PIMPL de QObjectPrivate (#include <private/qobject_p.h>), et en passant l'instance du PIMPL au QObject(QObjectPrivate&) protégé. Ce modèle est omniprésent dans Qt, donc même si c'est un détail de mise en œuvre, il ne disparaîtra pas dans Qt 5 à tout le moins. Voici une esquisse:

// 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) 
    {} 
}; 

Si vous dérivez de QAbstractItemModel, votre PIMPL dériverait de QAbstractItemModelPrivate, de la même façon; cela fonctionne pour n'importe quelle classe de QObject dans Qt!

+0

Assurez-vous d'inclure la macro 'Q_OBJECT' dans les définitions de classe' VectorIndexProxy' et 'HashIndexProxy' dans le premier exemple. –

+0

@JonHarper C'est ce qui est implicite dans le '...' :) Sinon, c'est une pente glissante et la prochaine chose que quelqu'un d'autre me dira est que l'implémentation complète et correcte d'un modèle lui appartient :) –

+0

Bon point. :-) –