列挙型をすべての人がアクセスできるようにするが、メタタイプで記述する方法

前文



ソフトウェアを開発する過程で、列挙型列挙子を集中ヘッダーファイルで定義する必要がありましたが、列挙型列挙子は多くのソースファイルで使用できます。 ソースコードと依存関係の構成の観点から非常に便利です。 ただし、私のタスクでは、Qtメタタイプシステムの列挙子の登録も必要でした。 私がこの登録をどのようにしたかについて、そしてそれは議論されます。



Qtへのインタビュー



「私のenumを理解する必要があります。」

-Q_DECLARE_METATYPE()は 、enumをQVariantのメタ型として宣言するのに適しています。 マクロは、 enum宣言の直後に呼び出す必要があります。
-すごい! そして、シグナルからスロットに列挙型をスローしたい場合、プロパティを突き出しますか?

-enumをメタタイプシステムに登録するには、 qRegisterMetaType <T>()が必要です。 これは機能であることに注意してください。 彼女はどこかに呼ばれなければなりません。 できれば、投げたり突き出す前に。 たとえば、 main()で。 そして、はい、 Q_DECLARE_METATYPE()なしでは何も動作しません。
-うーん...それは便利ではありません。 登録全体をソース内の1つの場所に制限したい...

「あなたはすぐに何を言いませんでしたか?!」 Q_ENUM()を使用してください! この素晴らしいマクロはあなたのためにすべてをします! さらにもっと! QVariantの 列挙型からQStringへの変換を登録します! QObjectから継承したクラス定義内の列挙宣言の直後にマクロを呼び出す必要があります...
-ちょっと待って? 教室でこれを行う必要がありますか?..そして、私はグローバルに、そしてすべての人のために...



特徴



  1. 列挙型のインスタンスをQVariantに保存するだけの場合、マクロQ_DECLARE_METATYPE()で十分です。 シグナルスロットを介して値を渡し、 intのプロパティに保存し、必要に応じて明示的にenumにキャストできます。



  2. intと enumを区別することが非常に重要である場合、または名前が1つで引数タイプが異なる呼び出し可能なメソッドが多数必要な場合は、メタタイプシステムに追加登録せずに行うことはできません。 これを行うには、実際に使用する前にqRegisterMetaType <T>()を呼び出します。



  3. ええと、enumがQObjectクラスの一部である場合、すべてはQ_ENUM()によって決定され、その後、 それで何でもできます。 完全修飾タイプ名( ClassName :: EnumName )を介して、別のクラスのスロットのプロパティまたはパラメーターとして使用することもできます。


解決策



以前に策定した私の目標は、メタタイプシステムへの完全な登録を必要としました。 最後の2つの可能性は私に合っていました。 機能番号2には、コードのどこかに明示的な関数呼び出しでenumを登録する必要があるという欠点があります。 可能性番号3には、それと連携する既存のクラスの1つにenumを配置すると、ソースの依存関係が複雑になる可能性があるという欠点があります。

しかし、新しいQObjectクラスを作成することを妨げるものはありません! 一般に、すべてのヘッダーファイルについて。 そして、このクラスのインスタンス化を禁止できます。 例を挙げます(そして、ウォームアップのために、c ++ 11標準の機能の小さな例を含んでいます):



///    class Enums : public QObject { public: ///   std=++11, 'class'       EnumA, ':int'     int enum class EnumA: int { A, B, C, }; Q_ENUM(EnumA) enum EnumB { A,///<   EnumA::A  , EnumA::A    A,    EnumB::A D, }; Q_ENUM(EnumB) Q_OBJECT Enums() = delete; ///< std=c++11,       Enums };
      
      





その結果、すべてのユーザーが使用可能なEnumsクラスがあり、必要なenumがメタタイプシステムに認識されており、インスタンス化する機能はありません!



ただし、いくつかの欠点があります。 Q_OBJECTマクロは、静的なメタオブジェクトに加えて、静的でない関数のパケットを作成します。 そして、私たちのクラスは決して作成されません! そして、これらの機能は必要ありません。 悲しいかな、これはいくつかの無駄は避けられません。 ただし、Qt 5.5以降、ドキュメントではQ_GADGETマクロについて説明しています。
-はい、はい、 Q_OBJECTよりも軽く、 QObjectからの継承を必要としません。 Q_ENUMQ_PROPERTYQ_INVOKABLEのみを許可します。 そして、長い間、すでに存在していましたが、私たちはそれについて黙っていました!


確かに、なぜあなたは黙っていたのですか! ここで私たちに最適です! ほんの数回の変更で、パンツが...



 ///    class Enums { /// <  ,  Enums    ! public: ///   std=++11, 'class'       EnumA, ':int'     int enum class EnumA: int { A, B, C, }; Q_ENUM(EnumA) enum EnumB { A,///<   EnumA::A  , EnumA::A    A,    EnumB::A D, }; Q_ENUM(EnumB) Q_GADGET ///<   Q_OBJECT   ! Enums() = delete; ///< std=c++11,       Enums };
      
      





確かに、プロパティと呼び出し可能なメソッドを宣言する機能は必要ありません。 しかし、それらはメタオブジェクトの不可欠な部分であり、カットは機能しません。 しかし、今、古いヘッダーと新しいmoc * .cppをこのヘッダーファイルと比較すると、欠落している静的qt_static_metacall()関数の実装と、通常のmetaObject()qt_metacast()qt_metacall()関数を見つけることができます。 些細なことですが、素晴らしい。 好奇心のために、バイナリのサイズを比較しましたが、些細なことは512バイト(ビルドリリース)であることが判明しました。



おわりに



1つのヘッダーファイルでenumを非常に簡単に定義し、同じ場所のメタタイプシステムにそれをスローし、他のクラスで使用できます。 たとえば、インターフェイスを通じてこの列挙型の値を選択する機能を提供しながら、基本的なシステムロジックでそのような列挙型を使用するタスクがありました。 列挙値の名前を登録するという形式のQ_ENUMの追加機能により、ホイールを再発明することはできませんでしたが、それは別の話です。 モデルとパターン付き。



All Articles