通常、クラスにフィールドのアクティブな変更が含まれる場合、フィールドを使用したアクション(初期化、コピー、シリアル化、リフレクション)自体を実装するマクロが記述されます。
結果として、変数は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のコードが列挙ブロックを閉じて関数ヘッダーを挿入します。次に、要素のレジスタ呼び出しが関数の本体に逆順でのみ挿入され、最後に中括弧が関数ブロックを閉じます(したがって、セミコロンも含まれません) )
同様のコードをクラスフィールドに書くことができます。
欠点について言うだけです。
- 逆の順序で登録しますが、順序が依然として重要な場合は、 登録の実装でこれを考慮することができます。たとえば、リストの最後ではなく、次のフィールドを追加します。
- DOxygenのようなドキュメントを自動生成するシステムの問題は、マクロの展開を推測しません。
- フィールドを追加するときに、 END_MODEL_TYPEの後に別の閉じ括弧を追加する必要があります。 不快な欠点、なぜなら それでも2つの場所でコードを編集する必要があります。 プリプロセッサがブラケットを忘れることを許さないことを少し嬉しく思います。
広告と実装を組み合わせる別の解決策は、コードジェネレーターを使用することですが、このアプローチには欠点もあります。