1

Qt 5.0.0 を使用

以下は大まかなObserverパターンです (問題のみを説明するために、コードは最小限に抑えられています)。

class A : public QObject
{
    Q_OBJECT
    public:
        void registerListner(Observer *pObs);
        static A* getInstance();
    signals:
        void sig();
};

void A::registerListner(Observer *pObs)
{
    connect(this, SIGNAL(sig()), pObs, SLOT(slo));
}

////////////////////////////////////////////////////////////////

class Observer : public QObject
{
    Q_OBJECT
    public slots:
        virtual void slo() = 0;
};

class ConcreteObserver : public Observer , public QWidget
{
    Q_OBJECT
    public: //re-mentioning "slots" is not necessary
        virtual void slo();
};

ConcreteObserver *pCObs = new ConcreteObserver;
A::getInstance()->registerListner(pCObs);

/////////////////////////////////////////////////////////////

問題 (dreaded-diamond を除く): QObject から複数回継承することはできません - moc はそれを許可しません。考えられる解決策の 1 つは、QWidget から Observer を派生させてから、Observer だけから ConcreteObserver を派生させることです。ただし、これは ConcreteObserver に制約を課しています。おそらく、ConcreteObserver_2 は代わりに QDialog から派生する必要があります。

この設計上の問題を解決するにはどうすればよいですか? これを解決できる Qt 5.0.0 Signal-Slot (以前のバージョンに加えて) に固有のものはありますか、または何を提案しますか?

4

1 に答える 1

1

実行時の警告では不十分な場合は、関数テンプレートを作成してコンパイル時の型チェックを少し追加し、クラス自体を定義しないことregisterListenerで多重継承を回避できます。QObjectObserver

これは次のようになります:(注:私のSFINAEスキルは存在しません、これはおそらくもっと良くなる可能性があります。)

#include <QObject>
#include <QDebug>

#include <type_traits>

class A : public QObject
{
    Q_OBJECT
    public:
        template <typename T>
        void registerListener(T *pObs)
        {
            static_assert(std::is_base_of<QObject, T>::value,
                          "Listener must be a QObject");
            static_assert(std::is_same<void, 
                                       decltype(std::declval<T>().slo())
                                      >::value,
                          "Slot slo must have signature void slo();");
            connect(this, SIGNAL(sig()), pObs, SLOT(slo()));
        }
        static A* getInstance() { return instance; }
        static void init() { instance = new A; }
        void doStuff() { emit sig(); }
    signals:
        void sig();
    private:
        static A *instance;
};

いくつかのテストケース:

class BadObject1 : public QObject
{
    Q_OBJECT
    public:
        BadObject1() {}
    public slots:
        void slo(int){}
};
class BadObject2 : public QObject
{
    Q_OBJECT
    public:
        BadObject2() {}
    public slots:
        int slo(){return 0;}
};
struct BadObject3 {
    void slo();
};

class ObservedObject : public QObject
{
    Q_OBJECT
    public:
        ObservedObject(QString const& name): QObject() {
            setObjectName(name);
        }
    public slots:
        virtual void slo(){
            qDebug() << objectName();
        }
};
class ObservedObject2 : public ObservedObject
{
    Q_OBJECT
    public:
        ObservedObject2(QString const& name)
          : ObservedObject(name + " (derived)") {}
};

そしてメインファイル:

#include "A.h"

A* A::instance = 0;

int main(int , char **)
{
    A::init();
    A::getInstance()->registerListener(new BadObject1);
    A::getInstance()->registerListener(new BadObject2);
    A::getInstance()->registerListener(new BadObject3);
    A::getInstance()->registerListener(new ObservedObject("foo"));
    A::getInstance()->registerListener(new ObservedObject2("bar"));
    A::getInstance()->doStuff();
}

すべての場合でコンパイラエラーが発生しますBadObjectN。コメントアウトすると、出力は次のようになります。

"foo" 
"bar (derived)" 

ただし、警告:これはメンバーが実際にスロットであるかどうかをチェックしません。void slo();実行時に次のように確認できます。

if (pObs->metaObject()->indexOfSlot("slo()") == -1) {
    qDebug() << "Class" << pObs->metaObject()->className() 
                        << "doesn't have a slo slot.";
   ::exit(1);
}

これは機能し、期待どおりに機能します(スロットが仮想として宣言されていないクラス階層がある場合を除きます。その場合、slots「指定子」を省略した派生クラスで奇妙なことが起こります。したがって、ドキュメントにはその指定子について上記のコメント:スロットをオーバーロードするときにそれを持っていることは常に良い考えです)。

この最後のチェックはコンパイル時に達成できるとは思いません。「スロット解決」はQObjectメタデータのランタイムウォークで行われ、mocで生成された文字列の解析が含まれます。再帰的なテンプレートマジックを使用したとしても、それがうまくいくとは思いません。登録タイプで実行時エラーメッセージが表示され、障害のあるオブジェクトの実際のクラス名を含めることができます。これは非常に正確なエラーメッセージであり、最も単純なテストケースでキャッチする必要があります。

于 2013-02-03T09:54:44.650 に答える