Qtメタオブジェクトシステム(Meta-Object)(二)、mocの使用

17695 ワード

この文章は公式文書から翻訳された.
目次
  • メタオブジェクトコンパイラ(MOC)
  • を使用
  • 使用方法
  • mocを呼び出すルール
  • を記述する
  • コマンドラインオプション
  • 診断
  • 制限
  • 多重継承にはQObjectが最初の
  • に必要である
  • 信号およびスロットのパラメータは、関数ポインタ
  • にはならない.
  • 列挙およびTypedefは、信号およびスロットパラメータの要件
  • に完全に適合する必要がある.
  • ネストクラスには、信号またはスロット
  • はありません.
  • 信号/スロットの戻り値タイプは、参照
  • ではない.
  • 信号とスロットのみがクラスのsignalsとslots部分
  • に現れる可能性がある
    メタオブジェクトコンパイラの使用(MOC)
    メタオブジェクトコンパイラMOCはQtのC++拡張を扱うプログラムである.MOCツールはC++ヘッダファイルを読み込みます.Q_が含まれている場合OBJECTマクロの1つ以上のクラス宣言は、これらのクラスのメタオブジェクトコードを含むC++ソースファイルを生成します.これに加えて、信号およびスロットメカニズム、ランタイムタイプ情報、および動的属性システムには、メタオブジェクトコードが必要です.MOCによって生成されたC++ソースファイルは、クラスのインプリメンテーションにコンパイルされ、リンクされる必要があります. qmakeを使用してmakefileを作成する場合、構築ルールには必要に応じてmocを呼び出すルールが含まれます.したがって、mocを直接使用する必要はありません.mocの詳細については、Qtがなぜmocを信号とスロットに使用するのかを参照してください.
    使用方法
    mocは通常、クラス宣言を含む入力ファイルとともに使用されます.以下に示します.
    class MyClass : public QObject
     {
         Q_OBJECT
    
     public:
         MyClass(QObject *parent = 0);
         ~MyClass();
    
     signals:
         void mySignal();
    
     public slots:
         void mySlot();
     };
    

      mocは、上述した信号およびスロットに加えて、以下の例に示すようにオブジェクト属性を実現する.Q_PROPERTY()マクロはオブジェクト属性を宣言し、Q_ENUM()はクラス内の列挙タイプリストを宣言し、属性システムで使用できます.次の例では、優先度とも呼ばれ、get関数priority()とset関数setPriority()を持つ列挙タイプPriorityの属性を宣言します.
    class MyClass : public QObject
    {
        Q_OBJECT
        Q_PROPERTY(Priority priority READ priority WRITE setPriority)
        Q_ENUMS(Priority)
    
    public:
        enum Priority { High, Low, VeryHigh, VeryLow };
    
        MyClass(QObject *parent = 0);
        ~MyClass();
    
        void setPriority(Priority priority) { m_priority = priority; }
        Priority priority() const { return m_priority; }
    
    private:
        Priority m_priority;
    };
    

      Q_FLAGS()マクロは、フラグとして使用する列挙、すなわちOR演算を宣言します.別のマクロQ_CLASSInFO()を使用すると、クラスのメタオブジェクトに追加の名前/値を追加できます.
    class MyClass : public QObject
    {
        Q_OBJECT
        Q_CLASSINFO("Author", "Oscar Peterson")
        Q_CLASSINFO("Status", "Active")
    
    public:
        MyClass(QObject *parent = 0);
        ~MyClass();
    };
    

    プログラム内の他のC++コードのようにmocによって生成されたC++ファイルをコンパイルし、リンクしなければならない.そうでなければ、構築は最後のリンクフェーズで失敗します.qmakeを使用すると、自動的に完了します.Qmakeを実行するたびに、プロジェクトのヘッダファイルを解析し、Q_を含むmakeルールを生成します.OBJECTマクロのファイル呼び出しmoc.myclassならhファイルにクラス宣言が見つかった場合、moc出力をmoc_という名前に置く必要があります.myclass.cppのファイルにあります.次に、Windows上のmoc_などのターゲットファイルを生成するために、通常のようにファイルをコンパイルする必要があります.myclass.obj. 次に、オブジェクトは、プログラムの最終構築フェーズでリンクされたターゲットファイルのリストに含める必要があります.
    呼び出しmocのルールの作成
    最も簡単なテストプログラムに加えて、mocを自動的に実行することをお勧めします.プログラムのmakefileにルールを追加することで、makeは必要に応じてmocを実行し、mocの出力ファイルを処理することができます.qmake makefile生成ツールを使用してmakefileを構築することをお勧めします.このツールはmakefileを生成し、必要なmoc処理をすべて実行します.自分でmakefileを作成したい場合は、moc処理を含める方法についてのヒントがあります.ヘッダファイルのQ_についてOBJECTクラス宣言は、GNU makeのみを使用する場合に便利なmakefileルールです.
    moc_%.cpp: %.h
              moc $(DEFINES) $(INCPATH) $< -o $@
    

    簡単に作成するには、次のフォームのルールを使用します.
     moc_foo.cpp: foo.h
              moc $(DEFINES) $(INCPATH) $< -o $@
    

      moc_をfoo.cppをあなたのSOURCES(あなたの好きな名前を置き換えます)変数に追加し、moc_foo.oまたはmoc_foo.objをあなたのOBJECTS変数に追加します.この2つの例では、$(DEFINES)と$(INCPATH)は、C++コンパイラのdefineおよびincludeパスオプションに渡されています.mocは、ソースファイルを前処理するためにこれらを必要とします.C++ソースファイル.cppの名前を付けるのが好きですが、必要に応じて、C,.cc,.CC,.cxxおよび.c++などの他の拡張子を使用できます.実装の場合(.cpp)ファイルのQ_OBJECTクラス宣言は、次のmakefileルールを使用することをお勧めします.
    foo.o: foo.moc
    
    foo.moc: foo.cpp
             moc $(DEFINES) $(INCPATH) -i $< -o $@
    

    これによりmakeがfooをコンパイルすることが保証される.cppの前にmocを実行します.次は
      #include "foo.moc"
    

    fooに置くcppの最後に、このファイルで宣言されたすべてのクラスが完全に既知です.
    コマンドラインオプション
    以下はmocツールでサポートされているコマンドラインオプションです.
    オプション
    説明
    -o
    標準出力ではなく出力に書き込み
    -f[]
    強制的に出力にinclude文を生成します.これは、拡張子がHまたはhで始まるヘッダファイルのデフォルト値です.このオプションは、ヘッダファイルが標準ネーミング規則に合致しない場合に便利です.部分はオプションです.
    -i
    出力に#include文を生成しないでください.これは、1つ以上のクラス宣言を含むC++ファイル上でmocを実行するために使用できます.次に、includeメタオブジェクトコードをcppファイルにあります.
    -nw
    警告を生成しないでください.(推奨しない)
    -p
    生成された#include文にmocをファイル名に追加/追加します.
    -I
    ヘッダファイルのincludeパスにdirを追加します.
    -E
    前処理のみメタオブジェクトコードは生成されません.
    -D[=]
    オプション定義を使用してマクロを定義します.
    -U
    マクロを定義しない
    -M
    プラグインに添付メタデータを添付します.クラスがQ_を指定した場合PLUGIN_METADATAでは、キー値ペアがメタデータに追加されます.これは、実行時にプラグインに対して解析されたJsonオブジェクト(QPluginLoaderからアクセス可能)に最終的に表示されます.このパラメータは、通常、システム解析を構築する情報を使用して静的プラグインをマークするために使用されます.
    @
    他のコマンドラインオプションを読み込みます.このファイルの各行はオプションとして扱われます.空の行は無視されます.オプションファイル自体はこのオプションをサポートしていません(つまり、オプションファイルは別のファイルを「含む」ことはできません).
    -h
    使用法とオプションのリストを表示します.
    -v
    mocのバージョン番号を表示
    -Fdir
    ヘッダファイルを検索するディレクトリリストのヘッダにmacOSでフレームディレクトリdirを追加します.これらのディレクトリは、-Iオプションで指定したディレクトリとインターリーブされ、左から右へスキャンされます(gccのオンラインヘルプページを参照).通常、-F/Library/Frameworks/
    mocにヘッダファイルの一部を解析しないように明確に伝えることができます.moc定義プリプロセッサシンボルQ_MOC_RUN.
    #ifndef Q_MOC_RUN
          ...
    #endif
    

    間のコードはスキップされます.
    しんだん
      mocはQ_を宣言するOBJECTのクラスでは、多くの危険や不正な構造が警告されています.プログラムの最終構築フェーズでリンクエラーが発生した場合、YourClass::className()が定義されていないか、またはYourClassにvtableが欠けている場合はエラーを示します.ほとんどの場合、コンパイルまたはinclude mocで生成されたC++コードを忘れたり、linkコマンドにオブジェクトファイルを含めたりします.qmakeを使用する場合は、makefileを更新するために再実行してみてください.これは問題を解決するはずです.
    制限
    mocはすべてのC++を処理しない.主な問題はクラステンプレートにQ_がないことです.OBJECTマクロ.例:
    class SomeTemplate<int> : public QFrame
      {
          Q_OBJECT
          ...
    
      signals:
          void mySignal(int);
      };
    

    以上の構造は違法です.これらはすべて、より良い代替案があると考えられています.そのため、これらの制限を解消することは、私たちの最も重要な任務ではありません.
    多重継承にはQObjectが最初に必要です
    多重継承を使用する場合、mocは、最初に継承されたクラスがQObjectのサブクラスであると仮定する.また、最初に継承されたクラスがQObjectであることを確認してください.
      // correct
      class SomeClass : public QObject, public OtherClass
      {
          ...
      }; 
    

    QObjectによる仮想継承はサポートされていません.
    信号とスロットのパラメータは関数ポインタにはできません
    ほとんどの場合、関数ポインタを信号やスロットのパラメータとして使用することを考慮するかもしれませんが、継承はより良い選択だと思います.不正な構文の例を次に示します.
      class SomeClass : public QObject
      {
          Q_OBJECT
    
      public slots:
          void apply(void (*apply)(List *, void *), char *); // WRONG
      };
    

    この制限は、次のように解決できます.
     typedef void (*ApplyFunction)(List *, void *);
    
      class SomeClass : public QObject
      {
          Q_OBJECT
    
      public slots:
          void apply(ApplyFunction, char *);
      };
    

    関数ポインタを継承関数と虚関数で置き換えると、より良い場合もあります.
    列挙とTypedefは、信号とスロットパラメータの要件を完全に満たす必要があります.
      パラメータの署名を確認するとき、QObject::connect()はデータ型を文字通り比較します.したがって、AlignmentとQt::Alignmentは2つの異なるタイプとみなされます.この制限を解決するには、信号とスロットを宣言するときと、接続を確立するときに完全なデータ型を書くようにしてください.たとえば、次のようにします.
      class MyClass : public QObject
      {
          Q_OBJECT
    
          enum Error {
              ConnectionRefused,
              RemoteHostClosed,
              UnknownError
          };
    
      signals:
          void stateChanged(MyClass::Error error);
      };
    

    ネストされたクラスには信号やスロットはありません
    以下に、エラー構造の例を示します.
     class A
      {
      public:
          class B
          {
              Q_OBJECT
    
          public slots:   // WRONG
              void b();
          };
      };
    

    信号/スロットの戻り値タイプは参照できません
    信号とスロットには戻りタイプがありますが、リファレンスの戻りは禁止されています.
    クラスのsignalsセクションとslotsセクションに信号とスロットのみが表示されます.
    信号やスロットではなく他の構造をクラスの信号やスロット部分に配置しようとすると、mocはエラーを報告します.