広告と実装を組み合わせるマクロマジック

不快な問題の1つは、単純な変更を行う場合でも、いくつかの場所でコードを編集する必要があることです。 たとえば、データフィールドをクラスに追加する場合、クラス宣言に追加し、コンストラクターで初期化する必要があります。また、コピー演算子または比較演算子をオーバーライドする場合は、これも必要です。 これは時間がかかり、場所の1つを忘れるとエラーになります(初期化を忘れることは特に不快であり、そのようなエラーは長年にわたって発生する可能性があり、予期しない、再現困難な問題を引き起こします)。



通常、クラスにフィールドのアクティブな変更が含まれる場合、フィールドを使用したアクション(初期化、コピー、シリアル化、リフレクション)自体を実装するマクロが記述されます。

結果として、変数は2つの場所にのみ登録する必要があります。クラスで宣言して実装します(または実装での後続の使用のために登録します)。



次のようになります:

class TData { public: int Number; float Factor; BEGIN_FIELDS FIELD(Number, 0) FIELD(Factor, 1.0f) END_FIELDS };
      
      





マクロの実装、たとえば、データフィールドへのポインターの登録、その名前、および後で使用するための初期値については言及しません。



別の簡単な例。 たとえば、列挙のバリアントをその名前の文字列と一致させるために、列挙を反映する必要があります。 通常、これは次のように行われます。

 enum TModelType { Car, Weapon, Human }; #define REFLECT_MODEL_TYPE(mac_value) Register(mac_value, #mac_value); void TModelTypeReflection::RegisterTypes() { REFLECT_MODEL_TYPE(Car) REFLECT_MODEL_TYPE(Weapon) REFLECT_MODEL_TYPE(Human) }
      
      





TModelTypeReflection宣言Register実装は、読者の想像力を提供します。



かなり長い間、私はこの状況に満足していました。 しかし最近、私は単一のアナウンスメントを管理して、もっと良いことができると考えました。 すべて同じマクロを使用してこれを行うことができます。



最後の例では、次のようになります。

 #define DECLARE_MODEL_TYPE(mac_value, mac_next) \ mac_value, \ mac_next \ Register(mac_value, #mac_value); #define END_MODEL_TYPE \ }; void TModelTypeReflection::RegisterTypes() { enum TModelType { DECLARE_MODEL_TYPE(Car, DECLARE_MODEL_TYPE(Weapon, DECLARE_MODEL_TYPE(Human, END_MODEL_TYPE))) }
      
      





DECLARE_MODEL_TYPEマクロ最初に列挙要素に展開され、次にEND_MODEL_TYPEのコードが列挙ブロックを閉じて関数ヘッダーを挿入します。次に、要素のレジスタ呼び出しが関数の本体に逆順でのみ挿入され、最後に中括弧が関数ブロックを閉じます(したがって、セミコロンも含まれません) )

同様のコードをクラスフィールドに書くことができます。



欠点について言うだけです。



広告と実装を組み合わせる別の解決策は、コードジェネレーターを使用することですが、このアプローチには欠点もあります。



All Articles